[debian-edu-commits] debian-edu/ 03/12: Imported Upstream version 0.9.9

Dominik George natureshadow-guest at moszumanska.debian.org
Thu Oct 6 21:11:43 UTC 2016


This is an automated email from the git hooks/post-receive script.

natureshadow-guest pushed a commit to branch master
in repository guacamole-server.

commit 8894ca9045ab5936f56e8c284adba7af18e91751
Author: Dominik George <nik at naturalnet.de>
Date:   Fri Jun 3 00:33:30 2016 +0200

    Imported Upstream version 0.9.9
---
 AUTHORS                                            |    6 +
 LICENSE                                            |  489 +---
 Makefile.am                                        |   84 +-
 Makefile.in                                        |  453 ++--
 README                                             |   25 +-
 aclocal.m4                                         |  742 ++++--
 bin/guacctl                                        |  136 +
 compile                                            |  347 +++
 config.guess                                       |  346 +--
 config.h.in                                        |  298 +++
 config.sub                                         |  114 +-
 configure                                          | 2617 ++++++++++++++++----
 configure.ac                                       |  955 +++++--
 depcomp                                            |  455 ++--
 doc/Doxyfile                                       | 1569 +-----------
 install-sh                                         |   14 +-
 ltmain.sh                                          |   32 +-
 m4/libtool.m4                                      |   73 +-
 missing                                            |  414 ++--
 src/common-ssh/Makefile.am                         |   54 +
 src/common-ssh/Makefile.in                         |  704 ++++++
 src/common-ssh/guac_sftp.c                         |  783 ++++++
 src/common-ssh/guac_sftp.h                         |  182 ++
 src/common-ssh/guac_ssh.c                          |  511 ++++
 src/common-ssh/guac_ssh.h                          |  117 +
 src/common-ssh/guac_ssh_buffer.c                   |  138 ++
 src/common-ssh/guac_ssh_buffer.h                   |  137 +
 src/common-ssh/guac_ssh_key.c                      |  219 ++
 src/common-ssh/guac_ssh_key.h                      |  173 ++
 src/common-ssh/guac_ssh_user.c                     |   85 +
 src/common-ssh/guac_ssh_user.h                     |  111 +
 src/common/Makefile.am                             |   58 +
 src/common/Makefile.in                             |  752 ++++++
 src/common/guac_clipboard.c                        |  113 +
 src/common/guac_clipboard.h                        |  107 +
 src/common/guac_dot_cursor.c                       |   86 +
 src/common/guac_dot_cursor.h                       |   64 +
 src/common/guac_iconv.c                            |  195 ++
 src/common/guac_iconv.h                            |  103 +
 src/common/guac_io.c                               |   71 +
 src/common/guac_io.h                               |   53 +
 src/common/guac_json.c                             |  183 ++
 src/common/guac_json.h                             |  199 ++
 src/common/guac_list.c                             |   84 +
 src/common/guac_list.h                             |  125 +
 src/common/guac_pointer_cursor.c                   |   97 +
 src/common/guac_pointer_cursor.h                   |   64 +
 src/common/guac_rect.c                             |  269 ++
 src/common/guac_rect.h                             |  146 ++
 src/common/guac_string.c                           |   93 +
 src/common/guac_string.h                           |   50 +
 src/common/guac_surface.c                          | 1526 ++++++++++++
 src/common/guac_surface.h                          |  341 +++
 src/guacd/Makefile.am                              |   92 +-
 src/guacd/Makefile.in                              |  550 ++--
 src/guacd/client-map.c                             |  172 ++
 src/guacd/client-map.h                             |   73 +
 src/guacd/client.c                                 |  133 +-
 src/guacd/client.h                                 |   63 +-
 src/guacd/conf-args.c                              |  121 +
 src/guacd/conf-args.h                              |   37 +
 src/guacd/conf-file.c                              |  215 ++
 src/guacd/conf-file.h                              |   88 +
 src/guacd/conf-parse.c                             |  538 ++++
 src/guacd/conf-parse.h                             |   71 +
 src/guacd/daemon.c                                 |  512 ++--
 src/guacd/init.d/guacd.in                          |   47 +-
 src/guacd/log.c                                    |  202 +-
 src/guacd/log.h                                    |  104 +-
 src/guacd/man/guacd.8                              |   27 +-
 src/guacd/man/guacd.conf.5                         |  162 ++
 src/guacd/socket-ssl.c                             |   67 +-
 src/guacd/socket-ssl.h                             |   56 +-
 src/libguac/Makefile.am                            |  131 +-
 src/libguac/Makefile.in                            |  597 +++--
 src/libguac/audio.c                                |  217 +-
 src/libguac/client-handlers.c                      |  347 ++-
 src/libguac/client-handlers.h                      |  109 +-
 src/libguac/client.c                               |  380 ++-
 src/libguac/encode-jpeg.c                          |  271 ++
 src/libguac/encode-jpeg.h                          |   56 +
 src/libguac/encode-png.c                           |  404 +++
 src/libguac/encode-png.h                           |   53 +
 src/libguac/encode-webp.c                          |  261 ++
 src/libguac/encode-webp.h                          |   61 +
 src/libguac/error.c                                |  193 +-
 src/libguac/guacamole/audio-fntypes.h              |   56 +
 src/libguac/guacamole/audio-types.h                |   45 +
 src/libguac/guacamole/audio.h                      |  252 +-
 src/libguac/guacamole/client-constants.h           |  106 +
 src/libguac/guacamole/client-fntypes.h             |  135 +
 src/libguac/guacamole/client-types.h               |   97 +
 src/libguac/guacamole/client.h                     |  644 +++--
 src/libguac/guacamole/error-types.h                |  172 ++
 src/libguac/guacamole/error.h                      |  131 +-
 src/libguac/guacamole/hash.h                       |   60 +-
 src/libguac/guacamole/instruction-constants.h      |   48 +
 src/libguac/guacamole/instruction-types.h          |   68 +
 src/libguac/guacamole/instruction.h                |  118 +-
 src/libguac/guacamole/layer-types.h                |   38 +
 src/libguac/guacamole/layer.h                      |   59 +-
 src/libguac/guacamole/object-types.h               |   38 +
 src/libguac/guacamole/object.h                     |   96 +
 src/libguac/guacamole/plugin-constants.h           |   64 +
 src/libguac/guacamole/plugin-types.h               |   40 +
 src/libguac/guacamole/plugin.h                     |   94 +-
 src/libguac/guacamole/pool-types.h                 |   46 +
 src/libguac/guacamole/pool.h                       |   93 +-
 src/libguac/guacamole/protocol-types.h             |  248 ++
 src/libguac/guacamole/protocol.h                   |  534 ++--
 src/libguac/guacamole/socket-constants.h           |   44 +
 src/libguac/guacamole/socket-fntypes.h             |   86 +
 src/libguac/guacamole/socket-types.h               |   56 +
 src/libguac/guacamole/socket.h                     |  188 +-
 src/libguac/guacamole/stream-types.h               |   38 +
 src/libguac/guacamole/stream.h                     |  130 +-
 src/libguac/guacamole/timestamp-types.h            |   40 +
 src/libguac/guacamole/timestamp.h                  |   68 +-
 src/libguac/guacamole/unicode.h                    |   56 +-
 src/libguac/hash.c                                 |   55 +-
 src/libguac/instruction.c                          |  466 ++--
 src/libguac/ogg_encoder.c                          |  211 --
 src/libguac/ogg_encoder.h                          |   67 -
 src/libguac/palette.c                              |   66 +-
 src/libguac/palette.h                              |   55 +-
 src/libguac/plugin.c                               |   73 +-
 src/libguac/pool.c                                 |   63 +-
 src/libguac/protocol.c                             |  682 ++---
 src/libguac/raw_encoder.c                          |  161 ++
 src/libguac/raw_encoder.h                          |   78 +
 src/libguac/socket-fd.c                            |   74 +-
 src/libguac/socket-nest.c                          |   76 +-
 src/libguac/socket.c                               |  154 +-
 src/libguac/timestamp.c                            |   63 +-
 src/libguac/unicode.c                              |   55 +-
 src/libguac/wav_encoder.c                          |  201 --
 src/libguac/wav_encoder.h                          |  144 --
 src/protocols/rdp/Makefile.am                      |  338 ++-
 src/protocols/rdp/Makefile.in                      | 1354 ++++++++--
 src/protocols/rdp/_generated_keymaps.c             |  680 +++++
 src/protocols/rdp/audio.c                          |  176 --
 src/protocols/rdp/audio.h                          |  212 --
 src/protocols/rdp/client.c                         |  744 ++++--
 src/protocols/rdp/client.h                         |  228 +-
 src/protocols/rdp/compat/client-cliprdr.h          |   53 +-
 src/protocols/rdp/compat/rail.h                    |   42 +
 src/protocols/rdp/compat/winpr-stream.c            |   53 +-
 src/protocols/rdp/compat/winpr-stream.h            |   64 +-
 src/protocols/rdp/compat/winpr-wtypes.h            |   54 +-
 src/protocols/rdp/config.h                         |    0
 src/protocols/rdp/default_pointer.c                |  106 -
 src/protocols/rdp/default_pointer.h                |   76 -
 src/protocols/rdp/guac_handlers.c                  |  238 +-
 src/protocols/rdp/guac_handlers.h                  |   56 +-
 src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages.c   |  532 ++++
 src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages.h   |  132 +
 .../rdp/guac_rdpdr/rdpdr_fs_messages_dir_info.c    |  222 ++
 .../rdp/guac_rdpdr/rdpdr_fs_messages_dir_info.h    |   77 +
 .../rdp/guac_rdpdr/rdpdr_fs_messages_file_info.c   |  291 +++
 .../rdp/guac_rdpdr/rdpdr_fs_messages_file_info.h   |  104 +
 .../rdp/guac_rdpdr/rdpdr_fs_messages_vol_info.c    |  150 ++
 .../rdp/guac_rdpdr/rdpdr_fs_messages_vol_info.h    |   78 +
 src/protocols/rdp/guac_rdpdr/rdpdr_fs_service.c    |  163 ++
 src/protocols/rdp/guac_rdpdr/rdpdr_fs_service.h    |   51 +
 src/protocols/rdp/guac_rdpdr/rdpdr_messages.c      |  110 +-
 src/protocols/rdp/guac_rdpdr/rdpdr_messages.h      |  107 +-
 src/protocols/rdp/guac_rdpdr/rdpdr_printer.c       |  138 +-
 src/protocols/rdp/guac_rdpdr/rdpdr_printer.h       |   70 +-
 src/protocols/rdp/guac_rdpdr/rdpdr_service.c       |  161 +-
 src/protocols/rdp/guac_rdpdr/rdpdr_service.h       |   76 +-
 src/protocols/rdp/guac_rdpsnd/rdpsnd_messages.c    |  289 +--
 src/protocols/rdp/guac_rdpsnd/rdpsnd_messages.h    |  159 +-
 src/protocols/rdp/guac_rdpsnd/rdpsnd_service.c     |   93 +-
 src/protocols/rdp/guac_rdpsnd/rdpsnd_service.h     |   79 +-
 src/protocols/rdp/guac_svc/svc_service.c           |  151 ++
 src/protocols/rdp/guac_svc/svc_service.h           |   80 +
 src/protocols/rdp/keymaps/base.keymap              |   92 +
 src/protocols/rdp/keymaps/de_de_qwertz.keymap      |   67 +
 src/protocols/rdp/keymaps/en_us_qwerty.keymap      |   36 +
 src/protocols/rdp/keymaps/failsafe.keymap          |   26 +
 src/protocols/rdp/keymaps/fr_fr_azerty.keymap      |   56 +
 src/protocols/rdp/keymaps/generate.pl              |  272 ++
 src/protocols/rdp/keymaps/it_it_qwerty.keymap      |   53 +
 src/protocols/rdp/keymaps/sv_se_qwerty.keymap      |   68 +
 src/protocols/rdp/ogg_encoder.c                    |  211 --
 src/protocols/rdp/ogg_encoder.h                    |   67 -
 src/protocols/rdp/rdp_bitmap.c                     |  225 +-
 src/protocols/rdp/rdp_bitmap.h                     |   67 +-
 src/protocols/rdp/rdp_cliprdr.c                    |  252 +-
 src/protocols/rdp/rdp_cliprdr.h                    |   72 +-
 src/protocols/rdp/rdp_color.c                      |   64 +
 src/protocols/rdp/rdp_color.h                      |   42 +
 src/protocols/rdp/rdp_disp.c                       |  178 ++
 src/protocols/rdp/rdp_disp.h                       |  142 ++
 src/protocols/rdp/rdp_fs.c                         |  775 ++++++
 src/protocols/rdp/rdp_fs.h                         |  454 ++++
 src/protocols/rdp/rdp_gdi.c                        |  326 +--
 src/protocols/rdp/rdp_gdi.h                        |  103 +-
 src/protocols/rdp/rdp_glyph.c                      |  189 +-
 src/protocols/rdp/rdp_glyph.h                      |   57 +-
 src/protocols/rdp/rdp_keymap.c                     |   73 +-
 src/protocols/rdp/rdp_keymap.h                     |   93 +-
 src/protocols/rdp/rdp_keymap_base.c                |  220 --
 src/protocols/rdp/rdp_keymap_de_de.c               |  552 -----
 src/protocols/rdp/rdp_keymap_en_us.c               |  442 ----
 src/protocols/rdp/rdp_keymap_failsafe.c            |   63 -
 src/protocols/rdp/rdp_keymap_fr_fr.c               |  560 -----
 src/protocols/rdp/rdp_pointer.c                    |   69 +-
 src/protocols/rdp/rdp_pointer.h                    |   58 +-
 src/protocols/rdp/rdp_rail.c                       |  124 +
 src/protocols/rdp/rdp_rail.h                       |   48 +
 src/protocols/rdp/rdp_settings.c                   |  213 +-
 src/protocols/rdp/rdp_settings.h                   |  184 +-
 src/protocols/rdp/rdp_status.h                     |   69 +
 src/protocols/rdp/rdp_stream.c                     |  571 +++++
 src/protocols/rdp/rdp_stream.h                     |  287 +++
 src/protocols/rdp/rdp_svc.c                        |  155 ++
 src/protocols/rdp/rdp_svc.h                        |  103 +
 src/protocols/rdp/resolution.c                     |   60 +
 src/protocols/rdp/resolution.h                     |   49 +
 src/protocols/rdp/sftp.c                           |   43 +
 src/protocols/rdp/sftp.h                           |   55 +
 src/protocols/rdp/unicode.c                        |   77 +
 src/protocols/rdp/unicode.h                        |   37 +
 src/protocols/rdp/wav_encoder.c                    |  201 --
 src/protocols/rdp/wav_encoder.h                    |  144 --
 src/protocols/ssh/Makefile.am                      |  101 +-
 src/protocols/ssh/Makefile.in                      |  499 ++--
 src/protocols/ssh/blank.c                          |   64 -
 src/protocols/ssh/blank.h                          |   52 -
 src/protocols/ssh/char_mappings.h                  |   63 -
 src/protocols/ssh/client.c                         |  178 +-
 src/protocols/ssh/client.h                         |  143 +-
 src/protocols/ssh/clipboard.c                      |   61 +
 src/protocols/ssh/clipboard.h                      |   49 +
 src/protocols/ssh/common.c                         |  121 -
 src/protocols/ssh/common.h                         |   68 -
 src/protocols/ssh/cursor.c                         |   74 -
 src/protocols/ssh/cursor.h                         |   89 -
 src/protocols/ssh/guac_handlers.c                  |  435 +---
 src/protocols/ssh/guac_handlers.h                  |   56 +-
 src/protocols/ssh/ibar.c                           |  108 -
 src/protocols/ssh/ibar.h                           |   77 -
 src/protocols/ssh/libssh_compat.h                  |   57 -
 src/protocols/ssh/sftp.c                           |   64 +
 src/protocols/ssh/sftp.h                           |   90 +
 src/protocols/ssh/ssh_agent.c                      |  204 ++
 src/protocols/ssh/ssh_agent.h                      |  119 +
 src/protocols/ssh/ssh_client.c                     |  412 +--
 src/protocols/ssh/ssh_client.h                     |   54 +-
 src/protocols/ssh/terminal.c                       |  868 -------
 src/protocols/ssh/terminal_handlers.h              |   54 -
 src/protocols/ssh/types.h                          |  122 -
 src/protocols/telnet/Makefile.am                   |   54 +
 src/protocols/telnet/Makefile.in                   |  752 ++++++
 src/protocols/telnet/client.c                      |  244 ++
 src/protocols/telnet/client.h                      |  122 +
 src/protocols/telnet/clipboard.c                   |   61 +
 src/protocols/telnet/clipboard.h                   |   49 +
 src/protocols/telnet/guac_handlers.c               |  150 ++
 src/protocols/telnet/guac_handlers.h               |   62 +
 src/protocols/telnet/telnet_client.c               |  501 ++++
 src/protocols/telnet/telnet_client.h               |   50 +
 src/protocols/vnc/Makefile.am                      |   86 +-
 src/protocols/vnc/Makefile.in                      |  414 +++-
 src/protocols/vnc/client.c                         |  512 +++-
 src/protocols/vnc/client.h                         |  200 +-
 src/protocols/vnc/clipboard.c                      |   77 +
 src/protocols/vnc/clipboard.h                      |   49 +
 src/protocols/vnc/convert.c                        |  116 -
 src/protocols/vnc/convert.h                        |   53 -
 src/protocols/vnc/guac_handlers.c                  |  156 +-
 src/protocols/vnc/guac_handlers.h                  |   55 +-
 src/protocols/vnc/pulse.c                          |  147 +-
 src/protocols/vnc/pulse.h                          |   54 +-
 src/protocols/vnc/sftp.c                           |   43 +
 src/protocols/vnc/sftp.h                           |   55 +
 src/protocols/vnc/vnc_handlers.c                   |  137 +-
 src/protocols/vnc/vnc_handlers.h                   |   54 +-
 src/terminal/Makefile.am                           |   73 +
 src/terminal/Makefile.in                           |  783 ++++++
 src/terminal/blank.c                               |   51 +
 src/terminal/blank.h                               |   42 +
 src/{protocols/ssh => terminal}/buffer.c           |   76 +-
 src/{protocols/ssh => terminal}/buffer.h           |   58 +-
 src/{protocols/ssh => terminal}/char_mappings.c    |   53 +-
 src/terminal/char_mappings.h                       |   51 +
 src/terminal/common.c                              |  130 +
 src/terminal/common.h                              |   77 +
 src/terminal/cursor.c                              |   61 +
 src/terminal/cursor.h                              |   78 +
 src/{protocols/ssh => terminal}/display.c          |  402 ++-
 src/{protocols/ssh => terminal}/display.h          |  196 +-
 src/terminal/ibar.c                                |   94 +
 src/terminal/ibar.h                                |   65 +
 src/terminal/packet.c                              |   67 +
 src/terminal/packet.h                              |   92 +
 src/terminal/pointer.c                             |   94 +
 src/terminal/pointer.h                             |   68 +
 src/terminal/scrollbar.c                           |  374 +++
 src/terminal/scrollbar.h                           |  342 +++
 src/terminal/terminal.c                            | 1730 +++++++++++++
 src/{protocols/ssh => terminal}/terminal.h         |  337 ++-
 .../ssh => terminal}/terminal_handlers.c           |  180 +-
 src/terminal/terminal_handlers.h                   |   42 +
 src/terminal/types.h                               |  127 +
 test-driver                                        |  139 ++
 tests/Makefile.am                                  |   90 +-
 tests/Makefile.in                                  | 1204 ++++++---
 tests/client/buffer_pool.c                         |   58 +-
 tests/client/client_suite.c                        |   55 +-
 tests/client/client_suite.h                        |   54 +-
 tests/client/layer_pool.c                          |   58 +-
 tests/common/common_suite.c                        |   60 +
 tests/common/common_suite.h                        |   58 +
 tests/common/guac_iconv.c                          |  131 +
 tests/common/guac_rect.c                           |  292 +++
 tests/common/guac_string.c                         |   73 +
 tests/protocol/base64_decode.c                     |   62 +
 tests/protocol/instruction_parse.c                 |   79 +
 tests/protocol/instruction_read.c                  |   61 +-
 tests/protocol/instruction_write.c                 |   65 +-
 tests/protocol/nest_write.c                        |   67 +-
 tests/protocol/suite.c                             |   59 +-
 tests/protocol/suite.h                             |   56 +-
 tests/test_libguac.c                               |   59 +-
 tests/util/guac_pool.c                             |   57 +-
 tests/util/guac_unicode.c                          |   57 +-
 tests/util/util_suite.c                            |   55 +-
 tests/util/util_suite.h                            |   53 +-
 330 files changed, 44963 insertions(+), 18852 deletions(-)

diff --git a/AUTHORS b/AUTHORS
index 76eb105..54d4f43 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -6,3 +6,9 @@ David Lechevalier <david at ulteo.com>
 Alexandre Devely <alex at koumoula.com>
 Laurent Meunier <laurent at deltalima.net>
 Saul Gio Perez <gio.perez at sv.cmu.edu>
+Tom Sealy <tom.sealy at yahoo.com>
+Felipe Weckx <felipe at weckx.net>
+Ruggero Vecchio <ruggero.vecchio at datev.it>
+Denis Bernacci <dbernaci at hotmail.com>
+Frode Langelo <frode at skytap.com>
+Daryl Borth <dborth at gmail.com>
diff --git a/LICENSE b/LICENSE
index 7714141..540cdcf 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,470 +1,19 @@
-                          MOZILLA PUBLIC LICENSE
-                                Version 1.1
-
-                              ---------------
-
-1. Definitions.
-
-     1.0.1. "Commercial Use" means distribution or otherwise making the
-     Covered Code available to a third party.
-
-     1.1. "Contributor" means each entity that creates or contributes to
-     the creation of Modifications.
-
-     1.2. "Contributor Version" means the combination of the Original
-     Code, prior Modifications used by a Contributor, and the Modifications
-     made by that particular Contributor.
-
-     1.3. "Covered Code" means the Original Code or Modifications or the
-     combination of the Original Code and Modifications, in each case
-     including portions thereof.
-
-     1.4. "Electronic Distribution Mechanism" means a mechanism generally
-     accepted in the software development community for the electronic
-     transfer of data.
-
-     1.5. "Executable" means Covered Code in any form other than Source
-     Code.
-
-     1.6. "Initial Developer" means the individual or entity identified
-     as the Initial Developer in the Source Code notice required by Exhibit
-     A.
-
-     1.7. "Larger Work" means a work which combines Covered Code or
-     portions thereof with code not governed by the terms of this License.
-
-     1.8. "License" means this document.
-
-     1.8.1. "Licensable" means having the right to grant, to the maximum
-     extent possible, whether at the time of the initial grant or
-     subsequently acquired, any and all of the rights conveyed herein.
-
-     1.9. "Modifications" means any addition to or deletion from the
-     substance or structure of either the Original Code or any previous
-     Modifications. When Covered Code is released as a series of files, a
-     Modification is:
-          A. Any addition to or deletion from the contents of a file
-          containing Original Code or previous Modifications.
-
-          B. Any new file that contains any part of the Original Code or
-          previous Modifications.
-
-     1.10. "Original Code" means Source Code of computer software code
-     which is described in the Source Code notice required by Exhibit A as
-     Original Code, and which, at the time of its release under this
-     License is not already Covered Code governed by this License.
-
-     1.10.1. "Patent Claims" means any patent claim(s), now owned or
-     hereafter acquired, including without limitation,  method, process,
-     and apparatus claims, in any patent Licensable by grantor.
-
-     1.11. "Source Code" means the preferred form of the Covered Code for
-     making modifications to it, including all modules it contains, plus
-     any associated interface definition files, scripts used to control
-     compilation and installation of an Executable, or source code
-     differential comparisons against either the Original Code or another
-     well known, available Covered Code of the Contributor's choice. The
-     Source Code can be in a compressed or archival form, provided the
-     appropriate decompression or de-archiving software is widely available
-     for no charge.
-
-     1.12. "You" (or "Your")  means an individual or a legal entity
-     exercising rights under, and complying with all of the terms of, this
-     License or a future version of this License issued under Section 6.1.
-     For legal entities, "You" includes any entity which controls, is
-     controlled by, or is under common control with You. For purposes of
-     this definition, "control" means (a) the power, direct or indirect,
-     to cause the direction or management of such entity, whether by
-     contract or otherwise, or (b) ownership of more than fifty percent
-     (50%) of the outstanding shares or beneficial ownership of such
-     entity.
-
-2. Source Code License.
-
-     2.1. The Initial Developer Grant.
-     The Initial Developer hereby grants You a world-wide, royalty-free,
-     non-exclusive license, subject to third party intellectual property
-     claims:
-          (a)  under intellectual property rights (other than patent or
-          trademark) Licensable by Initial Developer to use, reproduce,
-          modify, display, perform, sublicense and distribute the Original
-          Code (or portions thereof) with or without Modifications, and/or
-          as part of a Larger Work; and
-
-          (b) under Patents Claims infringed by the making, using or
-          selling of Original Code, to make, have made, use, practice,
-          sell, and offer for sale, and/or otherwise dispose of the
-          Original Code (or portions thereof).
-
-          (c) the licenses granted in this Section 2.1(a) and (b) are
-          effective on the date Initial Developer first distributes
-          Original Code under the terms of this License.
-
-          (d) Notwithstanding Section 2.1(b) above, no patent license is
-          granted: 1) for code that You delete from the Original Code; 2)
-          separate from the Original Code;  or 3) for infringements caused
-          by: i) the modification of the Original Code or ii) the
-          combination of the Original Code with other software or devices.
-
-     2.2. Contributor Grant.
-     Subject to third party intellectual property claims, each Contributor
-     hereby grants You a world-wide, royalty-free, non-exclusive license
-
-          (a)  under intellectual property rights (other than patent or
-          trademark) Licensable by Contributor, to use, reproduce, modify,
-          display, perform, sublicense and distribute the Modifications
-          created by such Contributor (or portions thereof) either on an
-          unmodified basis, with other Modifications, as Covered Code
-          and/or as part of a Larger Work; and
-
-          (b) under Patent Claims infringed by the making, using, or
-          selling of  Modifications made by that Contributor either alone
-          and/or in combination with its Contributor Version (or portions
-          of such combination), to make, use, sell, offer for sale, have
-          made, and/or otherwise dispose of: 1) Modifications made by that
-          Contributor (or portions thereof); and 2) the combination of
-          Modifications made by that Contributor with its Contributor
-          Version (or portions of such combination).
-
-          (c) the licenses granted in Sections 2.2(a) and 2.2(b) are
-          effective on the date Contributor first makes Commercial Use of
-          the Covered Code.
-
-          (d)    Notwithstanding Section 2.2(b) above, no patent license is
-          granted: 1) for any code that Contributor has deleted from the
-          Contributor Version; 2)  separate from the Contributor Version;
-          3)  for infringements caused by: i) third party modifications of
-          Contributor Version or ii)  the combination of Modifications made
-          by that Contributor with other software  (except as part of the
-          Contributor Version) or other devices; or 4) under Patent Claims
-          infringed by Covered Code in the absence of Modifications made by
-          that Contributor.
-
-3. Distribution Obligations.
-
-     3.1. Application of License.
-     The Modifications which You create or to which You contribute are
-     governed by the terms of this License, including without limitation
-     Section 2.2. The Source Code version of Covered Code may be
-     distributed only under the terms of this License or a future version
-     of this License released under Section 6.1, and You must include a
-     copy of this License with every copy of the Source Code You
-     distribute. You may not offer or impose any terms on any Source Code
-     version that alters or restricts the applicable version of this
-     License or the recipients' rights hereunder. However, You may include
-     an additional document offering the additional rights described in
-     Section 3.5.
-
-     3.2. Availability of Source Code.
-     Any Modification which You create or to which You contribute must be
-     made available in Source Code form under the terms of this License
-     either on the same media as an Executable version or via an accepted
-     Electronic Distribution Mechanism to anyone to whom you made an
-     Executable version available; and if made available via Electronic
-     Distribution Mechanism, must remain available for at least twelve (12)
-     months after the date it initially became available, or at least six
-     (6) months after a subsequent version of that particular Modification
-     has been made available to such recipients. You are responsible for
-     ensuring that the Source Code version remains available even if the
-     Electronic Distribution Mechanism is maintained by a third party.
-
-     3.3. Description of Modifications.
-     You must cause all Covered Code to which You contribute to contain a
-     file documenting the changes You made to create that Covered Code and
-     the date of any change. You must include a prominent statement that
-     the Modification is derived, directly or indirectly, from Original
-     Code provided by the Initial Developer and including the name of the
-     Initial Developer in (a) the Source Code, and (b) in any notice in an
-     Executable version or related documentation in which You describe the
-     origin or ownership of the Covered Code.
-
-     3.4. Intellectual Property Matters
-          (a) Third Party Claims.
-          If Contributor has knowledge that a license under a third party's
-          intellectual property rights is required to exercise the rights
-          granted by such Contributor under Sections 2.1 or 2.2,
-          Contributor must include a text file with the Source Code
-          distribution titled "LEGAL" which describes the claim and the
-          party making the claim in sufficient detail that a recipient will
-          know whom to contact. If Contributor obtains such knowledge after
-          the Modification is made available as described in Section 3.2,
-          Contributor shall promptly modify the LEGAL file in all copies
-          Contributor makes available thereafter and shall take other steps
-          (such as notifying appropriate mailing lists or newsgroups)
-          reasonably calculated to inform those who received the Covered
-          Code that new knowledge has been obtained.
-
-          (b) Contributor APIs.
-          If Contributor's Modifications include an application programming
-          interface and Contributor has knowledge of patent licenses which
-          are reasonably necessary to implement that API, Contributor must
-          also include this information in the LEGAL file.
-
-               (c)    Representations.
-          Contributor represents that, except as disclosed pursuant to
-          Section 3.4(a) above, Contributor believes that Contributor's
-          Modifications are Contributor's original creation(s) and/or
-          Contributor has sufficient rights to grant the rights conveyed by
-          this License.
-
-     3.5. Required Notices.
-     You must duplicate the notice in Exhibit A in each file of the Source
-     Code.  If it is not possible to put such notice in a particular Source
-     Code file due to its structure, then You must include such notice in a
-     location (such as a relevant directory) where a user would be likely
-     to look for such a notice.  If You created one or more Modification(s)
-     You may add your name as a Contributor to the notice described in
-     Exhibit A.  You must also duplicate this License in any documentation
-     for the Source Code where You describe recipients' rights or ownership
-     rights relating to Covered Code.  You may choose to offer, and to
-     charge a fee for, warranty, support, indemnity or liability
-     obligations to one or more recipients of Covered Code. However, You
-     may do so only on Your own behalf, and not on behalf of the Initial
-     Developer or any Contributor. You must make it absolutely clear than
-     any such warranty, support, indemnity or liability obligation is
-     offered by You alone, and You hereby agree to indemnify the Initial
-     Developer and every Contributor for any liability incurred by the
-     Initial Developer or such Contributor as a result of warranty,
-     support, indemnity or liability terms You offer.
-
-     3.6. Distribution of Executable Versions.
-     You may distribute Covered Code in Executable form only if the
-     requirements of Section 3.1-3.5 have been met for that Covered Code,
-     and if You include a notice stating that the Source Code version of
-     the Covered Code is available under the terms of this License,
-     including a description of how and where You have fulfilled the
-     obligations of Section 3.2. The notice must be conspicuously included
-     in any notice in an Executable version, related documentation or
-     collateral in which You describe recipients' rights relating to the
-     Covered Code. You may distribute the Executable version of Covered
-     Code or ownership rights under a license of Your choice, which may
-     contain terms different from this License, provided that You are in
-     compliance with the terms of this License and that the license for the
-     Executable version does not attempt to limit or alter the recipient's
-     rights in the Source Code version from the rights set forth in this
-     License. If You distribute the Executable version under a different
-     license You must make it absolutely clear that any terms which differ
-     from this License are offered by You alone, not by the Initial
-     Developer or any Contributor. You hereby agree to indemnify the
-     Initial Developer and every Contributor for any liability incurred by
-     the Initial Developer or such Contributor as a result of any such
-     terms You offer.
-
-     3.7. Larger Works.
-     You may create a Larger Work by combining Covered Code with other code
-     not governed by the terms of this License and distribute the Larger
-     Work as a single product. In such a case, You must make sure the
-     requirements of this License are fulfilled for the Covered Code.
-
-4. Inability to Comply Due to Statute or Regulation.
-
-     If it is impossible for You to comply with any of the terms of this
-     License with respect to some or all of the Covered Code due to
-     statute, judicial order, or regulation then You must: (a) comply with
-     the terms of this License to the maximum extent possible; and (b)
-     describe the limitations and the code they affect. Such description
-     must be included in the LEGAL file described in Section 3.4 and must
-     be included with all distributions of the Source Code. Except to the
-     extent prohibited by statute or regulation, such description must be
-     sufficiently detailed for a recipient of ordinary skill to be able to
-     understand it.
-
-5. Application of this License.
-
-     This License applies to code to which the Initial Developer has
-     attached the notice in Exhibit A and to related Covered Code.
-
-6. Versions of the License.
-
-     6.1. New Versions.
-     Netscape Communications Corporation ("Netscape") may publish revised
-     and/or new versions of the License from time to time. Each version
-     will be given a distinguishing version number.
-
-     6.2. Effect of New Versions.
-     Once Covered Code has been published under a particular version of the
-     License, You may always continue to use it under the terms of that
-     version. You may also choose to use such Covered Code under the terms
-     of any subsequent version of the License published by Netscape. No one
-     other than Netscape has the right to modify the terms applicable to
-     Covered Code created under this License.
-
-     6.3. Derivative Works.
-     If You create or use a modified version of this License (which you may
-     only do in order to apply it to code which is not already Covered Code
-     governed by this License), You must (a) rename Your license so that
-     the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
-     "MPL", "NPL" or any confusingly similar phrase do not appear in your
-     license (except to note that your license differs from this License)
-     and (b) otherwise make it clear that Your version of the license
-     contains terms which differ from the Mozilla Public License and
-     Netscape Public License. (Filling in the name of the Initial
-     Developer, Original Code or Contributor in the notice described in
-     Exhibit A shall not of themselves be deemed to be modifications of
-     this License.)
-
-7. DISCLAIMER OF WARRANTY.
-
-     COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
-     WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
-     WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
-     DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
-     THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
-     IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
-     YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
-     COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
-     OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
-     ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-8. TERMINATION.
-
-     8.1.  This License and the rights granted hereunder will terminate
-     automatically if You fail to comply with terms herein and fail to cure
-     such breach within 30 days of becoming aware of the breach. All
-     sublicenses to the Covered Code which are properly granted shall
-     survive any termination of this License. Provisions which, by their
-     nature, must remain in effect beyond the termination of this License
-     shall survive.
-
-     8.2.  If You initiate litigation by asserting a patent infringement
-     claim (excluding declatory judgment actions) against Initial Developer
-     or a Contributor (the Initial Developer or Contributor against whom
-     You file such action is referred to as "Participant")  alleging that:
-
-     (a)  such Participant's Contributor Version directly or indirectly
-     infringes any patent, then any and all rights granted by such
-     Participant to You under Sections 2.1 and/or 2.2 of this License
-     shall, upon 60 days notice from Participant terminate prospectively,
-     unless if within 60 days after receipt of notice You either: (i)
-     agree in writing to pay Participant a mutually agreeable reasonable
-     royalty for Your past and future use of Modifications made by such
-     Participant, or (ii) withdraw Your litigation claim with respect to
-     the Contributor Version against such Participant.  If within 60 days
-     of notice, a reasonable royalty and payment arrangement are not
-     mutually agreed upon in writing by the parties or the litigation claim
-     is not withdrawn, the rights granted by Participant to You under
-     Sections 2.1 and/or 2.2 automatically terminate at the expiration of
-     the 60 day notice period specified above.
-
-     (b)  any software, hardware, or device, other than such Participant's
-     Contributor Version, directly or indirectly infringes any patent, then
-     any rights granted to You by such Participant under Sections 2.1(b)
-     and 2.2(b) are revoked effective as of the date You first made, used,
-     sold, distributed, or had made, Modifications made by that
-     Participant.
-
-     8.3.  If You assert a patent infringement claim against Participant
-     alleging that such Participant's Contributor Version directly or
-     indirectly infringes any patent where such claim is resolved (such as
-     by license or settlement) prior to the initiation of patent
-     infringement litigation, then the reasonable value of the licenses
-     granted by such Participant under Sections 2.1 or 2.2 shall be taken
-     into account in determining the amount or value of any payment or
-     license.
-
-     8.4.  In the event of termination under Sections 8.1 or 8.2 above,
-     all end user license agreements (excluding distributors and resellers)
-     which have been validly granted by You or any distributor hereunder
-     prior to termination shall survive termination.
-
-9. LIMITATION OF LIABILITY.
-
-     UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
-     (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
-     DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
-     OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
-     ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
-     CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
-     WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
-     COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
-     INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
-     LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
-     RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
-     PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
-     EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
-     THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-10. U.S. GOVERNMENT END USERS.
-
-     The Covered Code is a "commercial item," as that term is defined in
-     48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
-     software" and "commercial computer software documentation," as such
-     terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
-     C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
-     all U.S. Government End Users acquire Covered Code with only those
-     rights set forth herein.
-
-11. MISCELLANEOUS.
-
-     This License represents the complete agreement concerning subject
-     matter hereof. If any provision of this License is held to be
-     unenforceable, such provision shall be reformed only to the extent
-     necessary to make it enforceable. This License shall be governed by
-     California law provisions (except to the extent applicable law, if
-     any, provides otherwise), excluding its conflict-of-law provisions.
-     With respect to disputes in which at least one party is a citizen of,
-     or an entity chartered or registered to do business in the United
-     States of America, any litigation relating to this License shall be
-     subject to the jurisdiction of the Federal Courts of the Northern
-     District of California, with venue lying in Santa Clara County,
-     California, with the losing party responsible for costs, including
-     without limitation, court costs and reasonable attorneys' fees and
-     expenses. The application of the United Nations Convention on
-     Contracts for the International Sale of Goods is expressly excluded.
-     Any law or regulation which provides that the language of a contract
-     shall be construed against the drafter shall not apply to this
-     License.
-
-12. RESPONSIBILITY FOR CLAIMS.
-
-     As between Initial Developer and the Contributors, each party is
-     responsible for claims and damages arising, directly or indirectly,
-     out of its utilization of rights under this License and You agree to
-     work with Initial Developer and Contributors to distribute such
-     responsibility on an equitable basis. Nothing herein is intended or
-     shall be deemed to constitute any admission of liability.
-
-13. MULTIPLE-LICENSED CODE.
-
-     Initial Developer may designate portions of the Covered Code as
-     "Multiple-Licensed".  "Multiple-Licensed" means that the Initial
-     Developer permits you to utilize portions of the Covered Code under
-     Your choice of the NPL or the alternative licenses, if any, specified
-     by the Initial Developer in the file described in Exhibit A.
-
-EXHIBIT A -Mozilla Public License.
-
-     ``The contents of this file are subject to the Mozilla Public License
-     Version 1.1 (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.mozilla.org/MPL/
-
-     Software distributed under the License is distributed on an "AS IS"
-     basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
-     License for the specific language governing rights and limitations
-     under the License.
-
-     The Original Code is ______________________________________.
-
-     The Initial Developer of the Original Code is ________________________.
-     Portions created by ______________________ are Copyright (C) ______
-     _______________________. All Rights Reserved.
-
-     Contributor(s): ______________________________________.
-
-     Alternatively, the contents of this file may be used under the terms
-     of the _____ license (the  "[___] License"), in which case the
-     provisions of [______] License are applicable instead of those
-     above.  If you wish to allow use of your version of this file only
-     under the terms of the [____] License and not to allow others to use
-     your version of this file under the MPL, indicate your decision by
-     deleting  the provisions above and replace  them with the notice and
-     other provisions required by the [___] License.  If you do not delete
-     the provisions above, a recipient may use your version of this file
-     under either the MPL or the [___] License."
-
-     [NOTE: The text of this Exhibit A may differ slightly from the text of
-     the notices in the Source Code files of the Original Code. You should
-     use the text of this Exhibit A rather than the text found in the
-     Original Code Source Code for Your Modifications.]
-
+Copyright (C) 2013 Glyptodon LLC
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/Makefile.am b/Makefile.am
index 862d00e..9b5ab08 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,66 +1,72 @@
-# ***** BEGIN LICENSE BLOCK *****
-# Version: MPL 1.1/GPL 2.0/LGPL 2.1
 #
-# The contents of this file are subject to the Mozilla Public License Version
-# 1.1 (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.mozilla.org/MPL/
+# Copyright (C) 2015 Glyptodon LLC
 #
-# Software distributed under the License is distributed on an "AS IS" basis,
-# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-# for the specific language governing rights and limitations under the
-# License.
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
 #
-# The Original Code is guacamole-server.
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
 #
-# The Initial Developer of the Original Code is
-# Michael Jumper.
-# Portions created by the Initial Developer are Copyright (C) 2010
-# the Initial Developer. All Rights Reserved.
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
 #
-# Contributor(s):
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either the GNU General Public License Version 2 or later (the "GPL"), or
-# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-# in which case the provisions of the GPL or the LGPL are applicable instead
-# of those above. If you wish to allow use of your version of this file only
-# under the terms of either the GPL or the LGPL, and not to allow others to
-# use your version of this file under the terms of the MPL, indicate your
-# decision by deleting the provisions above and replace them with the notice
-# and other provisions required by the GPL or the LGPL. If you do not delete
-# the provisions above, a recipient may use your version of this file under
-# the terms of any one of the MPL, the GPL or the LGPL.
-#
-# ***** END LICENSE BLOCK *****
 
 ACLOCAL_AMFLAGS = -I m4
 
 # Subprojects
 DIST_SUBDIRS =           \
     src/libguac          \
-	src/guacd            \
-	src/protocols/rdp    \
-	src/protocols/ssh    \
-	src/protocols/vnc    \
+    src/common           \
+    src/common-ssh       \
+    src/terminal         \
+    src/guacd            \
+    src/protocols/rdp    \
+    src/protocols/ssh    \
+    src/protocols/telnet \
+    src/protocols/vnc    \
     tests
 
 SUBDIRS =       \
     src/libguac \
-	src/guacd   \
+    src/common  \
+    src/guacd   \
     tests
 
+if ENABLE_COMMON_SSH
+SUBDIRS += src/common-ssh
+endif
+
+if ENABLE_TERMINAL
+SUBDIRS += src/terminal
+endif
+
 if ENABLE_RDP
-    SUBDIRS += src/protocols/rdp
+SUBDIRS += src/protocols/rdp
 endif
 
 if ENABLE_SSH
-    SUBDIRS += src/protocols/ssh
+SUBDIRS += src/protocols/ssh
+endif
+
+if ENABLE_TELNET
+SUBDIRS += src/protocols/telnet
 endif
 
 if ENABLE_VNC
-    SUBDIRS += src/protocols/vnc
+SUBDIRS += src/protocols/vnc
 endif
 
-EXTRA_DIST = LICENSE doc/Doxyfile
+EXTRA_DIST =     \
+    LICENSE      \
+    bin/guacctl  \
+    doc/Doxyfile
 
diff --git a/Makefile.in b/Makefile.in
index 3908888..6674f2e 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.6 from Makefile.am.
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software
-# Foundation, Inc.
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
@@ -15,59 +14,73 @@
 
 @SET_MAKE@
 
-# ***** BEGIN LICENSE BLOCK *****
-# Version: MPL 1.1/GPL 2.0/LGPL 2.1
-#
-# The contents of this file are subject to the Mozilla Public License Version
-# 1.1 (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.mozilla.org/MPL/
 #
-# Software distributed under the License is distributed on an "AS IS" basis,
-# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-# for the specific language governing rights and limitations under the
-# License.
+# Copyright (C) 2015 Glyptodon LLC
 #
-# The Original Code is guacamole-server.
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
 #
-# The Initial Developer of the Original Code is
-# Michael Jumper.
-# Portions created by the Initial Developer are Copyright (C) 2010
-# the Initial Developer. All Rights Reserved.
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
 #
-# Contributor(s):
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
 #
-# Alternatively, the contents of this file may be used under the terms of
-# either the GNU General Public License Version 2 or later (the "GPL"), or
-# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-# in which case the provisions of the GPL or the LGPL are applicable instead
-# of those above. If you wish to allow use of your version of this file only
-# under the terms of either the GPL or the LGPL, and not to allow others to
-# use your version of this file under the terms of the MPL, indicate your
-# decision by deleting the provisions above and replace them with the notice
-# and other provisions required by the GPL or the LGPL. If you do not delete
-# the provisions above, a recipient may use your version of this file under
-# the terms of any one of the MPL, the GPL or the LGPL.
-#
-# ***** END LICENSE BLOCK *****
 VPATH = @srcdir@
-am__make_dryrun = \
-  { \
-    am__dry=no; \
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
     case $$MAKEFLAGS in \
       *\\[\ \	]*) \
-        echo 'am--echo: ; @echo "AM"  OK' | $(MAKE) -f - 2>/dev/null \
-          | grep '^AM OK$$' >/dev/null || am__dry=yes;; \
-      *) \
-        for am__flg in $$MAKEFLAGS; do \
-          case $$am__flg in \
-            *=*|--*) ;; \
-            *n*) am__dry=yes; break;; \
-          esac; \
-        done;; \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
     esac; \
-    test $$am__dry = yes; \
-  }
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
 pkgdatadir = $(datadir)/@PACKAGE@
 pkgincludedir = $(includedir)/@PACKAGE@
 pkglibdir = $(libdir)/@PACKAGE@
@@ -86,14 +99,17 @@ PRE_UNINSTALL = :
 POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
- at ENABLE_RDP_TRUE@am__append_1 = src/protocols/rdp
- at ENABLE_SSH_TRUE@am__append_2 = src/protocols/ssh
- at ENABLE_VNC_TRUE@am__append_3 = src/protocols/vnc
+ at ENABLE_COMMON_SSH_TRUE@am__append_1 = src/common-ssh
+ at ENABLE_TERMINAL_TRUE@am__append_2 = src/terminal
+ at ENABLE_RDP_TRUE@am__append_3 = src/protocols/rdp
+ at ENABLE_SSH_TRUE@am__append_4 = src/protocols/ssh
+ at ENABLE_TELNET_TRUE@am__append_5 = src/protocols/telnet
+ at ENABLE_VNC_TRUE@am__append_6 = src/protocols/vnc
 subdir = .
-DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \
-	$(srcdir)/Makefile.in $(top_srcdir)/configure AUTHORS \
-	ChangeLog config.guess config.sub depcomp install-sh ltmain.sh \
-	missing
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(top_srcdir)/configure $(am__configure_deps) \
+	$(srcdir)/config.h.in AUTHORS ChangeLog README compile \
+	config.guess config.sub depcomp install-sh missing ltmain.sh
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
 	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
@@ -104,17 +120,31 @@ am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
  configure.lineno config.status.lineno
 mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = config.h
 CONFIG_CLEAN_FILES =
 CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_ at AM_V@)
+am__v_P_ = $(am__v_P_ at AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_ at AM_V@)
+am__v_GEN_ = $(am__v_GEN_ at AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_ at AM_V@)
+am__v_at_ = $(am__v_at_ at AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
 SOURCES =
 DIST_SOURCES =
-RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
-	html-recursive info-recursive install-data-recursive \
-	install-dvi-recursive install-exec-recursive \
-	install-html-recursive install-info-recursive \
-	install-pdf-recursive install-ps-recursive install-recursive \
-	installcheck-recursive installdirs-recursive pdf-recursive \
-	ps-recursive uninstall-recursive
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+	ctags-recursive dvi-recursive html-recursive info-recursive \
+	install-data-recursive install-dvi-recursive \
+	install-exec-recursive install-html-recursive \
+	install-info-recursive install-pdf-recursive \
+	install-ps-recursive install-recursive installcheck-recursive \
+	installdirs-recursive pdf-recursive ps-recursive \
+	tags-recursive uninstall-recursive
 am__can_run_installinfo = \
   case $$AM_UPDATE_INFO_DIR in \
     n|no|NO) false;; \
@@ -122,11 +152,33 @@ am__can_run_installinfo = \
   esac
 RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
   distclean-recursive maintainer-clean-recursive
-AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
-	$(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \
-	distdir dist dist-all distcheck
+am__recursive_targets = \
+  $(RECURSIVE_TARGETS) \
+  $(RECURSIVE_CLEAN_TARGETS) \
+  $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+	cscope distdir dist dist-all distcheck
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \
+	$(LISP)config.h.in
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
 ETAGS = etags
 CTAGS = ctags
+CSCOPE = cscope
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 distdir = $(PACKAGE)-$(VERSION)
 top_distdir = $(distdir)
@@ -136,6 +188,7 @@ am__remove_distdir = \
       && rm -rf "$(distdir)" \
       || { sleep 5 && rm -rf "$(distdir)"; }; \
   else :; fi
+am__post_remove_distdir = $(am__remove_distdir)
 am__relativize = \
   dir0=`pwd`; \
   sed_first='s,^\([^/]*\)/.*$$,\1,'; \
@@ -163,12 +216,14 @@ am__relativize = \
   reldir="$$dir2"
 DIST_ARCHIVES = $(distdir).tar.gz
 GZIP_ENV = --best
+DIST_TARGETS = dist-gzip
 distuninstallcheck_listfiles = find . -type f -print
 am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \
   | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$'
 distcleancheck_listfiles = find . -type f -print
 ACLOCAL = @ACLOCAL@
 AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -178,6 +233,10 @@ CAIRO_LIBS = @CAIRO_LIBS@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+COMMON_INCLUDE = @COMMON_INCLUDE@
+COMMON_LTLIB = @COMMON_LTLIB@
+COMMON_SSH_INCLUDE = @COMMON_SSH_INCLUDE@
+COMMON_SSH_LTLIB = @COMMON_SSH_LTLIB@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
 CUNIT_LIBS = @CUNIT_LIBS@
@@ -185,7 +244,6 @@ CYGPATH_W = @CYGPATH_W@
 DEFS = @DEFS@
 DEPDIR = @DEPDIR@
 DLLTOOL = @DLLTOOL@
-DL_LIBS = @DL_LIBS@
 DSYMUTIL = @DSYMUTIL@
 DUMPBIN = @DUMPBIN@
 ECHO_C = @ECHO_C@
@@ -200,8 +258,10 @@ INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
 INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+JPEG_LIBS = @JPEG_LIBS@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
+LIBADD_DLOPEN = @LIBADD_DLOPEN@
 LIBGUAC_INCLUDE = @LIBGUAC_INCLUDE@
 LIBGUAC_LTLIB = @LIBGUAC_LTLIB@
 LIBOBJS = @LIBOBJS@
@@ -212,6 +272,7 @@ LN_S = @LN_S@
 LTLIBOBJS = @LTLIBOBJS@
 MAKEINFO = @MAKEINFO@
 MANIFEST_TOOL = @MANIFEST_TOOL@
+MATH_LIBS = @MATH_LIBS@
 MKDIR_P = @MKDIR_P@
 NM = @NM@
 NMEDIT = @NMEDIT@
@@ -245,9 +306,14 @@ SHELL = @SHELL@
 SSH_LIBS = @SSH_LIBS@
 SSL_LIBS = @SSL_LIBS@
 STRIP = @STRIP@
+TELNET_LIBS = @TELNET_LIBS@
+TERMINAL_INCLUDE = @TERMINAL_INCLUDE@
+TERMINAL_LTLIB = @TERMINAL_LTLIB@
+UUID_LIBS = @UUID_LIBS@
 VERSION = @VERSION@
 VNC_LIBS = @VNC_LIBS@
 VORBIS_LIBS = @VORBIS_LIBS@
+WEBP_LIBS = @WEBP_LIBS@
 abs_builddir = @abs_builddir@
 abs_srcdir = @abs_srcdir@
 abs_top_builddir = @abs_top_builddir@
@@ -306,16 +372,26 @@ ACLOCAL_AMFLAGS = -I m4
 # Subprojects
 DIST_SUBDIRS = \
     src/libguac          \
-	src/guacd            \
-	src/protocols/rdp    \
-	src/protocols/ssh    \
-	src/protocols/vnc    \
+    src/common           \
+    src/common-ssh       \
+    src/terminal         \
+    src/guacd            \
+    src/protocols/rdp    \
+    src/protocols/ssh    \
+    src/protocols/telnet \
+    src/protocols/vnc    \
     tests
 
-SUBDIRS = src/libguac src/guacd tests $(am__append_1) $(am__append_2) \
-	$(am__append_3)
-EXTRA_DIST = LICENSE doc/Doxyfile
-all: all-recursive
+SUBDIRS = src/libguac src/common src/guacd tests $(am__append_1) \
+	$(am__append_2) $(am__append_3) $(am__append_4) \
+	$(am__append_5) $(am__append_6)
+EXTRA_DIST = \
+    LICENSE      \
+    bin/guacctl  \
+    doc/Doxyfile
+
+all: config.h
+	$(MAKE) $(AM_MAKEFLAGS) all-recursive
 
 .SUFFIXES:
 am--refresh: Makefile
@@ -353,6 +429,21 @@ $(ACLOCAL_M4):  $(am__aclocal_m4_deps)
 	$(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
 $(am__aclocal_m4_deps):
 
+config.h: stamp-h1
+	@test -f $@ || rm -f stamp-h1
+	@test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1
+
+stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
+	@rm -f stamp-h1
+	cd $(top_builddir) && $(SHELL) ./config.status config.h
+$(srcdir)/config.h.in:  $(am__configure_deps) 
+	($(am__cd) $(top_srcdir) && $(AUTOHEADER))
+	rm -f stamp-h1
+	touch $@
+
+distclean-hdr:
+	-rm -f config.h stamp-h1
+
 mostlyclean-libtool:
 	-rm -f *.lo
 
@@ -363,22 +454,25 @@ distclean-libtool:
 	-rm -f libtool config.lt
 
 # This directory's subdirectories are mostly independent; you can cd
-# into them and run `make' without going through this Makefile.
-# To change the values of `make' variables: instead of editing Makefiles,
-# (1) if the variable is set in `config.status', edit `config.status'
-#     (which will cause the Makefiles to be regenerated when you run `make');
-# (2) otherwise, pass the desired values on the `make' command line.
-$(RECURSIVE_TARGETS):
-	@fail= failcom='exit 1'; \
-	for f in x $$MAKEFLAGS; do \
-	  case $$f in \
-	    *=* | --[!k]*);; \
-	    *k*) failcom='fail=yes';; \
-	  esac; \
-	done; \
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+#     (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+	@fail=; \
+	if $(am__make_keepgoing); then \
+	  failcom='fail=yes'; \
+	else \
+	  failcom='exit 1'; \
+	fi; \
 	dot_seen=no; \
 	target=`echo $@ | sed s/-recursive//`; \
-	list='$(SUBDIRS)'; for subdir in $$list; do \
+	case "$@" in \
+	  distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+	  *) list='$(SUBDIRS)' ;; \
+	esac; \
+	for subdir in $$list; do \
 	  echo "Making $$target in $$subdir"; \
 	  if test "$$subdir" = "."; then \
 	    dot_seen=yes; \
@@ -393,57 +487,12 @@ $(RECURSIVE_TARGETS):
 	  $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
 	fi; test -z "$$fail"
 
-$(RECURSIVE_CLEAN_TARGETS):
-	@fail= failcom='exit 1'; \
-	for f in x $$MAKEFLAGS; do \
-	  case $$f in \
-	    *=* | --[!k]*);; \
-	    *k*) failcom='fail=yes';; \
-	  esac; \
-	done; \
-	dot_seen=no; \
-	case "$@" in \
-	  distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
-	  *) list='$(SUBDIRS)' ;; \
-	esac; \
-	rev=''; for subdir in $$list; do \
-	  if test "$$subdir" = "."; then :; else \
-	    rev="$$subdir $$rev"; \
-	  fi; \
-	done; \
-	rev="$$rev ."; \
-	target=`echo $@ | sed s/-recursive//`; \
-	for subdir in $$rev; do \
-	  echo "Making $$target in $$subdir"; \
-	  if test "$$subdir" = "."; then \
-	    local_target="$$target-am"; \
-	  else \
-	    local_target="$$target"; \
-	  fi; \
-	  ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
-	  || eval $$failcom; \
-	done && test -z "$$fail"
-tags-recursive:
-	list='$(SUBDIRS)'; for subdir in $$list; do \
-	  test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
-	done
-ctags-recursive:
-	list='$(SUBDIRS)'; for subdir in $$list; do \
-	  test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
-	done
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
 
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
-	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
-	unique=`for i in $$list; do \
-	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-	  done | \
-	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
-	      END { if (nonempty) { for (i in files) print i; }; }'`; \
-	mkid -fID $$unique
-tags: TAGS
-
-TAGS: tags-recursive $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
-		$(TAGS_FILES) $(LISP)
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
 	set x; \
 	here=`pwd`; \
 	if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
@@ -459,12 +508,7 @@ TAGS: tags-recursive $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
 	      set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
 	  fi; \
 	done; \
-	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
-	unique=`for i in $$list; do \
-	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-	  done | \
-	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
-	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	$(am__define_uniq_tagged_files); \
 	shift; \
 	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
 	  test -n "$$unique" || unique=$$empty_fix; \
@@ -476,15 +520,11 @@ TAGS: tags-recursive $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
 	      $$unique; \
 	  fi; \
 	fi
-ctags: CTAGS
-CTAGS: ctags-recursive $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
-		$(TAGS_FILES) $(LISP)
-	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
-	unique=`for i in $$list; do \
-	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-	  done | \
-	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
-	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
 	test -z "$(CTAGS_ARGS)$$unique" \
 	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
 	     $$unique
@@ -493,9 +533,31 @@ GTAGS:
 	here=`$(am__cd) $(top_builddir) && pwd` \
 	  && $(am__cd) $(top_srcdir) \
 	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscope: cscope.files
+	test ! -s cscope.files \
+	  || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS)
+clean-cscope:
+	-rm -f cscope.files
+cscope.files: clean-cscope cscopelist
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
 
 distclean-tags:
 	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+	-rm -f cscope.out cscope.in.out cscope.po.out cscope.files
 
 distdir: $(DISTFILES)
 	$(am__remove_distdir)
@@ -563,40 +625,42 @@ distdir: $(DISTFILES)
 	|| chmod -R a+r "$(distdir)"
 dist-gzip: distdir
 	tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
-	$(am__remove_distdir)
+	$(am__post_remove_distdir)
 
 dist-bzip2: distdir
 	tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2
-	$(am__remove_distdir)
+	$(am__post_remove_distdir)
 
 dist-lzip: distdir
 	tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz
-	$(am__remove_distdir)
-
-dist-lzma: distdir
-	tardir=$(distdir) && $(am__tar) | lzma -9 -c >$(distdir).tar.lzma
-	$(am__remove_distdir)
+	$(am__post_remove_distdir)
 
 dist-xz: distdir
 	tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz
-	$(am__remove_distdir)
+	$(am__post_remove_distdir)
 
 dist-tarZ: distdir
+	@echo WARNING: "Support for shar distribution archives is" \
+	               "deprecated." >&2
+	@echo WARNING: "It will be removed altogether in Automake 2.0" >&2
 	tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
-	$(am__remove_distdir)
+	$(am__post_remove_distdir)
 
 dist-shar: distdir
+	@echo WARNING: "Support for distribution archives compressed with" \
+		       "legacy program 'compress' is deprecated." >&2
+	@echo WARNING: "It will be removed altogether in Automake 2.0" >&2
 	shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
-	$(am__remove_distdir)
+	$(am__post_remove_distdir)
 
 dist-zip: distdir
 	-rm -f $(distdir).zip
 	zip -rq $(distdir).zip $(distdir)
-	$(am__remove_distdir)
+	$(am__post_remove_distdir)
 
-dist dist-all: distdir
-	tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
-	$(am__remove_distdir)
+dist dist-all:
+	$(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:'
+	$(am__post_remove_distdir)
 
 # This target untars the dist file and tries a VPATH configuration.  Then
 # it guarantees that the distribution is self-contained by making another
@@ -607,8 +671,6 @@ distcheck: dist
 	  GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\
 	*.tar.bz2*) \
 	  bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\
-	*.tar.lzma*) \
-	  lzma -dc $(distdir).tar.lzma | $(am__untar) ;;\
 	*.tar.lz*) \
 	  lzip -dc $(distdir).tar.lz | $(am__untar) ;;\
 	*.tar.xz*) \
@@ -620,18 +682,19 @@ distcheck: dist
 	*.zip*) \
 	  unzip $(distdir).zip ;;\
 	esac
-	chmod -R a-w $(distdir); chmod u+w $(distdir)
-	mkdir $(distdir)/_build
-	mkdir $(distdir)/_inst
+	chmod -R a-w $(distdir)
+	chmod u+w $(distdir)
+	mkdir $(distdir)/_build $(distdir)/_inst
 	chmod a-w $(distdir)
 	test -d $(distdir)/_build || exit 0; \
 	dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
 	  && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
 	  && am__cwd=`pwd` \
 	  && $(am__cd) $(distdir)/_build \
-	  && ../configure --srcdir=.. --prefix="$$dc_install_base" \
+	  && ../configure \
 	    $(AM_DISTCHECK_CONFIGURE_FLAGS) \
 	    $(DISTCHECK_CONFIGURE_FLAGS) \
+	    --srcdir=.. --prefix="$$dc_install_base" \
 	  && $(MAKE) $(AM_MAKEFLAGS) \
 	  && $(MAKE) $(AM_MAKEFLAGS) dvi \
 	  && $(MAKE) $(AM_MAKEFLAGS) check \
@@ -654,7 +717,7 @@ distcheck: dist
 	  && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \
 	  && cd "$$am__cwd" \
 	  || exit 1
-	$(am__remove_distdir)
+	$(am__post_remove_distdir)
 	@(echo "$(distdir) archives ready for distribution: "; \
 	  list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
 	  sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
@@ -686,7 +749,7 @@ distcleancheck: distclean
 	       exit 1; } >&2
 check-am: all-am
 check: check-recursive
-all-am: Makefile
+all-am: Makefile config.h
 installdirs: installdirs-recursive
 installdirs-am:
 install: install-recursive
@@ -726,8 +789,8 @@ clean-am: clean-generic clean-libtool mostlyclean-am
 distclean: distclean-recursive
 	-rm -f $(am__CONFIG_DISTCLEAN_FILES)
 	-rm -f Makefile
-distclean-am: clean-am distclean-generic distclean-libtool \
-	distclean-tags
+distclean-am: clean-am distclean-generic distclean-hdr \
+	distclean-libtool distclean-tags
 
 dvi: dvi-recursive
 
@@ -789,24 +852,24 @@ ps-am:
 
 uninstall-am:
 
-.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) ctags-recursive \
-	install-am install-strip tags-recursive
-
-.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
-	all all-am am--refresh check check-am clean clean-generic \
-	clean-libtool ctags ctags-recursive dist dist-all dist-bzip2 \
-	dist-gzip dist-lzip dist-lzma dist-shar dist-tarZ dist-xz \
-	dist-zip distcheck distclean distclean-generic \
-	distclean-libtool distclean-tags distcleancheck distdir \
-	distuninstallcheck dvi dvi-am html html-am info info-am \
-	install install-am install-data install-data-am install-dvi \
-	install-dvi-am install-exec install-exec-am install-html \
-	install-html-am install-info install-info-am install-man \
-	install-pdf install-pdf-am install-ps install-ps-am \
-	install-strip installcheck installcheck-am installdirs \
-	installdirs-am maintainer-clean maintainer-clean-generic \
-	mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
-	ps ps-am tags tags-recursive uninstall uninstall-am
+.MAKE: $(am__recursive_targets) all install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+	am--refresh check check-am clean clean-cscope clean-generic \
+	clean-libtool cscope cscopelist-am ctags ctags-am dist \
+	dist-all dist-bzip2 dist-gzip dist-lzip dist-shar dist-tarZ \
+	dist-xz dist-zip distcheck distclean distclean-generic \
+	distclean-hdr distclean-libtool distclean-tags distcleancheck \
+	distdir distuninstallcheck dvi dvi-am html html-am info \
+	info-am install install-am install-data install-data-am \
+	install-dvi install-dvi-am install-exec install-exec-am \
+	install-html install-html-am install-info install-info-am \
+	install-man install-pdf install-pdf-am install-ps \
+	install-ps-am install-strip installcheck installcheck-am \
+	installdirs installdirs-am maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-generic \
+	mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+	uninstall-am
 
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
diff --git a/README b/README
index 32a6090..97f4823 100644
--- a/README
+++ b/README
@@ -6,12 +6,11 @@
 This README is intended to provide quick and to-the-point documentation for
 technical users intending to compile parts of Guacamole themselves.
 
-Distribution-specific packages are available from the files section of the main
-project page:
+Source archives are available from the files section of the main project page:
  
     http://sourceforge.net/projects/guacamole/files/
 
-Distribution-specific documentation is provided on the Guacamole wiki:
+A full manual is provided on the Guacamole web site:
 
     http://guac-dev.org/
 
@@ -36,11 +35,6 @@ proxy which translates between arbitrary protocols and the Guacamole protocol.
  Compiling and installing guacd, libguac, etc.
 ------------------------------------------------------------
 
-Please note that distribution-specific pre-compiled packages are available from
-the files section of the main project site:
-
-    http://sourceforge.net/projects/guacamole/files/
-
 All software within guacamole-server is built using the popular GNU Automake,
 and thus provides the standard configure script. Before compiling, you need to
 have compiled and installed libguac, the core Guacamole library. This is
@@ -95,7 +89,7 @@ guacd itself directly without the init script (as any user):
 
     $ guacd
 
-guacd currently takes four command-line options:
+guacd currently takes several command-line options:
 
     -b HOST 
 
@@ -111,17 +105,26 @@ guacd currently takes four command-line options:
         file. This is useful for init scripts and is used by the provided init
         script.
 
+    -L LEVEL
+
+        Sets the maximum level at which guacd will log messages to syslog and,
+        if running in the foreground, the console.  Legal values are debug,
+        info, warning, and error.  The default value is info.
+
     -f
         Causes guacd to run in the foreground, rather than automatically
         forking into the background. 
 
+Additional information can be found in the guacd man page:
+
+    $ man guacd
 
 ------------------------------------------------------------
  Reporting problems
 ------------------------------------------------------------
 
-Please report any bugs encountered by opening a new ticket at the Trac system
+Please report any bugs encountered by opening a new issue in the JIRA system
 hosted at:
     
-    http://guac-dev.rg/trac/
+    http://glyptodon.org/jira/
 
diff --git a/aclocal.m4 b/aclocal.m4
index 6ae5efa..3707c3c 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -1,8 +1,7 @@
-# generated automatically by aclocal 1.11.6 -*- Autoconf -*-
+# generated automatically by aclocal 1.14.1 -*- Autoconf -*-
+
+# Copyright (C) 1996-2013 Free Software Foundation, Inc.
 
-# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-# 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation,
-# Inc.
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
@@ -12,13 +11,14 @@
 # even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 # PARTICULAR PURPOSE.
 
+m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
 m4_ifndef([AC_AUTOCONF_VERSION],
   [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
 m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],,
 [m4_warning([this file was generated for autoconf 2.69.
 You have another version of autoconf.  It may work, but is not guaranteed to.
 If you have problems, you may need to regenerate the build system entirely.
-To do so, use the procedure documented by the package, typically `autoreconf'.])])
+To do so, use the procedure documented by the package, typically 'autoreconf'.])])
 
 # pkg.m4 - Macros to locate and utilise pkg-config.            -*- Autoconf -*-
 # serial 1 (pkg-config-0.24)
@@ -180,25 +180,77 @@ else
 fi[]dnl
 ])# PKG_CHECK_MODULES
 
-# Copyright (C) 2002, 2003, 2005, 2006, 2007, 2008, 2011 Free Software
-# Foundation, Inc.
+
+# PKG_INSTALLDIR(DIRECTORY)
+# -------------------------
+# Substitutes the variable pkgconfigdir as the location where a module
+# should install pkg-config .pc files. By default the directory is
+# $libdir/pkgconfig, but the default can be changed by passing
+# DIRECTORY. The user can override through the --with-pkgconfigdir
+# parameter.
+AC_DEFUN([PKG_INSTALLDIR],
+[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])])
+m4_pushdef([pkg_description],
+    [pkg-config installation directory @<:@]pkg_default[@:>@])
+AC_ARG_WITH([pkgconfigdir],
+    [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],,
+    [with_pkgconfigdir=]pkg_default)
+AC_SUBST([pkgconfigdir], [$with_pkgconfigdir])
+m4_popdef([pkg_default])
+m4_popdef([pkg_description])
+]) dnl PKG_INSTALLDIR
+
+
+# PKG_NOARCH_INSTALLDIR(DIRECTORY)
+# -------------------------
+# Substitutes the variable noarch_pkgconfigdir as the location where a
+# module should install arch-independent pkg-config .pc files. By
+# default the directory is $datadir/pkgconfig, but the default can be
+# changed by passing DIRECTORY. The user can override through the
+# --with-noarch-pkgconfigdir parameter.
+AC_DEFUN([PKG_NOARCH_INSTALLDIR],
+[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])])
+m4_pushdef([pkg_description],
+    [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@])
+AC_ARG_WITH([noarch-pkgconfigdir],
+    [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],,
+    [with_noarch_pkgconfigdir=]pkg_default)
+AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir])
+m4_popdef([pkg_default])
+m4_popdef([pkg_description])
+]) dnl PKG_NOARCH_INSTALLDIR
+
+
+# PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
+# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+# -------------------------------------------
+# Retrieves the value of the pkg-config variable for the given module.
+AC_DEFUN([PKG_CHECK_VAR],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl
+
+_PKG_CONFIG([$1], [variable="][$3]["], [$2])
+AS_VAR_COPY([$1], [pkg_cv_][$1])
+
+AS_VAR_IF([$1], [""], [$5], [$4])dnl
+])# PKG_CHECK_VAR
+
+# Copyright (C) 2002-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
 
-# serial 1
-
 # AM_AUTOMAKE_VERSION(VERSION)
 # ----------------------------
 # Automake X.Y traces this macro to ensure aclocal.m4 has been
 # generated from the m4 files accompanying Automake X.Y.
 # (This private macro should not be called outside this file.)
 AC_DEFUN([AM_AUTOMAKE_VERSION],
-[am__api_version='1.11'
+[am__api_version='1.14'
 dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
 dnl require some minimum version.  Point them to the right macro.
-m4_if([$1], [1.11.6], [],
+m4_if([$1], [1.14.1], [],
       [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
 ])
 
@@ -214,24 +266,22 @@ m4_define([_AM_AUTOCONF_VERSION], [])
 # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
 # This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
 AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
-[AM_AUTOMAKE_VERSION([1.11.6])dnl
+[AM_AUTOMAKE_VERSION([1.14.1])dnl
 m4_ifndef([AC_AUTOCONF_VERSION],
   [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
 _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
 
 # AM_AUX_DIR_EXPAND                                         -*- Autoconf -*-
 
-# Copyright (C) 2001, 2003, 2005, 2011 Free Software Foundation, Inc.
+# Copyright (C) 2001-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
 
-# serial 1
-
 # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
-# $ac_aux_dir to `$srcdir/foo'.  In other projects, it is set to
-# `$srcdir', `$srcdir/..', or `$srcdir/../..'.
+# $ac_aux_dir to '$srcdir/foo'.  In other projects, it is set to
+# '$srcdir', '$srcdir/..', or '$srcdir/../..'.
 #
 # Of course, Automake must honor this variable whenever it calls a
 # tool from the auxiliary directory.  The problem is that $srcdir (and
@@ -250,7 +300,7 @@ _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
 #
 # The reason of the latter failure is that $top_srcdir and $ac_aux_dir
 # are both prefixed by $srcdir.  In an in-source build this is usually
-# harmless because $srcdir is `.', but things will broke when you
+# harmless because $srcdir is '.', but things will broke when you
 # start a VPATH build or use an absolute $srcdir.
 #
 # So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
@@ -276,14 +326,12 @@ am_aux_dir=`cd $ac_aux_dir && pwd`
 
 # AM_COND_IF                                            -*- Autoconf -*-
 
-# Copyright (C) 2008, 2010 Free Software Foundation, Inc.
+# Copyright (C) 2008-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
 
-# serial 3
-
 # _AM_COND_IF
 # _AM_COND_ELSE
 # _AM_COND_ENDIF
@@ -293,7 +341,6 @@ m4_define([_AM_COND_IF])
 m4_define([_AM_COND_ELSE])
 m4_define([_AM_COND_ENDIF])
 
-
 # AM_COND_IF(COND, [IF-TRUE], [IF-FALSE])
 # ---------------------------------------
 # If the shell condition COND is true, execute IF-TRUE, otherwise execute
@@ -316,22 +363,19 @@ fi[]dnl
 
 # AM_CONDITIONAL                                            -*- Autoconf -*-
 
-# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006, 2008
-# Free Software Foundation, Inc.
+# Copyright (C) 1997-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
 
-# serial 9
-
 # AM_CONDITIONAL(NAME, SHELL-CONDITION)
 # -------------------------------------
 # Define a conditional.
 AC_DEFUN([AM_CONDITIONAL],
-[AC_PREREQ(2.52)dnl
- ifelse([$1], [TRUE],  [AC_FATAL([$0: invalid condition: $1])],
-	[$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
+[AC_PREREQ([2.52])dnl
+ m4_if([$1], [TRUE],  [AC_FATAL([$0: invalid condition: $1])],
+       [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
 AC_SUBST([$1_TRUE])dnl
 AC_SUBST([$1_FALSE])dnl
 _AM_SUBST_NOTMAKE([$1_TRUE])dnl
@@ -350,16 +394,14 @@ AC_CONFIG_COMMANDS_PRE(
 Usually this means the macro was only invoked conditionally.]])
 fi])])
 
-# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009,
-# 2010, 2011 Free Software Foundation, Inc.
+# Copyright (C) 1999-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
 
-# serial 12
 
-# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be
+# There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be
 # written in clear, in which case automake, when reading aclocal.m4,
 # will think it sees a *use*, and therefore will trigger all it's
 # C support machinery.  Also note that it means that autoscan, seeing
@@ -369,7 +411,7 @@ fi])])
 # _AM_DEPENDENCIES(NAME)
 # ----------------------
 # See how the compiler implements dependency checking.
-# NAME is "CC", "CXX", "GCJ", or "OBJC".
+# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC".
 # We try a few techniques and use that to set a single cache variable.
 #
 # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
@@ -382,12 +424,13 @@ AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
 AC_REQUIRE([AM_MAKE_INCLUDE])dnl
 AC_REQUIRE([AM_DEP_TRACK])dnl
 
-ifelse([$1], CC,   [depcc="$CC"   am_compiler_list=],
-       [$1], CXX,  [depcc="$CXX"  am_compiler_list=],
-       [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
-       [$1], UPC,  [depcc="$UPC"  am_compiler_list=],
-       [$1], GCJ,  [depcc="$GCJ"  am_compiler_list='gcc3 gcc'],
-                   [depcc="$$1"   am_compiler_list=])
+m4_if([$1], [CC],   [depcc="$CC"   am_compiler_list=],
+      [$1], [CXX],  [depcc="$CXX"  am_compiler_list=],
+      [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
+      [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'],
+      [$1], [UPC],  [depcc="$UPC"  am_compiler_list=],
+      [$1], [GCJ],  [depcc="$GCJ"  am_compiler_list='gcc3 gcc'],
+                    [depcc="$$1"   am_compiler_list=])
 
 AC_CACHE_CHECK([dependency style of $depcc],
                [am_cv_$1_dependencies_compiler_type],
@@ -395,8 +438,8 @@ AC_CACHE_CHECK([dependency style of $depcc],
   # We make a subdir and do the tests there.  Otherwise we can end up
   # making bogus files that we don't know about and never remove.  For
   # instance it was reported that on HP-UX the gcc test will end up
-  # making a dummy file named `D' -- because `-MD' means `put the output
-  # in D'.
+  # making a dummy file named 'D' -- because '-MD' means "put the output
+  # in D".
   rm -rf conftest.dir
   mkdir conftest.dir
   # Copy depcomp to subdir because otherwise we won't find it if we're
@@ -436,16 +479,16 @@ AC_CACHE_CHECK([dependency style of $depcc],
     : > sub/conftest.c
     for i in 1 2 3 4 5 6; do
       echo '#include "conftst'$i'.h"' >> sub/conftest.c
-      # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
-      # Solaris 8's {/usr,}/bin/sh.
-      touch sub/conftst$i.h
+      # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
+      # Solaris 10 /bin/sh.
+      echo '/* dummy */' > sub/conftst$i.h
     done
     echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
 
-    # We check with `-c' and `-o' for the sake of the "dashmstdout"
+    # We check with '-c' and '-o' for the sake of the "dashmstdout"
     # mode.  It turns out that the SunPro C++ compiler does not properly
-    # handle `-M -o', and we need to detect this.  Also, some Intel
-    # versions had trouble with output in subdirs
+    # handle '-M -o', and we need to detect this.  Also, some Intel
+    # versions had trouble with output in subdirs.
     am__obj=sub/conftest.${OBJEXT-o}
     am__minus_obj="-o $am__obj"
     case $depmode in
@@ -454,8 +497,8 @@ AC_CACHE_CHECK([dependency style of $depcc],
       test "$am__universal" = false || continue
       ;;
     nosideeffect)
-      # after this tag, mechanisms are not by side-effect, so they'll
-      # only be used when explicitly requested
+      # After this tag, mechanisms are not by side-effect, so they'll
+      # only be used when explicitly requested.
       if test "x$enable_dependency_tracking" = xyes; then
 	continue
       else
@@ -463,7 +506,7 @@ AC_CACHE_CHECK([dependency style of $depcc],
       fi
       ;;
     msvc7 | msvc7msys | msvisualcpp | msvcmsys)
-      # This compiler won't grok `-c -o', but also, the minuso test has
+      # This compiler won't grok '-c -o', but also, the minuso test has
       # not run yet.  These depmodes are late enough in the game, and
       # so weak that their functioning should not be impacted.
       am__obj=conftest.${OBJEXT-o}
@@ -511,7 +554,7 @@ AM_CONDITIONAL([am__fastdep$1], [
 # AM_SET_DEPDIR
 # -------------
 # Choose a directory name for dependency files.
-# This macro is AC_REQUIREd in _AM_DEPENDENCIES
+# This macro is AC_REQUIREd in _AM_DEPENDENCIES.
 AC_DEFUN([AM_SET_DEPDIR],
 [AC_REQUIRE([AM_SET_LEADING_DOT])dnl
 AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
@@ -521,9 +564,13 @@ AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
 # AM_DEP_TRACK
 # ------------
 AC_DEFUN([AM_DEP_TRACK],
-[AC_ARG_ENABLE(dependency-tracking,
-[  --disable-dependency-tracking  speeds up one-time build
-  --enable-dependency-tracking   do not reject slow dependency extractors])
+[AC_ARG_ENABLE([dependency-tracking], [dnl
+AS_HELP_STRING(
+  [--enable-dependency-tracking],
+  [do not reject slow dependency extractors])
+AS_HELP_STRING(
+  [--disable-dependency-tracking],
+  [speeds up one-time build])])
 if test "x$enable_dependency_tracking" != xno; then
   am_depcomp="$ac_aux_dir/depcomp"
   AMDEPBACKSLASH='\'
@@ -538,20 +585,18 @@ _AM_SUBST_NOTMAKE([am__nodep])dnl
 
 # Generate code to set up dependency tracking.              -*- Autoconf -*-
 
-# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008
-# Free Software Foundation, Inc.
+# Copyright (C) 1999-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
 
-#serial 5
 
 # _AM_OUTPUT_DEPENDENCY_COMMANDS
 # ------------------------------
 AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
 [{
-  # Autoconf 2.62 quotes --file arguments for eval, but not when files
+  # Older Autoconf quotes --file arguments for eval, but not when files
   # are listed without --file.  Let's play safe and only enable the eval
   # if we detect the quoting.
   case $CONFIG_FILES in
@@ -564,7 +609,7 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
     # Strip MF so we end up with the name of the file.
     mf=`echo "$mf" | sed -e 's/:.*$//'`
     # Check whether this is an Automake generated Makefile or not.
-    # We used to match only the files named `Makefile.in', but
+    # We used to match only the files named 'Makefile.in', but
     # some people rename them; so instead we look at the file content.
     # Grep'ing the first line is not enough: some people post-process
     # each Makefile.in and add a new line on top of each file to say so.
@@ -576,21 +621,19 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
       continue
     fi
     # Extract the definition of DEPDIR, am__include, and am__quote
-    # from the Makefile without running `make'.
+    # from the Makefile without running 'make'.
     DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
     test -z "$DEPDIR" && continue
     am__include=`sed -n 's/^am__include = //p' < "$mf"`
-    test -z "am__include" && continue
+    test -z "$am__include" && continue
     am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
-    # When using ansi2knr, U may be empty or an underscore; expand it
-    U=`sed -n 's/^U = //p' < "$mf"`
     # Find all dependency output files, they are included files with
     # $(DEPDIR) in their names.  We invoke sed twice because it is the
     # simplest approach to changing $(DEPDIR) to its actual value in the
     # expansion.
     for file in `sed -n "
       s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
-	 sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+	 sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do
       # Make sure the directory exists.
       test -f "$dirpart/$file" && continue
       fdir=`AS_DIRNAME(["$file"])`
@@ -608,7 +651,7 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
 # This macro should only be invoked once -- use via AC_REQUIRE.
 #
 # This code is only required when automatic dependency tracking
-# is enabled.  FIXME.  This creates each `.P' file that we will
+# is enabled.  FIXME.  This creates each '.P' file that we will
 # need in order to bootstrap the dependency handling code.
 AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
 [AC_CONFIG_COMMANDS([depfiles],
@@ -618,18 +661,21 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
 
 # Do all the work for Automake.                             -*- Autoconf -*-
 
-# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-# 2005, 2006, 2008, 2009 Free Software Foundation, Inc.
+# Copyright (C) 1996-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
 
-# serial 16
-
 # This macro actually does too much.  Some checks are only needed if
 # your package does certain things.  But this isn't really a big deal.
 
+dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O.
+m4_define([AC_PROG_CC],
+m4_defn([AC_PROG_CC])
+[_AM_PROG_CC_C_O
+])
+
 # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
 # AM_INIT_AUTOMAKE([OPTIONS])
 # -----------------------------------------------
@@ -642,7 +688,7 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
 # arguments mandatory, and then we can depend on a new Autoconf
 # release and drop the old call support.
 AC_DEFUN([AM_INIT_AUTOMAKE],
-[AC_PREREQ([2.62])dnl
+[AC_PREREQ([2.65])dnl
 dnl Autoconf wants to disallow AM_ names.  We explicitly allow
 dnl the ones we care about.
 m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
@@ -671,31 +717,40 @@ AC_SUBST([CYGPATH_W])
 # Define the identity of the package.
 dnl Distinguish between old-style and new-style calls.
 m4_ifval([$2],
-[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
+[AC_DIAGNOSE([obsolete],
+             [$0: two- and three-arguments forms are deprecated.])
+m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
  AC_SUBST([PACKAGE], [$1])dnl
  AC_SUBST([VERSION], [$2])],
 [_AM_SET_OPTIONS([$1])dnl
 dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
-m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,,
+m4_if(
+  m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]),
+  [ok:ok],,
   [m4_fatal([AC_INIT should be called with package and version arguments])])dnl
  AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
  AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
 
 _AM_IF_OPTION([no-define],,
-[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
- AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl
+[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package])
+ AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl
 
 # Some tools Automake needs.
 AC_REQUIRE([AM_SANITY_CHECK])dnl
 AC_REQUIRE([AC_ARG_PROGRAM])dnl
-AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version})
-AM_MISSING_PROG(AUTOCONF, autoconf)
-AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version})
-AM_MISSING_PROG(AUTOHEADER, autoheader)
-AM_MISSING_PROG(MAKEINFO, makeinfo)
+AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}])
+AM_MISSING_PROG([AUTOCONF], [autoconf])
+AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}])
+AM_MISSING_PROG([AUTOHEADER], [autoheader])
+AM_MISSING_PROG([MAKEINFO], [makeinfo])
 AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
 AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl
-AC_REQUIRE([AM_PROG_MKDIR_P])dnl
+AC_REQUIRE([AC_PROG_MKDIR_P])dnl
+# For better backward compatibility.  To be removed once Automake 1.9.x
+# dies out for good.  For more background, see:
+# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
+# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
+AC_SUBST([mkdir_p], ['$(MKDIR_P)'])
 # We need awk for the "check" target.  The system "awk" is bad on
 # some platforms.
 AC_REQUIRE([AC_PROG_AWK])dnl
@@ -706,34 +761,78 @@ _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
 			     [_AM_PROG_TAR([v7])])])
 _AM_IF_OPTION([no-dependencies],,
 [AC_PROVIDE_IFELSE([AC_PROG_CC],
-		  [_AM_DEPENDENCIES(CC)],
-		  [define([AC_PROG_CC],
-			  defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl
+		  [_AM_DEPENDENCIES([CC])],
+		  [m4_define([AC_PROG_CC],
+			     m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl
 AC_PROVIDE_IFELSE([AC_PROG_CXX],
-		  [_AM_DEPENDENCIES(CXX)],
-		  [define([AC_PROG_CXX],
-			  defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
+		  [_AM_DEPENDENCIES([CXX])],
+		  [m4_define([AC_PROG_CXX],
+			     m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl
 AC_PROVIDE_IFELSE([AC_PROG_OBJC],
-		  [_AM_DEPENDENCIES(OBJC)],
-		  [define([AC_PROG_OBJC],
-			  defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl
+		  [_AM_DEPENDENCIES([OBJC])],
+		  [m4_define([AC_PROG_OBJC],
+			     m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_OBJCXX],
+		  [_AM_DEPENDENCIES([OBJCXX])],
+		  [m4_define([AC_PROG_OBJCXX],
+			     m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl
 ])
-_AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])dnl
-dnl The `parallel-tests' driver may need to know about EXEEXT, so add the
-dnl `am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen.  This macro
-dnl is hooked onto _AC_COMPILER_EXEEXT early, see below.
+AC_REQUIRE([AM_SILENT_RULES])dnl
+dnl The testsuite driver may need to know about EXEEXT, so add the
+dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen.  This
+dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below.
 AC_CONFIG_COMMANDS_PRE(dnl
 [m4_provide_if([_AM_COMPILER_EXEEXT],
   [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl
-])
 
-dnl Hook into `_AC_COMPILER_EXEEXT' early to learn its expansion.  Do not
+# POSIX will say in a future version that running "rm -f" with no argument
+# is OK; and we want to be able to make that assumption in our Makefile
+# recipes.  So use an aggressive probe to check that the usage we want is
+# actually supported "in the wild" to an acceptable degree.
+# See automake bug#10828.
+# To make any issue more visible, cause the running configure to be aborted
+# by default if the 'rm' program in use doesn't match our expectations; the
+# user can still override this though.
+if rm -f && rm -fr && rm -rf; then : OK; else
+  cat >&2 <<'END'
+Oops!
+
+Your 'rm' program seems unable to run without file operands specified
+on the command line, even when the '-f' option is present.  This is contrary
+to the behaviour of most rm programs out there, and not conforming with
+the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542>
+
+Please tell bug-automake at gnu.org about your system, including the value
+of your $PATH and any error possibly output before this message.  This
+can help us improve future automake versions.
+
+END
+  if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then
+    echo 'Configuration will proceed anyway, since you have set the' >&2
+    echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2
+    echo >&2
+  else
+    cat >&2 <<'END'
+Aborting the configuration process, to ensure you take notice of the issue.
+
+You can download and install GNU coreutils to get an 'rm' implementation
+that behaves properly: <http://www.gnu.org/software/coreutils/>.
+
+If you want to complete the configuration process using your problematic
+'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
+to "yes", and re-run configure.
+
+END
+    AC_MSG_ERROR([Your 'rm' program is bad, sorry.])
+  fi
+fi])
+
+dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion.  Do not
 dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further
 dnl mangled by Autoconf and run in a shell conditional statement.
 m4_define([_AC_COMPILER_EXEEXT],
 m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])])
 
-
 # When config.status generates a header, we must update the stamp-h file.
 # This file resides in the same directory as the config header
 # that is generated.  The stamp files are numbered to have different names.
@@ -755,15 +854,12 @@ for _am_header in $config_headers :; do
 done
 echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
 
-# Copyright (C) 2001, 2003, 2005, 2008, 2011 Free Software Foundation,
-# Inc.
+# Copyright (C) 2001-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
 
-# serial 1
-
 # AM_PROG_INSTALL_SH
 # ------------------
 # Define $install_sh.
@@ -777,16 +873,14 @@ if test x"${install_sh}" != xset; then
     install_sh="\${SHELL} $am_aux_dir/install-sh"
   esac
 fi
-AC_SUBST(install_sh)])
+AC_SUBST([install_sh])])
 
-# Copyright (C) 2003, 2005  Free Software Foundation, Inc.
+# Copyright (C) 2003-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
 
-# serial 2
-
 # Check whether the underlying file-system supports filenames
 # with a leading dot.  For instance MS-DOS doesn't.
 AC_DEFUN([AM_SET_LEADING_DOT],
@@ -802,14 +896,12 @@ AC_SUBST([am__leading_dot])])
 
 # Check to see how 'make' treats includes.	            -*- Autoconf -*-
 
-# Copyright (C) 2001, 2002, 2003, 2005, 2009  Free Software Foundation, Inc.
+# Copyright (C) 2001-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
 
-# serial 4
-
 # AM_MAKE_INCLUDE()
 # -----------------
 # Check to see how make treats includes.
@@ -827,7 +919,7 @@ am__quote=
 _am_result=none
 # First try GNU make style include.
 echo "include confinc" > confmf
-# Ignore all kinds of additional output from `make'.
+# Ignore all kinds of additional output from 'make'.
 case `$am_make -s -f confmf 2> /dev/null` in #(
 *the\ am__doit\ target*)
   am__include=include
@@ -854,15 +946,12 @@ rm -f confinc confmf
 
 # Fake the existence of programs that GNU maintainers use.  -*- Autoconf -*-
 
-# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005, 2008
-# Free Software Foundation, Inc.
+# Copyright (C) 1997-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
 
-# serial 6
-
 # AM_MISSING_PROG(NAME, PROGRAM)
 # ------------------------------
 AC_DEFUN([AM_MISSING_PROG],
@@ -870,11 +959,10 @@ AC_DEFUN([AM_MISSING_PROG],
 $1=${$1-"${am_missing_run}$2"}
 AC_SUBST($1)])
 
-
 # AM_MISSING_HAS_RUN
 # ------------------
-# Define MISSING if not defined so far and test if it supports --run.
-# If it does, set am_missing_run to use it, otherwise, to nothing.
+# Define MISSING if not defined so far and test if it is modern enough.
+# If it is, set am_missing_run to use it, otherwise, to nothing.
 AC_DEFUN([AM_MISSING_HAS_RUN],
 [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
 AC_REQUIRE_AUX_FILE([missing])dnl
@@ -887,54 +975,22 @@ if test x"${MISSING+set}" != xset; then
   esac
 fi
 # Use eval to expand $SHELL
-if eval "$MISSING --run true"; then
-  am_missing_run="$MISSING --run "
+if eval "$MISSING --is-lightweight"; then
+  am_missing_run="$MISSING "
 else
   am_missing_run=
-  AC_MSG_WARN([`missing' script is too old or missing])
+  AC_MSG_WARN(['missing' script is too old or missing])
 fi
 ])
 
-# Copyright (C) 2003, 2004, 2005, 2006, 2011 Free Software Foundation,
-# Inc.
-#
-# This file is free software; the Free Software Foundation
-# gives unlimited permission to copy and/or distribute it,
-# with or without modifications, as long as this notice is preserved.
-
-# serial 1
-
-# AM_PROG_MKDIR_P
-# ---------------
-# Check for `mkdir -p'.
-AC_DEFUN([AM_PROG_MKDIR_P],
-[AC_PREREQ([2.60])dnl
-AC_REQUIRE([AC_PROG_MKDIR_P])dnl
-dnl Automake 1.8 to 1.9.6 used to define mkdir_p.  We now use MKDIR_P,
-dnl while keeping a definition of mkdir_p for backward compatibility.
-dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile.
-dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of
-dnl Makefile.ins that do not define MKDIR_P, so we do our own
-dnl adjustment using top_builddir (which is defined more often than
-dnl MKDIR_P).
-AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl
-case $mkdir_p in
-  [[\\/$]]* | ?:[[\\/]]*) ;;
-  */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
-esac
-])
-
 # Helper functions for option handling.                     -*- Autoconf -*-
 
-# Copyright (C) 2001, 2002, 2003, 2005, 2008, 2010 Free Software
-# Foundation, Inc.
+# Copyright (C) 2001-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
 
-# serial 5
-
 # _AM_MANGLE_OPTION(NAME)
 # -----------------------
 AC_DEFUN([_AM_MANGLE_OPTION],
@@ -944,7 +1000,7 @@ AC_DEFUN([_AM_MANGLE_OPTION],
 # --------------------
 # Set option NAME.  Presently that only means defining a flag for this option.
 AC_DEFUN([_AM_SET_OPTION],
-[m4_define(_AM_MANGLE_OPTION([$1]), 1)])
+[m4_define(_AM_MANGLE_OPTION([$1]), [1])])
 
 # _AM_SET_OPTIONS(OPTIONS)
 # ------------------------
@@ -958,24 +1014,82 @@ AC_DEFUN([_AM_SET_OPTIONS],
 AC_DEFUN([_AM_IF_OPTION],
 [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
 
-# Check to make sure that the build environment is sane.    -*- Autoconf -*-
+# Copyright (C) 1999-2013 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# _AM_PROG_CC_C_O
+# ---------------
+# Like AC_PROG_CC_C_O, but changed for automake.  We rewrite AC_PROG_CC
+# to automatically call this.
+AC_DEFUN([_AM_PROG_CC_C_O],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([compile])dnl
+AC_LANG_PUSH([C])dnl
+AC_CACHE_CHECK(
+  [whether $CC understands -c and -o together],
+  [am_cv_prog_cc_c_o],
+  [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])])
+  # Make sure it works both with $CC and with simple cc.
+  # Following AC_PROG_CC_C_O, we do the test twice because some
+  # compilers refuse to overwrite an existing .o file with -o,
+  # though they will create one.
+  am_cv_prog_cc_c_o=yes
+  for am_i in 1 2; do
+    if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \
+         && test -f conftest2.$ac_objext; then
+      : OK
+    else
+      am_cv_prog_cc_c_o=no
+      break
+    fi
+  done
+  rm -f core conftest*
+  unset am_i])
+if test "$am_cv_prog_cc_c_o" != yes; then
+   # Losing compiler, so override with the script.
+   # FIXME: It is wrong to rewrite CC.
+   # But if we don't then we get into trouble of one sort or another.
+   # A longer-term fix would be to have automake use am__CC in this case,
+   # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
+   CC="$am_aux_dir/compile $CC"
+fi
+AC_LANG_POP([C])])
 
-# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005, 2008
-# Free Software Foundation, Inc.
+# For backward compatibility.
+AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])])
+
+# Copyright (C) 2001-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
 
-# serial 5
+# AM_RUN_LOG(COMMAND)
+# -------------------
+# Run COMMAND, save the exit status in ac_status, and log it.
+# (This has been adapted from Autoconf's _AC_RUN_LOG macro.)
+AC_DEFUN([AM_RUN_LOG],
+[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD
+   ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+   (exit $ac_status); }])
+
+# Check to make sure that the build environment is sane.    -*- Autoconf -*-
+
+# Copyright (C) 1996-2013 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
 
 # AM_SANITY_CHECK
 # ---------------
 AC_DEFUN([AM_SANITY_CHECK],
 [AC_MSG_CHECKING([whether build environment is sane])
-# Just in case
-sleep 1
-echo timestamp > conftest.file
 # Reject unsafe characters in $srcdir or the absolute working directory
 # name.  Accept space and tab only in the latter.
 am_lf='
@@ -986,32 +1100,40 @@ case `pwd` in
 esac
 case $srcdir in
   *[[\\\"\#\$\&\'\`$am_lf\ \	]]*)
-    AC_MSG_ERROR([unsafe srcdir value: `$srcdir']);;
+    AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);;
 esac
 
-# Do `set' in a subshell so we don't clobber the current shell's
+# Do 'set' in a subshell so we don't clobber the current shell's
 # arguments.  Must try -L first in case configure is actually a
 # symlink; some systems play weird games with the mod time of symlinks
 # (eg FreeBSD returns the mod time of the symlink's containing
 # directory).
 if (
-   set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
-   if test "$[*]" = "X"; then
-      # -L didn't work.
-      set X `ls -t "$srcdir/configure" conftest.file`
-   fi
-   rm -f conftest.file
-   if test "$[*]" != "X $srcdir/configure conftest.file" \
-      && test "$[*]" != "X conftest.file $srcdir/configure"; then
-
-      # If neither matched, then we have a broken ls.  This can happen
-      # if, for instance, CONFIG_SHELL is bash and it inherits a
-      # broken ls alias from the environment.  This has actually
-      # happened.  Such a system could not be considered "sane".
-      AC_MSG_ERROR([ls -t appears to fail.  Make sure there is not a broken
-alias in your environment])
-   fi
-
+   am_has_slept=no
+   for am_try in 1 2; do
+     echo "timestamp, slept: $am_has_slept" > conftest.file
+     set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+     if test "$[*]" = "X"; then
+	# -L didn't work.
+	set X `ls -t "$srcdir/configure" conftest.file`
+     fi
+     if test "$[*]" != "X $srcdir/configure conftest.file" \
+	&& test "$[*]" != "X conftest.file $srcdir/configure"; then
+
+	# If neither matched, then we have a broken ls.  This can happen
+	# if, for instance, CONFIG_SHELL is bash and it inherits a
+	# broken ls alias from the environment.  This has actually
+	# happened.  Such a system could not be considered "sane".
+	AC_MSG_ERROR([ls -t appears to fail.  Make sure there is not a broken
+  alias in your environment])
+     fi
+     if test "$[2]" = conftest.file || test $am_try -eq 2; then
+       break
+     fi
+     # Just in case.
+     sleep 1
+     am_has_slept=yes
+   done
    test "$[2]" = conftest.file
    )
 then
@@ -1021,46 +1143,118 @@ else
    AC_MSG_ERROR([newly created file is older than distributed files!
 Check your system clock])
 fi
-AC_MSG_RESULT(yes)])
+AC_MSG_RESULT([yes])
+# If we didn't sleep, we still need to ensure time stamps of config.status and
+# generated files are strictly newer.
+am_sleep_pid=
+if grep 'slept: no' conftest.file >/dev/null 2>&1; then
+  ( sleep 1 ) &
+  am_sleep_pid=$!
+fi
+AC_CONFIG_COMMANDS_PRE(
+  [AC_MSG_CHECKING([that generated files are newer than configure])
+   if test -n "$am_sleep_pid"; then
+     # Hide warnings about reused PIDs.
+     wait $am_sleep_pid 2>/dev/null
+   fi
+   AC_MSG_RESULT([done])])
+rm -f conftest.file
+])
 
-# Copyright (C) 2001, 2003, 2005, 2011 Free Software Foundation, Inc.
+# Copyright (C) 2009-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
 
-# serial 1
+# AM_SILENT_RULES([DEFAULT])
+# --------------------------
+# Enable less verbose build rules; with the default set to DEFAULT
+# ("yes" being less verbose, "no" or empty being verbose).
+AC_DEFUN([AM_SILENT_RULES],
+[AC_ARG_ENABLE([silent-rules], [dnl
+AS_HELP_STRING(
+  [--enable-silent-rules],
+  [less verbose build output (undo: "make V=1")])
+AS_HELP_STRING(
+  [--disable-silent-rules],
+  [verbose build output (undo: "make V=0")])dnl
+])
+case $enable_silent_rules in @%:@ (((
+  yes) AM_DEFAULT_VERBOSITY=0;;
+   no) AM_DEFAULT_VERBOSITY=1;;
+    *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);;
+esac
+dnl
+dnl A few 'make' implementations (e.g., NonStop OS and NextStep)
+dnl do not support nested variable expansions.
+dnl See automake bug#9928 and bug#10237.
+am_make=${MAKE-make}
+AC_CACHE_CHECK([whether $am_make supports nested variables],
+   [am_cv_make_support_nested_variables],
+   [if AS_ECHO([['TRUE=$(BAR$(V))
+BAR0=false
+BAR1=true
+V=1
+am__doit:
+	@$(TRUE)
+.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then
+  am_cv_make_support_nested_variables=yes
+else
+  am_cv_make_support_nested_variables=no
+fi])
+if test $am_cv_make_support_nested_variables = yes; then
+  dnl Using '$V' instead of '$(V)' breaks IRIX make.
+  AM_V='$(V)'
+  AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
+else
+  AM_V=$AM_DEFAULT_VERBOSITY
+  AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
+fi
+AC_SUBST([AM_V])dnl
+AM_SUBST_NOTMAKE([AM_V])dnl
+AC_SUBST([AM_DEFAULT_V])dnl
+AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl
+AC_SUBST([AM_DEFAULT_VERBOSITY])dnl
+AM_BACKSLASH='\'
+AC_SUBST([AM_BACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
+])
+
+# Copyright (C) 2001-2013 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
 
 # AM_PROG_INSTALL_STRIP
 # ---------------------
-# One issue with vendor `install' (even GNU) is that you can't
+# One issue with vendor 'install' (even GNU) is that you can't
 # specify the program used to strip binaries.  This is especially
 # annoying in cross-compiling environments, where the build's strip
 # is unlikely to handle the host's binaries.
 # Fortunately install-sh will honor a STRIPPROG variable, so we
-# always use install-sh in `make install-strip', and initialize
+# always use install-sh in "make install-strip", and initialize
 # STRIPPROG with the value of the STRIP variable (set by the user).
 AC_DEFUN([AM_PROG_INSTALL_STRIP],
 [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
-# Installed binaries are usually stripped using `strip' when the user
-# run `make install-strip'.  However `strip' might not be the right
+# Installed binaries are usually stripped using 'strip' when the user
+# run "make install-strip".  However 'strip' might not be the right
 # tool to use in cross-compilation environments, therefore Automake
-# will honor the `STRIP' environment variable to overrule this program.
-dnl Don't test for $cross_compiling = yes, because it might be `maybe'.
+# will honor the 'STRIP' environment variable to overrule this program.
+dnl Don't test for $cross_compiling = yes, because it might be 'maybe'.
 if test "$cross_compiling" != no; then
   AC_CHECK_TOOL([STRIP], [strip], :)
 fi
 INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
 AC_SUBST([INSTALL_STRIP_PROGRAM])])
 
-# Copyright (C) 2006, 2008, 2010 Free Software Foundation, Inc.
+# Copyright (C) 2006-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
 
-# serial 3
-
 # _AM_SUBST_NOTMAKE(VARIABLE)
 # ---------------------------
 # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in.
@@ -1074,18 +1268,16 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
 
 # Check how to create a tarball.                            -*- Autoconf -*-
 
-# Copyright (C) 2004, 2005, 2012 Free Software Foundation, Inc.
+# Copyright (C) 2004-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
 
-# serial 2
-
 # _AM_PROG_TAR(FORMAT)
 # --------------------
 # Check how to create a tarball in format FORMAT.
-# FORMAT should be one of `v7', `ustar', or `pax'.
+# FORMAT should be one of 'v7', 'ustar', or 'pax'.
 #
 # Substitute a variable $(am__tar) that is a command
 # writing to stdout a FORMAT-tarball containing the directory
@@ -1095,76 +1287,114 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
 # Substitute a variable $(am__untar) that extract such
 # a tarball read from stdin.
 #     $(am__untar) < result.tar
+#
 AC_DEFUN([_AM_PROG_TAR],
 [# Always define AMTAR for backward compatibility.  Yes, it's still used
 # in the wild :-(  We should find a proper way to deprecate it ...
 AC_SUBST([AMTAR], ['$${TAR-tar}'])
-m4_if([$1], [v7],
-     [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'],
-     [m4_case([$1], [ustar],, [pax],,
-              [m4_fatal([Unknown tar format])])
-AC_MSG_CHECKING([how to create a $1 tar archive])
-# Loop over all known methods to create a tar archive until one works.
+
+# We'll loop over all known methods to create a tar archive until one works.
 _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
-_am_tools=${am_cv_prog_tar_$1-$_am_tools}
-# Do not fold the above two line into one, because Tru64 sh and
-# Solaris sh will not grok spaces in the rhs of `-'.
-for _am_tool in $_am_tools
-do
-  case $_am_tool in
-  gnutar)
-    for _am_tar in tar gnutar gtar;
-    do
-      AM_RUN_LOG([$_am_tar --version]) && break
-    done
-    am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
-    am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
-    am__untar="$_am_tar -xf -"
-    ;;
-  plaintar)
-    # Must skip GNU tar: if it does not support --format= it doesn't create
-    # ustar tarball either.
-    (tar --version) >/dev/null 2>&1 && continue
-    am__tar='tar chf - "$$tardir"'
-    am__tar_='tar chf - "$tardir"'
-    am__untar='tar xf -'
-    ;;
-  pax)
-    am__tar='pax -L -x $1 -w "$$tardir"'
-    am__tar_='pax -L -x $1 -w "$tardir"'
-    am__untar='pax -r'
-    ;;
-  cpio)
-    am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
-    am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
-    am__untar='cpio -i -H $1 -d'
-    ;;
-  none)
-    am__tar=false
-    am__tar_=false
-    am__untar=false
-    ;;
-  esac
 
-  # If the value was cached, stop now.  We just wanted to have am__tar
-  # and am__untar set.
-  test -n "${am_cv_prog_tar_$1}" && break
+m4_if([$1], [v7],
+  [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'],
+
+  [m4_case([$1],
+    [ustar],
+     [# The POSIX 1988 'ustar' format is defined with fixed-size fields.
+      # There is notably a 21 bits limit for the UID and the GID.  In fact,
+      # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343
+      # and bug#13588).
+      am_max_uid=2097151 # 2^21 - 1
+      am_max_gid=$am_max_uid
+      # The $UID and $GID variables are not portable, so we need to resort
+      # to the POSIX-mandated id(1) utility.  Errors in the 'id' calls
+      # below are definitely unexpected, so allow the users to see them
+      # (that is, avoid stderr redirection).
+      am_uid=`id -u || echo unknown`
+      am_gid=`id -g || echo unknown`
+      AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format])
+      if test $am_uid -le $am_max_uid; then
+         AC_MSG_RESULT([yes])
+      else
+         AC_MSG_RESULT([no])
+         _am_tools=none
+      fi
+      AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format])
+      if test $am_gid -le $am_max_gid; then
+         AC_MSG_RESULT([yes])
+      else
+        AC_MSG_RESULT([no])
+        _am_tools=none
+      fi],
+
+  [pax],
+    [],
+
+  [m4_fatal([Unknown tar format])])
+
+  AC_MSG_CHECKING([how to create a $1 tar archive])
+
+  # Go ahead even if we have the value already cached.  We do so because we
+  # need to set the values for the 'am__tar' and 'am__untar' variables.
+  _am_tools=${am_cv_prog_tar_$1-$_am_tools}
+
+  for _am_tool in $_am_tools; do
+    case $_am_tool in
+    gnutar)
+      for _am_tar in tar gnutar gtar; do
+        AM_RUN_LOG([$_am_tar --version]) && break
+      done
+      am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
+      am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
+      am__untar="$_am_tar -xf -"
+      ;;
+    plaintar)
+      # Must skip GNU tar: if it does not support --format= it doesn't create
+      # ustar tarball either.
+      (tar --version) >/dev/null 2>&1 && continue
+      am__tar='tar chf - "$$tardir"'
+      am__tar_='tar chf - "$tardir"'
+      am__untar='tar xf -'
+      ;;
+    pax)
+      am__tar='pax -L -x $1 -w "$$tardir"'
+      am__tar_='pax -L -x $1 -w "$tardir"'
+      am__untar='pax -r'
+      ;;
+    cpio)
+      am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
+      am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
+      am__untar='cpio -i -H $1 -d'
+      ;;
+    none)
+      am__tar=false
+      am__tar_=false
+      am__untar=false
+      ;;
+    esac
 
-  # tar/untar a dummy directory, and stop if the command works
-  rm -rf conftest.dir
-  mkdir conftest.dir
-  echo GrepMe > conftest.dir/file
-  AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
+    # If the value was cached, stop now.  We just wanted to have am__tar
+    # and am__untar set.
+    test -n "${am_cv_prog_tar_$1}" && break
+
+    # tar/untar a dummy directory, and stop if the command works.
+    rm -rf conftest.dir
+    mkdir conftest.dir
+    echo GrepMe > conftest.dir/file
+    AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
+    rm -rf conftest.dir
+    if test -s conftest.tar; then
+      AM_RUN_LOG([$am__untar <conftest.tar])
+      AM_RUN_LOG([cat conftest.dir/file])
+      grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
+    fi
+  done
   rm -rf conftest.dir
-  if test -s conftest.tar; then
-    AM_RUN_LOG([$am__untar <conftest.tar])
-    grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
-  fi
-done
-rm -rf conftest.dir
 
-AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
-AC_MSG_RESULT([$am_cv_prog_tar_$1])])
+  AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
+  AC_MSG_RESULT([$am_cv_prog_tar_$1])])
+
 AC_SUBST([am__tar])
 AC_SUBST([am__untar])
 ]) # _AM_PROG_TAR
diff --git a/bin/guacctl b/bin/guacctl
new file mode 100755
index 0000000..4c0508f
--- /dev/null
+++ b/bin/guacctl
@@ -0,0 +1,136 @@
+#!/bin/sh
+#
+# Copyright (C) 2013 Glyptodon LLC
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+#
+# guacctl
+# -------
+#
+# Utility for sending Guacamole-specific console codes for controlling the SSH
+# session, such as:
+#
+#     * Downloading files
+#     * Setting the destination directory for uploads
+#
+# This script may also be run as "guacget", in which case the script accepts
+# no options and assumes anything given on the commandline is a file to be
+# downloaded.
+#
+
+
+# Given the name of a file, which may be a relative path, produce the full,
+# real, non-relative path for that same file.
+fullpath() {
+    FILENAME="$1"
+    DIR=`dirname "$FILENAME"`
+    FILE=`basename "$FILENAME"`
+    (cd "$DIR" && echo "$PWD/$FILE")
+}
+
+# Sends the Guacamole-specific console code for initiating a download.
+send_download_file() {
+    FILENAME="$1"
+    printf "\033]482200;%s\007" "$FILENAME"
+}
+
+# Sends the Guacamole-specific console code for setting the upload directory.
+send_set_directory() {
+    FILENAME="$1"
+    printf "\033]482201;%s\007" "$FILENAME"
+}
+
+# Prints the given error text to STDERR.
+error() {
+    echo "$NAME:" "$@" >&2
+}
+
+# Prints usage documentation for this script.
+usage() {
+    cat >&2 <<END
+guacctl 0.8.0, Guacamole SSH session control utility.
+Usage: guacctl [OPTION] [FILE]...
+
+    -d, --download         download each of the files listed.
+    -s, --set-directory    set the destination directory for future uploaded 
+                           files.
+END
+}
+
+# Initiates a download for each of the specified files
+download_files() {
+
+    # Validate arguments
+    if [ $# -lt 1 ]; then
+        error "No files specified."
+        return;
+    fi
+
+    for FILENAME in "$@"; do
+        if [ -e "$FILENAME" ]; then
+            send_download_file "`fullpath "$FILENAME"`"
+        else
+            error "$FILENAME: File does not exist."
+        fi
+    done
+
+}
+
+# Changes the upload path for future uploads to the given directory
+set_directory() {
+
+    # Validate arguments
+    if [ $# -lt 1 ]; then
+        error "No destination directory specified."
+        return;
+    fi
+
+    if [ $# -gt 1 ]; then
+        error "Only one destination directory may be given."
+        return;
+    fi
+
+    FILENAME="$1"
+    if [ -d "$FILENAME" ]; then
+        send_set_directory "`fullpath "$FILENAME"`"
+    else
+        error "$FILENAME: File does not exist or is not a directory."
+    fi
+
+}
+
+# Get script name
+NAME=`basename "$0"`
+
+# Parse options
+if [ "x$NAME" = "xguacget" ]; then
+    download_files "$@"
+elif [ "x$1" = "x--download" -o "x$1" = "x-d" ]; then
+    shift
+    download_files "$@"
+elif [ "x$1" = "x--set-directory" -o "x$1" = "x-s" ]; then
+    shift
+    set_directory "$@"
+else
+    usage
+    exit 1
+fi
+
diff --git a/compile b/compile
new file mode 100755
index 0000000..531136b
--- /dev/null
+++ b/compile
@@ -0,0 +1,347 @@
+#! /bin/sh
+# Wrapper for compilers which do not understand '-c -o'.
+
+scriptversion=2012-10-14.11; # UTC
+
+# Copyright (C) 1999-2013 Free Software Foundation, Inc.
+# Written by Tom Tromey <tromey at cygnus.com>.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# This file is maintained in Automake, please report
+# bugs to <bug-automake at gnu.org> or send patches to
+# <automake-patches at gnu.org>.
+
+nl='
+'
+
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent tools from complaining about whitespace usage.
+IFS=" ""	$nl"
+
+file_conv=
+
+# func_file_conv build_file lazy
+# Convert a $build file to $host form and store it in $file
+# Currently only supports Windows hosts. If the determined conversion
+# type is listed in (the comma separated) LAZY, no conversion will
+# take place.
+func_file_conv ()
+{
+  file=$1
+  case $file in
+    / | /[!/]*) # absolute file, and not a UNC file
+      if test -z "$file_conv"; then
+	# lazily determine how to convert abs files
+	case `uname -s` in
+	  MINGW*)
+	    file_conv=mingw
+	    ;;
+	  CYGWIN*)
+	    file_conv=cygwin
+	    ;;
+	  *)
+	    file_conv=wine
+	    ;;
+	esac
+      fi
+      case $file_conv/,$2, in
+	*,$file_conv,*)
+	  ;;
+	mingw/*)
+	  file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
+	  ;;
+	cygwin/*)
+	  file=`cygpath -m "$file" || echo "$file"`
+	  ;;
+	wine/*)
+	  file=`winepath -w "$file" || echo "$file"`
+	  ;;
+      esac
+      ;;
+  esac
+}
+
+# func_cl_dashL linkdir
+# Make cl look for libraries in LINKDIR
+func_cl_dashL ()
+{
+  func_file_conv "$1"
+  if test -z "$lib_path"; then
+    lib_path=$file
+  else
+    lib_path="$lib_path;$file"
+  fi
+  linker_opts="$linker_opts -LIBPATH:$file"
+}
+
+# func_cl_dashl library
+# Do a library search-path lookup for cl
+func_cl_dashl ()
+{
+  lib=$1
+  found=no
+  save_IFS=$IFS
+  IFS=';'
+  for dir in $lib_path $LIB
+  do
+    IFS=$save_IFS
+    if $shared && test -f "$dir/$lib.dll.lib"; then
+      found=yes
+      lib=$dir/$lib.dll.lib
+      break
+    fi
+    if test -f "$dir/$lib.lib"; then
+      found=yes
+      lib=$dir/$lib.lib
+      break
+    fi
+    if test -f "$dir/lib$lib.a"; then
+      found=yes
+      lib=$dir/lib$lib.a
+      break
+    fi
+  done
+  IFS=$save_IFS
+
+  if test "$found" != yes; then
+    lib=$lib.lib
+  fi
+}
+
+# func_cl_wrapper cl arg...
+# Adjust compile command to suit cl
+func_cl_wrapper ()
+{
+  # Assume a capable shell
+  lib_path=
+  shared=:
+  linker_opts=
+  for arg
+  do
+    if test -n "$eat"; then
+      eat=
+    else
+      case $1 in
+	-o)
+	  # configure might choose to run compile as 'compile cc -o foo foo.c'.
+	  eat=1
+	  case $2 in
+	    *.o | *.[oO][bB][jJ])
+	      func_file_conv "$2"
+	      set x "$@" -Fo"$file"
+	      shift
+	      ;;
+	    *)
+	      func_file_conv "$2"
+	      set x "$@" -Fe"$file"
+	      shift
+	      ;;
+	  esac
+	  ;;
+	-I)
+	  eat=1
+	  func_file_conv "$2" mingw
+	  set x "$@" -I"$file"
+	  shift
+	  ;;
+	-I*)
+	  func_file_conv "${1#-I}" mingw
+	  set x "$@" -I"$file"
+	  shift
+	  ;;
+	-l)
+	  eat=1
+	  func_cl_dashl "$2"
+	  set x "$@" "$lib"
+	  shift
+	  ;;
+	-l*)
+	  func_cl_dashl "${1#-l}"
+	  set x "$@" "$lib"
+	  shift
+	  ;;
+	-L)
+	  eat=1
+	  func_cl_dashL "$2"
+	  ;;
+	-L*)
+	  func_cl_dashL "${1#-L}"
+	  ;;
+	-static)
+	  shared=false
+	  ;;
+	-Wl,*)
+	  arg=${1#-Wl,}
+	  save_ifs="$IFS"; IFS=','
+	  for flag in $arg; do
+	    IFS="$save_ifs"
+	    linker_opts="$linker_opts $flag"
+	  done
+	  IFS="$save_ifs"
+	  ;;
+	-Xlinker)
+	  eat=1
+	  linker_opts="$linker_opts $2"
+	  ;;
+	-*)
+	  set x "$@" "$1"
+	  shift
+	  ;;
+	*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
+	  func_file_conv "$1"
+	  set x "$@" -Tp"$file"
+	  shift
+	  ;;
+	*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
+	  func_file_conv "$1" mingw
+	  set x "$@" "$file"
+	  shift
+	  ;;
+	*)
+	  set x "$@" "$1"
+	  shift
+	  ;;
+      esac
+    fi
+    shift
+  done
+  if test -n "$linker_opts"; then
+    linker_opts="-link$linker_opts"
+  fi
+  exec "$@" $linker_opts
+  exit 1
+}
+
+eat=
+
+case $1 in
+  '')
+     echo "$0: No command.  Try '$0 --help' for more information." 1>&2
+     exit 1;
+     ;;
+  -h | --h*)
+    cat <<\EOF
+Usage: compile [--help] [--version] PROGRAM [ARGS]
+
+Wrapper for compilers which do not understand '-c -o'.
+Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
+arguments, and rename the output as expected.
+
+If you are trying to build a whole package this is not the
+right script to run: please start by reading the file 'INSTALL'.
+
+Report bugs to <bug-automake at gnu.org>.
+EOF
+    exit $?
+    ;;
+  -v | --v*)
+    echo "compile $scriptversion"
+    exit $?
+    ;;
+  cl | *[/\\]cl | cl.exe | *[/\\]cl.exe )
+    func_cl_wrapper "$@"      # Doesn't return...
+    ;;
+esac
+
+ofile=
+cfile=
+
+for arg
+do
+  if test -n "$eat"; then
+    eat=
+  else
+    case $1 in
+      -o)
+	# configure might choose to run compile as 'compile cc -o foo foo.c'.
+	# So we strip '-o arg' only if arg is an object.
+	eat=1
+	case $2 in
+	  *.o | *.obj)
+	    ofile=$2
+	    ;;
+	  *)
+	    set x "$@" -o "$2"
+	    shift
+	    ;;
+	esac
+	;;
+      *.c)
+	cfile=$1
+	set x "$@" "$1"
+	shift
+	;;
+      *)
+	set x "$@" "$1"
+	shift
+	;;
+    esac
+  fi
+  shift
+done
+
+if test -z "$ofile" || test -z "$cfile"; then
+  # If no '-o' option was seen then we might have been invoked from a
+  # pattern rule where we don't need one.  That is ok -- this is a
+  # normal compilation that the losing compiler can handle.  If no
+  # '.c' file was seen then we are probably linking.  That is also
+  # ok.
+  exec "$@"
+fi
+
+# Name of file we expect compiler to create.
+cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
+
+# Create the lock directory.
+# Note: use '[/\\:.-]' here to ensure that we don't use the same name
+# that we are using for the .o file.  Also, base the name on the expected
+# object file name, since that is what matters with a parallel build.
+lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
+while true; do
+  if mkdir "$lockdir" >/dev/null 2>&1; then
+    break
+  fi
+  sleep 1
+done
+# FIXME: race condition here if user kills between mkdir and trap.
+trap "rmdir '$lockdir'; exit 1" 1 2 15
+
+# Run the compile.
+"$@"
+ret=$?
+
+if test -f "$cofile"; then
+  test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
+elif test -f "${cofile}bj"; then
+  test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
+fi
+
+rmdir "$lockdir"
+exit $ret
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/config.guess b/config.guess
index d622a44..1f5c50c 100755
--- a/config.guess
+++ b/config.guess
@@ -1,14 +1,12 @@
 #! /bin/sh
 # Attempt to guess a canonical system name.
-#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-#   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
-#   2011, 2012 Free Software Foundation, Inc.
+#   Copyright 1992-2014 Free Software Foundation, Inc.
 
-timestamp='2012-02-10'
+timestamp='2014-03-23'
 
 # This file is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
+# the Free Software Foundation; either version 3 of the License, or
 # (at your option) any later version.
 #
 # This program is distributed in the hope that it will be useful, but
@@ -22,19 +20,17 @@ timestamp='2012-02-10'
 # As a special exception to the GNU General Public License, if you
 # distribute this file as part of a program that contains a
 # configuration script generated by Autoconf, you may include it under
-# the same distribution terms that you use for the rest of that program.
-
-
-# Originally written by Per Bothner.  Please send patches (context
-# diff format) to <config-patches at gnu.org> and include a ChangeLog
-# entry.
+# the same distribution terms that you use for the rest of that
+# program.  This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
 #
-# This script attempts to guess a canonical system name similar to
-# config.sub.  If it succeeds, it prints the system name on stdout, and
-# exits with 0.  Otherwise, it exits with 1.
+# Originally written by Per Bothner.
 #
 # You can get the latest version of this script from:
 # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+#
+# Please send patches with a ChangeLog entry to config-patches at gnu.org.
+
 
 me=`echo "$0" | sed -e 's,.*/,,'`
 
@@ -54,9 +50,7 @@ version="\
 GNU config.guess ($timestamp)
 
 Originally written by Per Bothner.
-Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
-Free Software Foundation, Inc.
+Copyright 1992-2014 Free Software Foundation, Inc.
 
 This is free software; see the source for copying conditions.  There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -138,6 +132,27 @@ UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
 UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
 UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
 
+case "${UNAME_SYSTEM}" in
+Linux|GNU|GNU/*)
+	# If the system lacks a compiler, then just pick glibc.
+	# We could probably try harder.
+	LIBC=gnu
+
+	eval $set_cc_for_build
+	cat <<-EOF > $dummy.c
+	#include <features.h>
+	#if defined(__UCLIBC__)
+	LIBC=uclibc
+	#elif defined(__dietlibc__)
+	LIBC=dietlibc
+	#else
+	LIBC=gnu
+	#endif
+	EOF
+	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`
+	;;
+esac
+
 # Note: order is significant - the case branches are not exclusive.
 
 case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
@@ -200,6 +215,10 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
 	# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
 	echo "${machine}-${os}${release}"
 	exit ;;
+    *:Bitrig:*:*)
+	UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
+	echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE}
+	exit ;;
     *:OpenBSD:*:*)
 	UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
 	echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
@@ -302,7 +321,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
     arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
 	echo arm-acorn-riscix${UNAME_RELEASE}
 	exit ;;
-    arm:riscos:*:*|arm:RISCOS:*:*)
+    arm*:riscos:*:*|arm*:RISCOS:*:*)
 	echo arm-unknown-riscos
 	exit ;;
     SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
@@ -801,10 +820,13 @@ EOF
     i*:CYGWIN*:*)
 	echo ${UNAME_MACHINE}-pc-cygwin
 	exit ;;
+    *:MINGW64*:*)
+	echo ${UNAME_MACHINE}-pc-mingw64
+	exit ;;
     *:MINGW*:*)
 	echo ${UNAME_MACHINE}-pc-mingw32
 	exit ;;
-    i*:MSYS*:*)
+    *:MSYS*:*)
 	echo ${UNAME_MACHINE}-pc-msys
 	exit ;;
     i*:windows32*:*)
@@ -852,21 +874,21 @@ EOF
 	exit ;;
     *:GNU:*:*)
 	# the GNU system
-	echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+	echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
 	exit ;;
     *:GNU/*:*:*)
 	# other systems with GNU libc and userland
-	echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+	echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC}
 	exit ;;
     i*86:Minix:*:*)
 	echo ${UNAME_MACHINE}-pc-minix
 	exit ;;
     aarch64:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     aarch64_be:Linux:*:*)
 	UNAME_MACHINE=aarch64_be
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     alpha:Linux:*:*)
 	case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
@@ -879,59 +901,54 @@ EOF
 	  EV68*) UNAME_MACHINE=alphaev68 ;;
 	esac
 	objdump --private-headers /bin/sh | grep -q ld.so.1
-	if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
-	echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+	if test "$?" = 0 ; then LIBC="gnulibc1" ; fi
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	exit ;;
+    arc:Linux:*:* | arceb:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     arm*:Linux:*:*)
 	eval $set_cc_for_build
 	if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
 	    | grep -q __ARM_EABI__
 	then
-	    echo ${UNAME_MACHINE}-unknown-linux-gnu
+	    echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	else
 	    if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
 		| grep -q __ARM_PCS_VFP
 	    then
-		echo ${UNAME_MACHINE}-unknown-linux-gnueabi
+		echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi
 	    else
-		echo ${UNAME_MACHINE}-unknown-linux-gnueabihf
+		echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf
 	    fi
 	fi
 	exit ;;
     avr32*:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     cris:Linux:*:*)
-	echo ${UNAME_MACHINE}-axis-linux-gnu
+	echo ${UNAME_MACHINE}-axis-linux-${LIBC}
 	exit ;;
     crisv32:Linux:*:*)
-	echo ${UNAME_MACHINE}-axis-linux-gnu
+	echo ${UNAME_MACHINE}-axis-linux-${LIBC}
 	exit ;;
     frv:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     hexagon:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     i*86:Linux:*:*)
-	LIBC=gnu
-	eval $set_cc_for_build
-	sed 's/^	//' << EOF >$dummy.c
-	#ifdef __dietlibc__
-	LIBC=dietlibc
-	#endif
-EOF
-	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
-	echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
+	echo ${UNAME_MACHINE}-pc-linux-${LIBC}
 	exit ;;
     ia64:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     m32r*:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     m68*:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     mips:Linux:*:* | mips64:Linux:*:*)
 	eval $set_cc_for_build
@@ -950,54 +967,63 @@ EOF
 	#endif
 EOF
 	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
-	test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+	test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; }
 	;;
-    or32:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+    openrisc*:Linux:*:*)
+	echo or1k-unknown-linux-${LIBC}
+	exit ;;
+    or32:Linux:*:* | or1k*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     padre:Linux:*:*)
-	echo sparc-unknown-linux-gnu
+	echo sparc-unknown-linux-${LIBC}
 	exit ;;
     parisc64:Linux:*:* | hppa64:Linux:*:*)
-	echo hppa64-unknown-linux-gnu
+	echo hppa64-unknown-linux-${LIBC}
 	exit ;;
     parisc:Linux:*:* | hppa:Linux:*:*)
 	# Look for CPU level
 	case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
-	  PA7*) echo hppa1.1-unknown-linux-gnu ;;
-	  PA8*) echo hppa2.0-unknown-linux-gnu ;;
-	  *)    echo hppa-unknown-linux-gnu ;;
+	  PA7*) echo hppa1.1-unknown-linux-${LIBC} ;;
+	  PA8*) echo hppa2.0-unknown-linux-${LIBC} ;;
+	  *)    echo hppa-unknown-linux-${LIBC} ;;
 	esac
 	exit ;;
     ppc64:Linux:*:*)
-	echo powerpc64-unknown-linux-gnu
+	echo powerpc64-unknown-linux-${LIBC}
 	exit ;;
     ppc:Linux:*:*)
-	echo powerpc-unknown-linux-gnu
+	echo powerpc-unknown-linux-${LIBC}
+	exit ;;
+    ppc64le:Linux:*:*)
+	echo powerpc64le-unknown-linux-${LIBC}
+	exit ;;
+    ppcle:Linux:*:*)
+	echo powerpcle-unknown-linux-${LIBC}
 	exit ;;
     s390:Linux:*:* | s390x:Linux:*:*)
-	echo ${UNAME_MACHINE}-ibm-linux
+	echo ${UNAME_MACHINE}-ibm-linux-${LIBC}
 	exit ;;
     sh64*:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     sh*:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     sparc:Linux:*:* | sparc64:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     tile*:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     vax:Linux:*:*)
-	echo ${UNAME_MACHINE}-dec-linux-gnu
+	echo ${UNAME_MACHINE}-dec-linux-${LIBC}
 	exit ;;
     x86_64:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     xtensa*:Linux:*:*)
-	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
 	exit ;;
     i*86:DYNIX/ptx:4*:*)
 	# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
@@ -1201,6 +1227,9 @@ EOF
     BePC:Haiku:*:*)	# Haiku running on Intel PC compatible.
 	echo i586-pc-haiku
 	exit ;;
+    x86_64:Haiku:*:*)
+	echo x86_64-unknown-haiku
+	exit ;;
     SX-4:SUPER-UX:*:*)
 	echo sx4-nec-superux${UNAME_RELEASE}
 	exit ;;
@@ -1227,19 +1256,31 @@ EOF
 	exit ;;
     *:Darwin:*:*)
 	UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
-	case $UNAME_PROCESSOR in
-	    i386)
-		eval $set_cc_for_build
-		if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
-		  if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
-		      (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
-		      grep IS_64BIT_ARCH >/dev/null
-		  then
-		      UNAME_PROCESSOR="x86_64"
-		  fi
-		fi ;;
-	    unknown) UNAME_PROCESSOR=powerpc ;;
-	esac
+	eval $set_cc_for_build
+	if test "$UNAME_PROCESSOR" = unknown ; then
+	    UNAME_PROCESSOR=powerpc
+	fi
+	if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then
+	    if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+		if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+		    (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+		    grep IS_64BIT_ARCH >/dev/null
+		then
+		    case $UNAME_PROCESSOR in
+			i386) UNAME_PROCESSOR=x86_64 ;;
+			powerpc) UNAME_PROCESSOR=powerpc64 ;;
+		    esac
+		fi
+	    fi
+	elif test "$UNAME_PROCESSOR" = i386 ; then
+	    # Avoid executing cc on OS X 10.9, as it ships with a stub
+	    # that puts up a graphical alert prompting to install
+	    # developer tools.  Any system running Mac OS X 10.7 or
+	    # later (Darwin 11 and later) is required to have a 64-bit
+	    # processor. This is not true of the ARM version of Darwin
+	    # that Apple uses in portable devices.
+	    UNAME_PROCESSOR=x86_64
+	fi
 	echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
 	exit ;;
     *:procnto*:*:* | *:QNX:[0123456789]*:*)
@@ -1256,7 +1297,7 @@ EOF
     NEO-?:NONSTOP_KERNEL:*:*)
 	echo neo-tandem-nsk${UNAME_RELEASE}
 	exit ;;
-    NSE-?:NONSTOP_KERNEL:*:*)
+    NSE-*:NONSTOP_KERNEL:*:*)
 	echo nse-tandem-nsk${UNAME_RELEASE}
 	exit ;;
     NSR-?:NONSTOP_KERNEL:*:*)
@@ -1330,157 +1371,6 @@ EOF
 	exit ;;
 esac
 
-#echo '(No uname command or uname output not recognized.)' 1>&2
-#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
-
-eval $set_cc_for_build
-cat >$dummy.c <<EOF
-#ifdef _SEQUENT_
-# include <sys/types.h>
-# include <sys/utsname.h>
-#endif
-main ()
-{
-#if defined (sony)
-#if defined (MIPSEB)
-  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
-     I don't know....  */
-  printf ("mips-sony-bsd\n"); exit (0);
-#else
-#include <sys/param.h>
-  printf ("m68k-sony-newsos%s\n",
-#ifdef NEWSOS4
-	"4"
-#else
-	""
-#endif
-	); exit (0);
-#endif
-#endif
-
-#if defined (__arm) && defined (__acorn) && defined (__unix)
-  printf ("arm-acorn-riscix\n"); exit (0);
-#endif
-
-#if defined (hp300) && !defined (hpux)
-  printf ("m68k-hp-bsd\n"); exit (0);
-#endif
-
-#if defined (NeXT)
-#if !defined (__ARCHITECTURE__)
-#define __ARCHITECTURE__ "m68k"
-#endif
-  int version;
-  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
-  if (version < 4)
-    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
-  else
-    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
-  exit (0);
-#endif
-
-#if defined (MULTIMAX) || defined (n16)
-#if defined (UMAXV)
-  printf ("ns32k-encore-sysv\n"); exit (0);
-#else
-#if defined (CMU)
-  printf ("ns32k-encore-mach\n"); exit (0);
-#else
-  printf ("ns32k-encore-bsd\n"); exit (0);
-#endif
-#endif
-#endif
-
-#if defined (__386BSD__)
-  printf ("i386-pc-bsd\n"); exit (0);
-#endif
-
-#if defined (sequent)
-#if defined (i386)
-  printf ("i386-sequent-dynix\n"); exit (0);
-#endif
-#if defined (ns32000)
-  printf ("ns32k-sequent-dynix\n"); exit (0);
-#endif
-#endif
-
-#if defined (_SEQUENT_)
-    struct utsname un;
-
-    uname(&un);
-
-    if (strncmp(un.version, "V2", 2) == 0) {
-	printf ("i386-sequent-ptx2\n"); exit (0);
-    }
-    if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
-	printf ("i386-sequent-ptx1\n"); exit (0);
-    }
-    printf ("i386-sequent-ptx\n"); exit (0);
-
-#endif
-
-#if defined (vax)
-# if !defined (ultrix)
-#  include <sys/param.h>
-#  if defined (BSD)
-#   if BSD == 43
-      printf ("vax-dec-bsd4.3\n"); exit (0);
-#   else
-#    if BSD == 199006
-      printf ("vax-dec-bsd4.3reno\n"); exit (0);
-#    else
-      printf ("vax-dec-bsd\n"); exit (0);
-#    endif
-#   endif
-#  else
-    printf ("vax-dec-bsd\n"); exit (0);
-#  endif
-# else
-    printf ("vax-dec-ultrix\n"); exit (0);
-# endif
-#endif
-
-#if defined (alliant) && defined (i860)
-  printf ("i860-alliant-bsd\n"); exit (0);
-#endif
-
-  exit (1);
-}
-EOF
-
-$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
-	{ echo "$SYSTEM_NAME"; exit; }
-
-# Apollos put the system type in the environment.
-
-test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
-
-# Convex versions that predate uname can use getsysinfo(1)
-
-if [ -x /usr/convex/getsysinfo ]
-then
-    case `getsysinfo -f cpu_type` in
-    c1*)
-	echo c1-convex-bsd
-	exit ;;
-    c2*)
-	if getsysinfo -f scalar_acc
-	then echo c32-convex-bsd
-	else echo c2-convex-bsd
-	fi
-	exit ;;
-    c34*)
-	echo c34-convex-bsd
-	exit ;;
-    c38*)
-	echo c38-convex-bsd
-	exit ;;
-    c4*)
-	echo c4-convex-bsd
-	exit ;;
-    esac
-fi
-
 cat >&2 <<EOF
 $0: unable to guess system type
 
diff --git a/config.h.in b/config.h.in
new file mode 100644
index 0000000..c3d6066
--- /dev/null
+++ b/config.h.in
@@ -0,0 +1,298 @@
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Type compatibility */
+#undef CHANNEL_ENTRY_POINTS_FREERDP
+
+/* Whether support for the common SSH core is enabled */
+#undef ENABLE_COMMON_SSH
+
+/* Whether support for Ogg Vorbis is enabled */
+#undef ENABLE_OGG
+
+/* Whether PulseAudio support is enabled */
+#undef ENABLE_PULSE
+
+/* Whether agent forwarding support for SSH is enabled */
+#undef ENABLE_SSH_AGENT
+
+/* Whether SSL-related support is enabled */
+#undef ENABLE_SSL
+
+/* Whether support for listen-mode VNC connections is enabled. */
+#undef ENABLE_VNC_LISTEN
+
+/* Whether support for VNC repeaters is enabled. */
+#undef ENABLE_VNC_REPEATER
+
+/* Whether WebP support is enabled */
+#undef ENABLE_WEBP
+
+/* Whether library support for WinPR types was found */
+#undef ENABLE_WINPR
+
+/* Whether this version of FreeRDP requires _aligned_malloc() for bitmap data
+   */
+#undef FREERDP_BITMAP_REQUIRES_ALIGNED_MALLOC
+
+/* The full path to the guacd config file */
+#undef GUACD_CONF_FILE
+
+/* Define to 1 if you have the <cairo/cairo.h> header file. */
+#undef HAVE_CAIRO_CAIRO_H
+
+/* Whether cairo_format_stride_for_width() is defined */
+#undef HAVE_CAIRO_FORMAT_STRIDE_FOR_WIDTH
+
+/* Define to 1 if you have the `clock_gettime' function. */
+#undef HAVE_CLOCK_GETTIME
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
+/* Define to 1 if you have the <freerdp/addin.h> header file. */
+#undef HAVE_FREERDP_ADDIN_H
+
+/* Whether freerdp_channels_global_init() is defined */
+#undef HAVE_FREERDP_CHANNELS_GLOBAL_INIT
+
+/* Define to 1 if you have the <freerdp/client/channels.h> header file. */
+#undef HAVE_FREERDP_CLIENT_CHANNELS_H
+
+/* Define to 1 if you have the <freerdp/client/cliprdr.h> header file. */
+#undef HAVE_FREERDP_CLIENT_CLIPRDR_H
+
+/* Define to 1 if you have the <freerdp/client/disp.h> header file. */
+#undef HAVE_FREERDP_CLIENT_DISP_H
+
+/* Whether freerdp_color_convert_drawing_order_color_to_gdi_color() is defined
+   */
+#undef HAVE_FREERDP_COLOR_CONVERT_DRAWING_ORDER_COLOR_TO_GDI_COLOR
+
+/* Define to 1 if `ContextSize' is a member of `freerdp'. */
+#undef HAVE_FREERDP_CONTEXTSIZE
+
+/* Define to 1 if `context_size' is a member of `freerdp'. */
+#undef HAVE_FREERDP_CONTEXT_SIZE
+
+/* Whether freerdp_convert_gdi_order_color() is defined */
+#undef HAVE_FREERDP_CONVERT_GDI_ORDER_COLOR
+
+/* Whether FreeRDP supports the display update channel */
+#undef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
+
+/* Whether this version of FreeRDP provides the PubSub event system */
+#undef HAVE_FREERDP_EVENT_PUBSUB
+
+/* Define to 1 if you have the <freerdp/kbd/layouts.h> header file. */
+#undef HAVE_FREERDP_KBD_LAYOUTS_H
+
+/* Define to 1 if you have the <freerdp/locale/keyboard.h> header file. */
+#undef HAVE_FREERDP_LOCALE_KEYBOARD_H
+
+/* Define to 1 if you have the <freerdp/plugins/cliprdr.h> header file. */
+#undef HAVE_FREERDP_PLUGINS_CLIPRDR_H
+
+/* Whether freerdp_register_addin_provider() is defined */
+#undef HAVE_FREERDP_REGISTER_ADDIN_PROVIDER
+
+/* Define to 1 if you have the <freerdp/version.h> header file. */
+#undef HAVE_FREERDP_VERSION_H
+
+/* Define to 1 if you have the `gettimeofday' function. */
+#undef HAVE_GETTIMEOFDAY
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Whether libpthread was found */
+#undef HAVE_LIBPTHREAD
+
+/* Define to 1 if you have the `wsock32' library (-lwsock32). */
+#undef HAVE_LIBWSOCK32
+
+/* Define to 1 if you have the `memmove' function. */
+#undef HAVE_MEMMOVE
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the `memset' function. */
+#undef HAVE_MEMSET
+
+/* Define to 1 if you have the `nanosleep' function. */
+#undef HAVE_NANOSLEEP
+
+/* Define to 1 if you have the <ossp/uuid.h> header file. */
+#undef HAVE_OSSP_UUID_H
+
+/* Define to 1 if you have the <pngstruct.h> header file. */
+#undef HAVE_PNGSTRUCT_H
+
+/* Whether png_get_io_ptr() is defined */
+#undef HAVE_PNG_GET_IO_PTR
+
+/* Define to 1 if `codecs' is a member of `rdpContext'. */
+#undef HAVE_RDPCONTEXT_CODECS
+
+/* Define to 1 if `SetDefault' is a member of `rdpPointer'. */
+#undef HAVE_RDPPOINTER_SETDEFAULT
+
+/* Define to 1 if `SetNull' is a member of `rdpPointer'. */
+#undef HAVE_RDPPOINTER_SETNULL
+
+/* Whether the rdpSettings structure has AudioPlayback settings */
+#undef HAVE_RDPSETTINGS_AUDIOPLAYBACK
+
+/* Define to 1 if `audio_playback' is a member of `rdpSettings'. */
+#undef HAVE_RDPSETTINGS_AUDIO_PLAYBACK
+
+/* Whether the rdpSettings structure has DeviceRedirection settings */
+#undef HAVE_RDPSETTINGS_DEVICEREDIRECTION
+
+/* Define to 1 if `device_redirection' is a member of `rdpSettings'. */
+#undef HAVE_RDPSETTINGS_DEVICE_REDIRECTION
+
+/* Define to 1 if `FastPathInput' is a member of `rdpSettings'. */
+#undef HAVE_RDPSETTINGS_FASTPATHINPUT
+
+/* Define to 1 if `FastPathOutput' is a member of `rdpSettings'. */
+#undef HAVE_RDPSETTINGS_FASTPATHOUTPUT
+
+/* Define to 1 if `height' is a member of `rdpSettings'. */
+#undef HAVE_RDPSETTINGS_HEIGHT
+
+/* Define to 1 if `OrderSupport' is a member of `rdpSettings'. */
+#undef HAVE_RDPSETTINGS_ORDERSUPPORT
+
+/* Define to 1 if `order_support' is a member of `rdpSettings'. */
+#undef HAVE_RDPSETTINGS_ORDER_SUPPORT
+
+/* Define to 1 if `SendPreconnectionPdu' is a member of `rdpSettings'. */
+#undef HAVE_RDPSETTINGS_SENDPRECONNECTIONPDU
+
+/* Define to 1 if `SupportDisplayControl' is a member of `rdpSettings'. */
+#undef HAVE_RDPSETTINGS_SUPPORTDISPLAYCONTROL
+
+/* Define to 1 if `width' is a member of `rdpSettings'. */
+#undef HAVE_RDPSETTINGS_WIDTH
+
+/* Define to 1 if `interval_ms' is a member of `rdpSvcPlugin'. */
+#undef HAVE_RDPSVCPLUGIN_INTERVAL_MS
+
+/* Define to 1 if `event_class' is a member of `RDP_EVENT'. */
+#undef HAVE_RDP_EVENT_EVENT_CLASS
+
+/* Define to 1 if `destHost' is a member of `rfbClient'. */
+#undef HAVE_RFBCLIENT_DESTHOST
+
+/* Define to 1 if `destPort' is a member of `rfbClient'. */
+#undef HAVE_RFBCLIENT_DESTPORT
+
+/* Define to 1 if you have the `select' function. */
+#undef HAVE_SELECT
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the `strdup' function. */
+#undef HAVE_STRDUP
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <syslog.h> header file. */
+#undef HAVE_SYSLOG_H
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#undef HAVE_SYS_SOCKET_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <time.h> header file. */
+#undef HAVE_TIME_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 if `id' is a member of `wMessage'. */
+#undef HAVE_WMESSAGE_ID
+
+/* Whether interleaved_decompress() accepts an additional palette parameter */
+#undef INTERLEAVED_DECOMPRESS_TAKES_PALETTE
+
+/* Whether the legacy RDP_EVENT API was found */
+#undef LEGACY_EVENT
+
+/* Whether the older version of the FreeRDP API was found */
+#undef LEGACY_FREERDP
+
+/* Whether the legacy rdpBitmap API was found */
+#undef LEGACY_RDPBITMAP
+
+/* Whether the legacy rdpPalette API was found */
+#undef LEGACY_RDPPALETTE
+
+/* Whether the legacy version of the rdpSettings API was found */
+#undef LEGACY_RDPSETTINGS
+
+/* Whether libssh2 was built against libgcrypt */
+#undef LIBSSH2_USES_GCRYPT
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#undef LT_OBJDIR
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Whether planar_decompress() can flip */
+#undef PLANAR_DECOMPRESS_CAN_FLIP
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Version number of package */
+#undef VERSION
+
+/* Uses X/Open and POSIX APIs */
+#undef _XOPEN_SOURCE
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef size_t
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef ssize_t
diff --git a/config.sub b/config.sub
index 6205f84..66c5074 100755
--- a/config.sub
+++ b/config.sub
@@ -1,24 +1,18 @@
 #! /bin/sh
 # Configuration validation subroutine script.
-#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-#   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
-#   2011, 2012 Free Software Foundation, Inc.
+#   Copyright 1992-2014 Free Software Foundation, Inc.
 
-timestamp='2012-04-18'
+timestamp='2014-07-28'
 
-# This file is (in principle) common to ALL GNU software.
-# The presence of a machine in this file suggests that SOME GNU software
-# can handle that machine.  It does not imply ALL GNU software can.
-#
-# This file is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
 # (at your option) any later version.
 #
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, see <http://www.gnu.org/licenses/>.
@@ -26,11 +20,12 @@ timestamp='2012-04-18'
 # As a special exception to the GNU General Public License, if you
 # distribute this file as part of a program that contains a
 # configuration script generated by Autoconf, you may include it under
-# the same distribution terms that you use for the rest of that program.
+# the same distribution terms that you use for the rest of that
+# program.  This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
 
 
-# Please send patches to <config-patches at gnu.org>.  Submit a context
-# diff and a properly formatted GNU ChangeLog entry.
+# Please send patches with a ChangeLog entry to config-patches at gnu.org.
 #
 # Configuration subroutine to validate and canonicalize a configuration type.
 # Supply the specified configuration type as an argument.
@@ -73,9 +68,7 @@ Report bugs and patches to <config-patches at gnu.org>."
 version="\
 GNU config.sub ($timestamp)
 
-Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
-Free Software Foundation, Inc.
+Copyright 1992-2014 Free Software Foundation, Inc.
 
 This is free software; see the source for copying conditions.  There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -123,7 +116,7 @@ esac
 maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
 case $maybe_os in
   nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
-  linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
+  linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
   knetbsd*-gnu* | netbsd*-gnu* | \
   kopensolaris*-gnu* | \
   storm-chaos* | os2-emx* | rtmk-nova*)
@@ -156,7 +149,7 @@ case $os in
 	-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
 	-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
 	-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
-	-apple | -axis | -knuth | -cray | -microblaze)
+	-apple | -axis | -knuth | -cray | -microblaze*)
 		os=
 		basic_machine=$1
 		;;
@@ -259,10 +252,12 @@ case $basic_machine in
 	| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
 	| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
 	| am33_2.0 \
-	| arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \
-        | be32 | be64 \
+	| arc | arceb \
+	| arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
+	| avr | avr32 \
+	| be32 | be64 \
 	| bfin \
-	| c4x | clipper \
+	| c4x | c8051 | clipper \
 	| d10v | d30v | dlx | dsp16xx \
 	| epiphany \
 	| fido | fr30 | frv \
@@ -270,10 +265,11 @@ case $basic_machine in
 	| hexagon \
 	| i370 | i860 | i960 | ia64 \
 	| ip2k | iq2000 \
+	| k1om \
 	| le32 | le64 \
 	| lm32 \
 	| m32c | m32r | m32rle | m68000 | m68k | m88k \
-	| maxq | mb | microblaze | mcore | mep | metag \
+	| maxq | mb | microblaze | microblazeel | mcore | mep | metag \
 	| mips | mipsbe | mipseb | mipsel | mipsle \
 	| mips16 \
 	| mips64 | mips64el \
@@ -287,20 +283,22 @@ case $basic_machine in
 	| mips64vr5900 | mips64vr5900el \
 	| mipsisa32 | mipsisa32el \
 	| mipsisa32r2 | mipsisa32r2el \
+	| mipsisa32r6 | mipsisa32r6el \
 	| mipsisa64 | mipsisa64el \
 	| mipsisa64r2 | mipsisa64r2el \
+	| mipsisa64r6 | mipsisa64r6el \
 	| mipsisa64sb1 | mipsisa64sb1el \
 	| mipsisa64sr71k | mipsisa64sr71kel \
+	| mipsr5900 | mipsr5900el \
 	| mipstx39 | mipstx39el \
 	| mn10200 | mn10300 \
 	| moxie \
 	| mt \
 	| msp430 \
 	| nds32 | nds32le | nds32be \
-	| nios | nios2 \
+	| nios | nios2 | nios2eb | nios2el \
 	| ns16k | ns32k \
-	| open8 \
-	| or32 \
+	| open8 | or1k | or1knd | or32 \
 	| pdp10 | pdp11 | pj | pjl \
 	| powerpc | powerpc64 | powerpc64le | powerpcle \
 	| pyramid \
@@ -328,7 +326,7 @@ case $basic_machine in
 	c6x)
 		basic_machine=tic6x-unknown
 		;;
-	m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip)
+	m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip)
 		basic_machine=$basic_machine-unknown
 		os=-none
 		;;
@@ -370,13 +368,13 @@ case $basic_machine in
 	| aarch64-* | aarch64_be-* \
 	| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
 	| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
-	| alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+	| alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \
 	| arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
 	| avr-* | avr32-* \
 	| be32-* | be64-* \
 	| bfin-* | bs2000-* \
 	| c[123]* | c30-* | [cjt]90-* | c4x-* \
-	| clipper-* | craynv-* | cydra-* \
+	| c8051-* | clipper-* | craynv-* | cydra-* \
 	| d10v-* | d30v-* | dlx-* \
 	| elxsi-* \
 	| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
@@ -385,11 +383,13 @@ case $basic_machine in
 	| hexagon-* \
 	| i*86-* | i860-* | i960-* | ia64-* \
 	| ip2k-* | iq2000-* \
+	| k1om-* \
 	| le32-* | le64-* \
 	| lm32-* \
 	| m32c-* | m32r-* | m32rle-* \
 	| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
-	| m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \
+	| m88110-* | m88k-* | maxq-* | mcore-* | metag-* \
+	| microblaze-* | microblazeel-* \
 	| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
 	| mips16-* \
 	| mips64-* | mips64el-* \
@@ -403,18 +403,22 @@ case $basic_machine in
 	| mips64vr5900-* | mips64vr5900el-* \
 	| mipsisa32-* | mipsisa32el-* \
 	| mipsisa32r2-* | mipsisa32r2el-* \
+	| mipsisa32r6-* | mipsisa32r6el-* \
 	| mipsisa64-* | mipsisa64el-* \
 	| mipsisa64r2-* | mipsisa64r2el-* \
+	| mipsisa64r6-* | mipsisa64r6el-* \
 	| mipsisa64sb1-* | mipsisa64sb1el-* \
 	| mipsisa64sr71k-* | mipsisa64sr71kel-* \
+	| mipsr5900-* | mipsr5900el-* \
 	| mipstx39-* | mipstx39el-* \
 	| mmix-* \
 	| mt-* \
 	| msp430-* \
 	| nds32-* | nds32le-* | nds32be-* \
-	| nios-* | nios2-* \
+	| nios-* | nios2-* | nios2eb-* | nios2el-* \
 	| none-* | np1-* | ns16k-* | ns32k-* \
 	| open8-* \
+	| or1k*-* \
 	| orion-* \
 	| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
 	| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
@@ -788,11 +792,15 @@ case $basic_machine in
 		basic_machine=ns32k-utek
 		os=-sysv
 		;;
-	microblaze)
+	microblaze*)
 		basic_machine=microblaze-xilinx
 		;;
+	mingw64)
+		basic_machine=x86_64-pc
+		os=-mingw64
+		;;
 	mingw32)
-		basic_machine=i386-pc
+		basic_machine=i686-pc
 		os=-mingw32
 		;;
 	mingw32ce)
@@ -820,6 +828,10 @@ case $basic_machine in
 		basic_machine=powerpc-unknown
 		os=-morphos
 		;;
+	moxiebox)
+		basic_machine=moxie-unknown
+		os=-moxiebox
+		;;
 	msdos)
 		basic_machine=i386-pc
 		os=-msdos
@@ -828,7 +840,7 @@ case $basic_machine in
 		basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
 		;;
 	msys)
-		basic_machine=i386-pc
+		basic_machine=i686-pc
 		os=-msys
 		;;
 	mvs)
@@ -1004,7 +1016,7 @@ case $basic_machine in
 		;;
 	ppc64)	basic_machine=powerpc64-unknown
 		;;
-	ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+	ppc64-* | ppc64p7-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
 		;;
 	ppc64le | powerpc64little | ppc64-le | powerpc64-little)
 		basic_machine=powerpc64le-unknown
@@ -1019,7 +1031,11 @@ case $basic_machine in
 		basic_machine=i586-unknown
 		os=-pw32
 		;;
-	rdos)
+	rdos | rdos64)
+		basic_machine=x86_64-pc
+		os=-rdos
+		;;
+	rdos32)
 		basic_machine=i386-pc
 		os=-rdos
 		;;
@@ -1346,29 +1362,29 @@ case $os in
 	-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
 	      | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
 	      | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
-	      | -sym* | -kopensolaris* \
+	      | -sym* | -kopensolaris* | -plan9* \
 	      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
 	      | -aos* | -aros* \
 	      | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
 	      | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
 	      | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
-	      | -openbsd* | -solidbsd* \
+	      | -bitrig* | -openbsd* | -solidbsd* \
 	      | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
 	      | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
 	      | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
 	      | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
 	      | -chorusos* | -chorusrdb* | -cegcc* \
 	      | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
-	      | -mingw32* | -linux-gnu* | -linux-android* \
-	      | -linux-newlib* | -linux-uclibc* \
-	      | -uxpv* | -beos* | -mpeix* | -udk* \
+	      | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
+	      | -linux-newlib* | -linux-musl* | -linux-uclibc* \
+	      | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
 	      | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
 	      | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
 	      | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
 	      | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
 	      | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
 	      | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
-	      | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)
+	      | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*)
 	# Remember, each alternative MUST END IN *, to match a version number.
 		;;
 	-qnx*)
@@ -1492,9 +1508,6 @@ case $os in
 	-aros*)
 		os=-aros
 		;;
-	-kaos*)
-		os=-kaos
-		;;
 	-zvmoe)
 		os=-zvmoe
 		;;
@@ -1543,6 +1556,9 @@ case $basic_machine in
 	c4x-* | tic4x-*)
 		os=-coff
 		;;
+	c8051-*)
+		os=-elf
+		;;
 	hexagon-*)
 		os=-elf
 		;;
diff --git a/configure b/configure
index 735d3f5..4a332b3 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for guacamole-server 0.8.3.
+# Generated by GNU Autoconf 2.69 for guacamole-server 0.9.9.
 #
 #
 # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
@@ -587,8 +587,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='guacamole-server'
 PACKAGE_TARNAME='guacamole-server'
-PACKAGE_VERSION='0.8.3'
-PACKAGE_STRING='guacamole-server 0.8.3'
+PACKAGE_VERSION='0.9.9'
+PACKAGE_STRING='guacamole-server 0.9.9'
 PACKAGE_BUGREPORT=''
 PACKAGE_URL=''
 
@@ -632,19 +632,33 @@ ac_subst_vars='am__EXEEXT_FALSE
 am__EXEEXT_TRUE
 LTLIBOBJS
 LIBOBJS
+WEBP_LIBS
+ENABLE_WEBP_FALSE
+ENABLE_WEBP_TRUE
+TELNET_LIBS
+ENABLE_TELNET_FALSE
+ENABLE_TELNET_TRUE
+ENABLE_SSH_AGENT_FALSE
+ENABLE_SSH_AGENT_TRUE
 SSH_LIBS
 ENABLE_SSH_FALSE
 ENABLE_SSH_TRUE
+ENABLE_COMMON_SSH_FALSE
+ENABLE_COMMON_SSH_TRUE
 RDP_LIBS
 ENABLE_RDP_FALSE
 ENABLE_RDP_TRUE
 ENABLE_WINPR_FALSE
 ENABLE_WINPR_TRUE
+ENABLE_DISPLAY_UPDATE_FALSE
+ENABLE_DISPLAY_UPDATE_TRUE
 LEGACY_FREERDP_EXTENSIONS_FALSE
 LEGACY_FREERDP_EXTENSIONS_TRUE
 VNC_LIBS
 ENABLE_VNC_FALSE
 ENABLE_VNC_TRUE
+ENABLE_TERMINAL_FALSE
+ENABLE_TERMINAL_TRUE
 PANGOCAIRO_LIBS
 PANGOCAIRO_CFLAGS
 PANGO_LIBS
@@ -664,13 +678,22 @@ ENABLE_SSL_TRUE
 init_dir
 ENABLE_INIT_FALSE
 ENABLE_INIT_TRUE
+TERMINAL_INCLUDE
+TERMINAL_LTLIB
+COMMON_SSH_INCLUDE
+COMMON_SSH_LTLIB
+COMMON_INCLUDE
+COMMON_LTLIB
 LIBGUAC_INCLUDE
 LIBGUAC_LTLIB
 CUNIT_LIBS
+UUID_LIBS
 PTHREAD_LIBS
 CAIRO_LIBS
+JPEG_LIBS
 PNG_LIBS
-DL_LIBS
+MATH_LIBS
+LIBADD_DLOPEN
 CPP
 OTOOL64
 OTOOL
@@ -718,6 +741,10 @@ build_vendor
 build_cpu
 build
 LIBTOOL
+AM_BACKSLASH
+AM_DEFAULT_VERBOSITY
+AM_DEFAULT_V
+AM_V
 am__untar
 am__tar
 AMTAR
@@ -782,6 +809,7 @@ SHELL'
 ac_subst_files=''
 ac_user_opts='
 enable_option_checking
+enable_silent_rules
 enable_shared
 enable_static
 with_pic
@@ -791,6 +819,18 @@ with_gnu_ld
 with_sysroot
 enable_libtool_lock
 with_init_dir
+with_guacd_conf
+with_ssl
+with_vorbis
+with_pulse
+with_pango
+with_terminal
+with_vnc
+with_rdp
+with_ssh
+enable_ssh_agent
+with_telnet
+with_webp
 '
       ac_precious_vars='build_alias
 host_alias
@@ -1348,7 +1388,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures guacamole-server 0.8.3 to adapt to many kinds of systems.
+\`configure' configures guacamole-server 0.9.9 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1419,7 +1459,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of guacamole-server 0.8.3:";;
+     short | recursive ) echo "Configuration of guacamole-server 0.9.9:";;
    esac
   cat <<\_ACEOF
 
@@ -1427,13 +1467,19 @@ Optional Features:
   --disable-option-checking  ignore unrecognized --enable/--with options
   --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
   --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --enable-silent-rules   less verbose build output (undo: "make V=1")
+  --disable-silent-rules  verbose build output (undo: "make V=0")
   --enable-shared[=PKGS]  build shared libraries [default=yes]
   --enable-static[=PKGS]  build static libraries [default=yes]
   --enable-fast-install[=PKGS]
                           optimize for fast installation [default=yes]
-  --disable-dependency-tracking  speeds up one-time build
-  --enable-dependency-tracking   do not reject slow dependency extractors
+  --enable-dependency-tracking
+                          do not reject slow dependency extractors
+  --disable-dependency-tracking
+                          speeds up one-time build
   --disable-libtool-lock  avoid locking (might break parallel builds)
+  --enable-ssh-agent      enable built-in ssh-agent
+
 
 Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
@@ -1445,6 +1491,19 @@ Optional Packages:
                         (or the compiler's sysroot if not specified).
   --with-init-dir=<path>  install init scripts to the given directory
 
+  --with-guacd-conf=<path>
+                          the full path to the guacd config file
+                          [default=/etc/guacamole/guacd.conf]
+  --with-ssl              support SSL encryption [default=check]
+  --with-vorbis           support Ogg Vorbis [default=check]
+  --with-pulse            support PulseAudio [default=check]
+  --with-pango            support Pango text layout [default=check]
+  --with-terminal         support text-based protocols [default=check]
+  --with-vnc              support VNC [default=check]
+  --with-rdp              support RDP [default=check]
+  --with-ssh              support SSH [default=check]
+  --with-telnet           support Telnet [default=check]
+  --with-webp             support WebP image encoding [default=check]
 
 Some influential environment variables:
   CC          C compiler command
@@ -1534,7 +1593,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-guacamole-server configure 0.8.3
+guacamole-server configure 0.9.9
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2056,7 +2115,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by guacamole-server $as_me 0.8.3, which was
+It was created by guacamole-server $as_me 0.9.9, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -2404,7 +2463,7 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $
 ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
 
-am__api_version='1.11'
+am__api_version='1.14'
 
 ac_aux_dir=
 for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
@@ -2530,9 +2589,6 @@ test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
 
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5
 $as_echo_n "checking whether build environment is sane... " >&6; }
-# Just in case
-sleep 1
-echo timestamp > conftest.file
 # Reject unsafe characters in $srcdir or the absolute working directory
 # name.  Accept space and tab only in the latter.
 am_lf='
@@ -2543,32 +2599,40 @@ case `pwd` in
 esac
 case $srcdir in
   *[\\\"\#\$\&\'\`$am_lf\ \	]*)
-    as_fn_error $? "unsafe srcdir value: \`$srcdir'" "$LINENO" 5;;
+    as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;;
 esac
 
-# Do `set' in a subshell so we don't clobber the current shell's
+# Do 'set' in a subshell so we don't clobber the current shell's
 # arguments.  Must try -L first in case configure is actually a
 # symlink; some systems play weird games with the mod time of symlinks
 # (eg FreeBSD returns the mod time of the symlink's containing
 # directory).
 if (
-   set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
-   if test "$*" = "X"; then
-      # -L didn't work.
-      set X `ls -t "$srcdir/configure" conftest.file`
-   fi
-   rm -f conftest.file
-   if test "$*" != "X $srcdir/configure conftest.file" \
-      && test "$*" != "X conftest.file $srcdir/configure"; then
-
-      # If neither matched, then we have a broken ls.  This can happen
-      # if, for instance, CONFIG_SHELL is bash and it inherits a
-      # broken ls alias from the environment.  This has actually
-      # happened.  Such a system could not be considered "sane".
-      as_fn_error $? "ls -t appears to fail.  Make sure there is not a broken
-alias in your environment" "$LINENO" 5
-   fi
-
+   am_has_slept=no
+   for am_try in 1 2; do
+     echo "timestamp, slept: $am_has_slept" > conftest.file
+     set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+     if test "$*" = "X"; then
+	# -L didn't work.
+	set X `ls -t "$srcdir/configure" conftest.file`
+     fi
+     if test "$*" != "X $srcdir/configure conftest.file" \
+	&& test "$*" != "X conftest.file $srcdir/configure"; then
+
+	# If neither matched, then we have a broken ls.  This can happen
+	# if, for instance, CONFIG_SHELL is bash and it inherits a
+	# broken ls alias from the environment.  This has actually
+	# happened.  Such a system could not be considered "sane".
+	as_fn_error $? "ls -t appears to fail.  Make sure there is not a broken
+  alias in your environment" "$LINENO" 5
+     fi
+     if test "$2" = conftest.file || test $am_try -eq 2; then
+       break
+     fi
+     # Just in case.
+     sleep 1
+     am_has_slept=yes
+   done
    test "$2" = conftest.file
    )
 then
@@ -2580,6 +2644,16 @@ Check your system clock" "$LINENO" 5
 fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
+# If we didn't sleep, we still need to ensure time stamps of config.status and
+# generated files are strictly newer.
+am_sleep_pid=
+if grep 'slept: no' conftest.file >/dev/null 2>&1; then
+  ( sleep 1 ) &
+  am_sleep_pid=$!
+fi
+
+rm -f conftest.file
+
 test "$program_prefix" != NONE &&
   program_transform_name="s&^&$program_prefix&;$program_transform_name"
 # Use a double $ so make ignores it.
@@ -2602,12 +2676,12 @@ if test x"${MISSING+set}" != xset; then
   esac
 fi
 # Use eval to expand $SHELL
-if eval "$MISSING --run true"; then
-  am_missing_run="$MISSING --run "
+if eval "$MISSING --is-lightweight"; then
+  am_missing_run="$MISSING "
 else
   am_missing_run=
-  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`missing' script is too old or missing" >&5
-$as_echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;}
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5
+$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;}
 fi
 
 if test x"${install_sh}" != xset; then
@@ -2619,10 +2693,10 @@ if test x"${install_sh}" != xset; then
   esac
 fi
 
-# Installed binaries are usually stripped using `strip' when the user
-# run `make install-strip'.  However `strip' might not be the right
+# Installed binaries are usually stripped using 'strip' when the user
+# run "make install-strip".  However 'strip' might not be the right
 # tool to use in cross-compilation environments, therefore Automake
-# will honor the `STRIP' environment variable to overrule this program.
+# will honor the 'STRIP' environment variable to overrule this program.
 if test "$cross_compiling" != no; then
   if test -n "$ac_tool_prefix"; then
   # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
@@ -2761,12 +2835,6 @@ fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5
 $as_echo "$MKDIR_P" >&6; }
 
-mkdir_p="$MKDIR_P"
-case $mkdir_p in
-  [\\/$]* | ?:[\\/]*) ;;
-  */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
-esac
-
 for ac_prog in gawk mawk nawk awk
 do
   # Extract the first word of "$ac_prog", so it can be a program name with args.
@@ -2849,6 +2917,45 @@ else
 fi
 rmdir .tst 2>/dev/null
 
+# Check whether --enable-silent-rules was given.
+if test "${enable_silent_rules+set}" = set; then :
+  enableval=$enable_silent_rules;
+fi
+
+case $enable_silent_rules in # (((
+  yes) AM_DEFAULT_VERBOSITY=0;;
+   no) AM_DEFAULT_VERBOSITY=1;;
+    *) AM_DEFAULT_VERBOSITY=1;;
+esac
+am_make=${MAKE-make}
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5
+$as_echo_n "checking whether $am_make supports nested variables... " >&6; }
+if ${am_cv_make_support_nested_variables+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if $as_echo 'TRUE=$(BAR$(V))
+BAR0=false
+BAR1=true
+V=1
+am__doit:
+	@$(TRUE)
+.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then
+  am_cv_make_support_nested_variables=yes
+else
+  am_cv_make_support_nested_variables=no
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5
+$as_echo "$am_cv_make_support_nested_variables" >&6; }
+if test $am_cv_make_support_nested_variables = yes; then
+    AM_V='$(V)'
+  AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
+else
+  AM_V=$AM_DEFAULT_VERBOSITY
+  AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
+fi
+AM_BACKSLASH='\'
+
 if test "`cd $srcdir && pwd`" != "`pwd`"; then
   # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
   # is not polluted with repeated "-I."
@@ -2871,7 +2978,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='guacamole-server'
- VERSION='0.8.3'
+ VERSION='0.9.9'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -2899,12 +3006,22 @@ AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"}
 
 MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
 
+# For better backward compatibility.  To be removed once Automake 1.9.x
+# dies out for good.  For more background, see:
+# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
+# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
+mkdir_p='$(MKDIR_P)'
+
 # We need awk for the "check" target.  The system "awk" is bad on
 # some platforms.
 # Always define AMTAR for backward compatibility.  Yes, it's still used
 # in the wild :-(  We should find a proper way to deprecate it ...
 AMTAR='$${TAR-tar}'
 
+
+# We'll loop over all known methods to create a tar archive until one works.
+_am_tools='gnutar  pax cpio none'
+
 am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'
 
 
@@ -2912,6 +3029,87 @@ am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'
 
 
 
+# POSIX will say in a future version that running "rm -f" with no argument
+# is OK; and we want to be able to make that assumption in our Makefile
+# recipes.  So use an aggressive probe to check that the usage we want is
+# actually supported "in the wild" to an acceptable degree.
+# See automake bug#10828.
+# To make any issue more visible, cause the running configure to be aborted
+# by default if the 'rm' program in use doesn't match our expectations; the
+# user can still override this though.
+if rm -f && rm -fr && rm -rf; then : OK; else
+  cat >&2 <<'END'
+Oops!
+
+Your 'rm' program seems unable to run without file operands specified
+on the command line, even when the '-f' option is present.  This is contrary
+to the behaviour of most rm programs out there, and not conforming with
+the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542>
+
+Please tell bug-automake at gnu.org about your system, including the value
+of your $PATH and any error possibly output before this message.  This
+can help us improve future automake versions.
+
+END
+  if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then
+    echo 'Configuration will proceed anyway, since you have set the' >&2
+    echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2
+    echo >&2
+  else
+    cat >&2 <<'END'
+Aborting the configuration process, to ensure you take notice of the issue.
+
+You can download and install GNU coreutils to get an 'rm' implementation
+that behaves properly: <http://www.gnu.org/software/coreutils/>.
+
+If you want to complete the configuration process using your problematic
+'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
+to "yes", and re-run configure.
+
+END
+    as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5
+  fi
+fi
+# Check whether --enable-silent-rules was given.
+if test "${enable_silent_rules+set}" = set; then :
+  enableval=$enable_silent_rules;
+fi
+
+case $enable_silent_rules in # (((
+  yes) AM_DEFAULT_VERBOSITY=0;;
+   no) AM_DEFAULT_VERBOSITY=1;;
+    *) AM_DEFAULT_VERBOSITY=0;;
+esac
+am_make=${MAKE-make}
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5
+$as_echo_n "checking whether $am_make supports nested variables... " >&6; }
+if ${am_cv_make_support_nested_variables+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if $as_echo 'TRUE=$(BAR$(V))
+BAR0=false
+BAR1=true
+V=1
+am__doit:
+	@$(TRUE)
+.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then
+  am_cv_make_support_nested_variables=yes
+else
+  am_cv_make_support_nested_variables=no
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5
+$as_echo "$am_cv_make_support_nested_variables" >&6; }
+if test $am_cv_make_support_nested_variables = yes; then
+    AM_V='$(V)'
+  AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
+else
+  AM_V=$AM_DEFAULT_VERBOSITY
+  AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
+fi
+AM_BACKSLASH='\'
+
+
 
 case `pwd` in
   *\ * | *\	*)
@@ -3097,7 +3295,7 @@ am__quote=
 _am_result=none
 # First try GNU make style include.
 echo "include confinc" > confmf
-# Ignore all kinds of additional output from `make'.
+# Ignore all kinds of additional output from 'make'.
 case `$am_make -s -f confmf 2> /dev/null` in #(
 *the\ am__doit\ target*)
   am__include=include
@@ -3930,6 +4128,65 @@ ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
 ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5
+$as_echo_n "checking whether $CC understands -c and -o together... " >&6; }
+if ${am_cv_prog_cc_c_o+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+  # Make sure it works both with $CC and with simple cc.
+  # Following AC_PROG_CC_C_O, we do the test twice because some
+  # compilers refuse to overwrite an existing .o file with -o,
+  # though they will create one.
+  am_cv_prog_cc_c_o=yes
+  for am_i in 1 2; do
+    if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5
+   ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   (exit $ac_status); } \
+         && test -f conftest2.$ac_objext; then
+      : OK
+    else
+      am_cv_prog_cc_c_o=no
+      break
+    fi
+  done
+  rm -f core conftest*
+  unset am_i
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5
+$as_echo "$am_cv_prog_cc_c_o" >&6; }
+if test "$am_cv_prog_cc_c_o" != yes; then
+   # Losing compiler, so override with the script.
+   # FIXME: It is wrong to rewrite CC.
+   # But if we don't then we get into trouble of one sort or another.
+   # A longer-term fix would be to have automake use am__CC in this case,
+   # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
+   CC="$am_aux_dir/compile $CC"
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
 depcc="$CC"   am_compiler_list=
 
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5
@@ -3941,8 +4198,8 @@ else
   # We make a subdir and do the tests there.  Otherwise we can end up
   # making bogus files that we don't know about and never remove.  For
   # instance it was reported that on HP-UX the gcc test will end up
-  # making a dummy file named `D' -- because `-MD' means `put the output
-  # in D'.
+  # making a dummy file named 'D' -- because '-MD' means "put the output
+  # in D".
   rm -rf conftest.dir
   mkdir conftest.dir
   # Copy depcomp to subdir because otherwise we won't find it if we're
@@ -3977,16 +4234,16 @@ else
     : > sub/conftest.c
     for i in 1 2 3 4 5 6; do
       echo '#include "conftst'$i'.h"' >> sub/conftest.c
-      # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
-      # Solaris 8's {/usr,}/bin/sh.
-      touch sub/conftst$i.h
+      # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
+      # Solaris 10 /bin/sh.
+      echo '/* dummy */' > sub/conftst$i.h
     done
     echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
 
-    # We check with `-c' and `-o' for the sake of the "dashmstdout"
+    # We check with '-c' and '-o' for the sake of the "dashmstdout"
     # mode.  It turns out that the SunPro C++ compiler does not properly
-    # handle `-M -o', and we need to detect this.  Also, some Intel
-    # versions had trouble with output in subdirs
+    # handle '-M -o', and we need to detect this.  Also, some Intel
+    # versions had trouble with output in subdirs.
     am__obj=sub/conftest.${OBJEXT-o}
     am__minus_obj="-o $am__obj"
     case $depmode in
@@ -3995,8 +4252,8 @@ else
       test "$am__universal" = false || continue
       ;;
     nosideeffect)
-      # after this tag, mechanisms are not by side-effect, so they'll
-      # only be used when explicitly requested
+      # After this tag, mechanisms are not by side-effect, so they'll
+      # only be used when explicitly requested.
       if test "x$enable_dependency_tracking" = xyes; then
 	continue
       else
@@ -4004,7 +4261,7 @@ else
       fi
       ;;
     msvc7 | msvc7msys | msvisualcpp | msvcmsys)
-      # This compiler won't grok `-c -o', but also, the minuso test has
+      # This compiler won't grok '-c -o', but also, the minuso test has
       # not run yet.  These depmodes are late enough in the game, and
       # so weak that their functioning should not be impacted.
       am__obj=conftest.${OBJEXT-o}
@@ -5179,6 +5436,10 @@ freebsd* | dragonfly*)
   fi
   ;;
 
+gnu*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
 haiku*)
   lt_cv_deplibs_check_method=pass_all
   ;;
@@ -5217,11 +5478,11 @@ irix5* | irix6* | nonstopux*)
   ;;
 
 # This must be glibc/ELF.
-linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+linux* | k*bsd*-gnu | kopensolaris*-gnu)
   lt_cv_deplibs_check_method=pass_all
   ;;
 
-netbsd* | netbsdelf*-gnu)
+netbsd*)
   if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
     lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
   else
@@ -6299,7 +6560,7 @@ ia64-*-hpux*)
   rm -rf conftest*
   ;;
 
-x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \
 s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
   # Find out which ABI we are using.
   echo 'int i;' > conftest.$ac_ext
@@ -6317,7 +6578,10 @@ s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
 	  x86_64-*linux*)
 	    LD="${LD-ld} -m elf_i386"
 	    ;;
-	  ppc64-*linux*|powerpc64-*linux*)
+	  powerpc64le-*linux*)
+	    LD="${LD-ld} -m elf32lppclinux"
+	    ;;
+	  powerpc64-*linux*)
 	    LD="${LD-ld} -m elf32ppclinux"
 	    ;;
 	  s390x-*linux*)
@@ -6336,7 +6600,10 @@ s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
 	  x86_64-*linux*)
 	    LD="${LD-ld} -m elf_x86_64"
 	    ;;
-	  ppc*-*linux*|powerpc*-*linux*)
+	  powerpcle-*linux*)
+	    LD="${LD-ld} -m elf64lppc"
+	    ;;
+	  powerpc-*linux*)
 	    LD="${LD-ld} -m elf64ppc"
 	    ;;
 	  s390*-*linux*|s390*-*tpf*)
@@ -8143,7 +8410,7 @@ lt_prog_compiler_static=
       lt_prog_compiler_static='-non_shared'
       ;;
 
-    linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+    linux* | k*bsd*-gnu | kopensolaris*-gnu)
       case $cc_basename in
       # old Intel for x86_64 which still supported -KPIC.
       ecc*)
@@ -8621,9 +8888,6 @@ $as_echo_n "checking whether the $compiler linker ($LD) supports shared librarie
   openbsd*)
     with_gnu_ld=no
     ;;
-  linux* | k*bsd*-gnu | gnu*)
-    link_all_deplibs=no
-    ;;
   esac
 
   ld_shlibs=yes
@@ -8845,7 +9109,7 @@ _LT_EOF
       fi
       ;;
 
-    netbsd* | netbsdelf*-gnu)
+    netbsd*)
       if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
 	archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
 	wlarc=
@@ -9022,7 +9286,6 @@ _LT_EOF
 	if test "$aix_use_runtimelinking" = yes; then
 	  shared_flag="$shared_flag "'${wl}-G'
 	fi
-	link_all_deplibs=no
       else
 	# not using gcc
 	if test "$host_cpu" = ia64; then
@@ -9476,7 +9739,7 @@ $as_echo "$lt_cv_irix_exported_symbol" >&6; }
       link_all_deplibs=yes
       ;;
 
-    netbsd* | netbsdelf*-gnu)
+    netbsd*)
       if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
 	archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'  # a.out
       else
@@ -10313,6 +10576,17 @@ freebsd* | dragonfly*)
   esac
   ;;
 
+gnu*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
 haiku*)
   version_type=linux # correct to gnu/linux during the next big refactor
   need_lib_prefix=no
@@ -10429,7 +10703,7 @@ linux*oldld* | linux*aout* | linux*coff*)
   ;;
 
 # This must be glibc/ELF.
-linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+linux* | k*bsd*-gnu | kopensolaris*-gnu)
   version_type=linux # correct to gnu/linux during the next big refactor
   need_lib_prefix=no
   need_version=no
@@ -10478,10 +10752,14 @@ fi
   # before this can be enabled.
   hardcode_into_libs=yes
 
+  # Add ABI-specific directories to the system library path.
+  sys_lib_dlsearch_path_spec="/lib64 /usr/lib64 /lib /usr/lib"
+
   # Append ld.so.conf contents to the search path
   if test -f /etc/ld.so.conf; then
     lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[	 ]*hwcap[	 ]/d;s/[:,	]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '`
-    sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+    sys_lib_dlsearch_path_spec="$sys_lib_dlsearch_path_spec $lt_ld_extra"
+
   fi
 
   # We used to test for /lib/ld.so.1 and disable shared libraries on
@@ -10493,18 +10771,6 @@ fi
   dynamic_linker='GNU/Linux ld.so'
   ;;
 
-netbsdelf*-gnu)
-  version_type=linux
-  need_lib_prefix=no
-  need_version=no
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
-  soname_spec='${libname}${release}${shared_ext}$major'
-  shlibpath_var=LD_LIBRARY_PATH
-  shlibpath_overrides_runpath=no
-  hardcode_into_libs=yes
-  dynamic_linker='NetBSD ld.elf_so'
-  ;;
-
 netbsd*)
   version_type=sunos
   need_lib_prefix=no
@@ -11476,6 +11742,8 @@ CC="$lt_save_CC"
 
 
 
+ac_config_headers="$ac_config_headers config.h"
+
 
 
 # Programs
@@ -12018,6 +12286,65 @@ ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
 ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5
+$as_echo_n "checking whether $CC understands -c and -o together... " >&6; }
+if ${am_cv_prog_cc_c_o+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+  # Make sure it works both with $CC and with simple cc.
+  # Following AC_PROG_CC_C_O, we do the test twice because some
+  # compilers refuse to overwrite an existing .o file with -o,
+  # though they will create one.
+  am_cv_prog_cc_c_o=yes
+  for am_i in 1 2; do
+    if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5
+   ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   (exit $ac_status); } \
+         && test -f conftest2.$ac_objext; then
+      : OK
+    else
+      am_cv_prog_cc_c_o=no
+      break
+    fi
+  done
+  rm -f core conftest*
+  unset am_i
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5
+$as_echo "$am_cv_prog_cc_c_o" >&6; }
+if test "$am_cv_prog_cc_c_o" != yes; then
+   # Losing compiler, so override with the script.
+   # FIXME: It is wrong to rewrite CC.
+   # But if we don't then we get into trouble of one sort or another.
+   # A longer-term fix would be to have automake use am__CC in this case,
+   # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
+   CC="$am_aux_dir/compile $CC"
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
 depcc="$CC"   am_compiler_list=
 
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5
@@ -12029,8 +12356,8 @@ else
   # We make a subdir and do the tests there.  Otherwise we can end up
   # making bogus files that we don't know about and never remove.  For
   # instance it was reported that on HP-UX the gcc test will end up
-  # making a dummy file named `D' -- because `-MD' means `put the output
-  # in D'.
+  # making a dummy file named 'D' -- because '-MD' means "put the output
+  # in D".
   rm -rf conftest.dir
   mkdir conftest.dir
   # Copy depcomp to subdir because otherwise we won't find it if we're
@@ -12065,16 +12392,16 @@ else
     : > sub/conftest.c
     for i in 1 2 3 4 5 6; do
       echo '#include "conftst'$i'.h"' >> sub/conftest.c
-      # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
-      # Solaris 8's {/usr,}/bin/sh.
-      touch sub/conftst$i.h
+      # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
+      # Solaris 10 /bin/sh.
+      echo '/* dummy */' > sub/conftst$i.h
     done
     echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
 
-    # We check with `-c' and `-o' for the sake of the "dashmstdout"
+    # We check with '-c' and '-o' for the sake of the "dashmstdout"
     # mode.  It turns out that the SunPro C++ compiler does not properly
-    # handle `-M -o', and we need to detect this.  Also, some Intel
-    # versions had trouble with output in subdirs
+    # handle '-M -o', and we need to detect this.  Also, some Intel
+    # versions had trouble with output in subdirs.
     am__obj=sub/conftest.${OBJEXT-o}
     am__minus_obj="-o $am__obj"
     case $depmode in
@@ -12083,8 +12410,8 @@ else
       test "$am__universal" = false || continue
       ;;
     nosideeffect)
-      # after this tag, mechanisms are not by side-effect, so they'll
-      # only be used when explicitly requested
+      # After this tag, mechanisms are not by side-effect, so they'll
+      # only be used when explicitly requested.
       if test "x$enable_dependency_tracking" = xyes; then
 	continue
       else
@@ -12092,7 +12419,7 @@ else
       fi
       ;;
     msvc7 | msvc7msys | msvisualcpp | msvcmsys)
-      # This compiler won't grok `-c -o', but also, the minuso test has
+      # This compiler won't grok '-c -o', but also, the minuso test has
       # not run yet.  These depmodes are late enough in the game, and
       # so weak that their functioning should not be impacted.
       am__obj=conftest.${OBJEXT-o}
@@ -12342,20 +12669,17 @@ done
 
 # Source characteristics
 
-$as_echo "#define _POSIX_C_SOURCE 199309L" >>confdefs.h
-
-
-$as_echo "#define _BSD_SOURCE /**/" >>confdefs.h
+$as_echo "#define _XOPEN_SOURCE 700" >>confdefs.h
 
 
-# libdl
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
-$as_echo_n "checking for dlopen in -ldl... " >&6; }
-if ${ac_cv_lib_dl_dlopen+:} false; then :
+# Check for whether math library is required
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for cos in -lm" >&5
+$as_echo_n "checking for cos in -lm... " >&6; }
+if ${ac_cv_lib_m_cos+:} false; then :
   $as_echo_n "(cached) " >&6
 else
   ac_check_lib_save_LIBS=$LIBS
-LIBS="-ldl  $LIBS"
+LIBS="-lm  $LIBS"
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -12365,30 +12689,37 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 #ifdef __cplusplus
 extern "C"
 #endif
-char dlopen ();
+char cos ();
 int
 main ()
 {
-return dlopen ();
+return cos ();
   ;
   return 0;
 }
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
-  ac_cv_lib_dl_dlopen=yes
+  ac_cv_lib_m_cos=yes
 else
-  ac_cv_lib_dl_dlopen=no
+  ac_cv_lib_m_cos=no
 fi
 rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
 LIBS=$ac_check_lib_save_LIBS
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
-$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
-if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
-  DL_LIBS=-ldl
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_cos" >&5
+$as_echo "$ac_cv_lib_m_cos" >&6; }
+if test "x$ac_cv_lib_m_cos" = xyes; then :
+  MATH_LIBS=-lm
+else
+  ac_fn_c_check_decl "$LINENO" "cos" "ac_cv_have_decl_cos" "$ac_includes_default"
+if test "x$ac_cv_have_decl_cos" = xyes; then :
+
 else
-  as_fn_error $? "\"libdl is required for loading client plugins\"" "$LINENO" 5
+  as_fn_error $? "\"Complex math functions are missing and no libm was found\"" "$LINENO" 5
+                            #include <math.h>
+fi
+
 fi
 
 
@@ -12436,6 +12767,50 @@ else
 fi
 
 
+# libjpeg
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for jpeg_start_compress in -ljpeg" >&5
+$as_echo_n "checking for jpeg_start_compress in -ljpeg... " >&6; }
+if ${ac_cv_lib_jpeg_jpeg_start_compress+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ljpeg  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char jpeg_start_compress ();
+int
+main ()
+{
+return jpeg_start_compress ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_jpeg_jpeg_start_compress=yes
+else
+  ac_cv_lib_jpeg_jpeg_start_compress=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_jpeg_jpeg_start_compress" >&5
+$as_echo "$ac_cv_lib_jpeg_jpeg_start_compress" >&6; }
+if test "x$ac_cv_lib_jpeg_jpeg_start_compress" = xyes; then :
+  JPEG_LIBS=-ljpeg
+else
+  as_fn_error $? "\"libjpeg is required for writing jpeg messages\"" "$LINENO" 5
+fi
+
+
 # Cairo
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cairo_create in -lcairo" >&5
 $as_echo_n "checking for cairo_create in -lcairo... " >&6; }
@@ -12519,19 +12894,20 @@ fi
 $as_echo "$ac_cv_lib_pthread_pthread_create" >&6; }
 if test "x$ac_cv_lib_pthread_pthread_create" = xyes; then :
   PTHREAD_LIBS=-lpthread
-              $as_echo "#define HAVE_LIBPTHREAD 1" >>confdefs.h
+
+$as_echo "#define HAVE_LIBPTHREAD /**/" >>confdefs.h
 
 fi
 
 
-# cunit
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for CU_run_test in -lcunit" >&5
-$as_echo_n "checking for CU_run_test in -lcunit... " >&6; }
-if ${ac_cv_lib_cunit_CU_run_test+:} false; then :
+# OSSP UUID
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for uuid_make in -lossp-uuid" >&5
+$as_echo_n "checking for uuid_make in -lossp-uuid... " >&6; }
+if ${ac_cv_lib_ossp_uuid_uuid_make+:} false; then :
   $as_echo_n "(cached) " >&6
 else
   ac_check_lib_save_LIBS=$LIBS
-LIBS="-lcunit  $LIBS"
+LIBS="-lossp-uuid  $LIBS"
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -12541,106 +12917,223 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 #ifdef __cplusplus
 extern "C"
 #endif
-char CU_run_test ();
+char uuid_make ();
 int
 main ()
 {
-return CU_run_test ();
+return uuid_make ();
   ;
   return 0;
 }
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
-  ac_cv_lib_cunit_CU_run_test=yes
+  ac_cv_lib_ossp_uuid_uuid_make=yes
 else
-  ac_cv_lib_cunit_CU_run_test=no
+  ac_cv_lib_ossp_uuid_uuid_make=no
 fi
 rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
 LIBS=$ac_check_lib_save_LIBS
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cunit_CU_run_test" >&5
-$as_echo "$ac_cv_lib_cunit_CU_run_test" >&6; }
-if test "x$ac_cv_lib_cunit_CU_run_test" = xyes; then :
-  CUNIT_LIBS=-lcunit
-fi
-
-
-# WinSock
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lwsock32" >&5
-$as_echo_n "checking for main in -lwsock32... " >&6; }
-if ${ac_cv_lib_wsock32_main+:} false; then :
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ossp_uuid_uuid_make" >&5
+$as_echo "$ac_cv_lib_ossp_uuid_uuid_make" >&6; }
+if test "x$ac_cv_lib_ossp_uuid_uuid_make" = xyes; then :
+  UUID_LIBS=-lossp-uuid
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uuid_make in -luuid" >&5
+$as_echo_n "checking for uuid_make in -luuid... " >&6; }
+if ${ac_cv_lib_uuid_uuid_make+:} false; then :
   $as_echo_n "(cached) " >&6
 else
   ac_check_lib_save_LIBS=$LIBS
-LIBS="-lwsock32  $LIBS"
+LIBS="-luuid  $LIBS"
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char uuid_make ();
 int
 main ()
 {
-return main ();
+return uuid_make ();
   ;
   return 0;
 }
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
-  ac_cv_lib_wsock32_main=yes
+  ac_cv_lib_uuid_uuid_make=yes
 else
-  ac_cv_lib_wsock32_main=no
+  ac_cv_lib_uuid_uuid_make=no
 fi
 rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
 LIBS=$ac_check_lib_save_LIBS
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_wsock32_main" >&5
-$as_echo "$ac_cv_lib_wsock32_main" >&6; }
-if test "x$ac_cv_lib_wsock32_main" = xyes; then :
-  cat >>confdefs.h <<_ACEOF
-#define HAVE_LIBWSOCK32 1
-_ACEOF
-
-  LIBS="-lwsock32 $LIBS"
-
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_uuid_uuid_make" >&5
+$as_echo "$ac_cv_lib_uuid_uuid_make" >&6; }
+if test "x$ac_cv_lib_uuid_uuid_make" = xyes; then :
+  UUID_LIBS=-luuid
+else
+  as_fn_error $? "\"The OSSP UUID library is required\"" "$LINENO" 5
 fi
 
+fi
 
 
-
-
-
-
-
-# Library functions
-for ac_func in clock_gettime gettimeofday memmove memset select strdup nanosleep
+# Check for and validate OSSP uuid.h header
+for ac_header in ossp/uuid.h
 do :
-  as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
-ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
-if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+  ac_fn_c_check_header_mongrel "$LINENO" "ossp/uuid.h" "ac_cv_header_ossp_uuid_h" "$ac_includes_default"
+if test "x$ac_cv_header_ossp_uuid_h" = xyes; then :
   cat >>confdefs.h <<_ACEOF
-#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+#define HAVE_OSSP_UUID_H 1
 _ACEOF
 
 fi
+
 done
 
+ac_fn_c_check_decl "$LINENO" "uuid_make" "ac_cv_have_decl_uuid_make" "#ifdef HAVE_OSSP_UUID_H
+               #include <ossp/uuid.h>
+               #else
+               #include <uuid.h>
+               #endif
 
-ac_fn_c_check_decl "$LINENO" "png_get_io_ptr" "ac_cv_have_decl_png_get_io_ptr" "#include <png.h>
 "
-if test "x$ac_cv_have_decl_png_get_io_ptr" = xyes; then :
-  $as_echo "#define HAVE_PNG_GET_IO_PTR 1" >>confdefs.h
+if test "x$ac_cv_have_decl_uuid_make" = xyes; then :
 
+else
+  as_fn_error $? "\"No OSSP uuid.h found in include path\"" "$LINENO" 5
 fi
 
 
-ac_fn_c_check_decl "$LINENO" "cairo_format_stride_for_width" "ac_cv_have_decl_cairo_format_stride_for_width" "#include <cairo/cairo.h>
-"
-if test "x$ac_cv_have_decl_cairo_format_stride_for_width" = xyes; then :
-  $as_echo "#define HAVE_CAIRO_FORMAT_STRIDE_FOR_WIDTH 1" >>confdefs.h
-
-fi
+# cunit
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for CU_run_test in -lcunit" >&5
+$as_echo_n "checking for CU_run_test in -lcunit... " >&6; }
+if ${ac_cv_lib_cunit_CU_run_test+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lcunit  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char CU_run_test ();
+int
+main ()
+{
+return CU_run_test ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_cunit_CU_run_test=yes
+else
+  ac_cv_lib_cunit_CU_run_test=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cunit_CU_run_test" >&5
+$as_echo "$ac_cv_lib_cunit_CU_run_test" >&6; }
+if test "x$ac_cv_lib_cunit_CU_run_test" = xyes; then :
+  CUNIT_LIBS=-lcunit
+fi
+
+
+# WinSock
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lwsock32" >&5
+$as_echo_n "checking for main in -lwsock32... " >&6; }
+if ${ac_cv_lib_wsock32_main+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lwsock32  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+
+int
+main ()
+{
+return main ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_wsock32_main=yes
+else
+  ac_cv_lib_wsock32_main=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_wsock32_main" >&5
+$as_echo "$ac_cv_lib_wsock32_main" >&6; }
+if test "x$ac_cv_lib_wsock32_main" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBWSOCK32 1
+_ACEOF
+
+  LIBS="-lwsock32 $LIBS"
+
+fi
+
+
+
+
+
+
+
+
+
+
+
+# Library functions
+for ac_func in clock_gettime gettimeofday memmove memset select strdup nanosleep
+do :
+  as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+ac_fn_c_check_decl "$LINENO" "png_get_io_ptr" "ac_cv_have_decl_png_get_io_ptr" "#include <png.h>
+"
+if test "x$ac_cv_have_decl_png_get_io_ptr" = xyes; then :
+
+$as_echo "#define HAVE_PNG_GET_IO_PTR /**/" >>confdefs.h
+
+fi
+
+
+ac_fn_c_check_decl "$LINENO" "cairo_format_stride_for_width" "ac_cv_have_decl_cairo_format_stride_for_width" "#include <cairo/cairo.h>
+"
+if test "x$ac_cv_have_decl_cairo_format_stride_for_width" = xyes; then :
+
+$as_echo "#define HAVE_CAIRO_FORMAT_STRIDE_FOR_WIDTH /**/" >>confdefs.h
+
+fi
 
 
 # Typedefs
@@ -12673,7 +13166,25 @@ LIBGUAC_LTLIB='$(top_builddir)/src/libguac/libguac.la'
 LIBGUAC_INCLUDE='-I$(top_srcdir)/src/libguac'
 
 
-# Options
+# Common non-libguac utility library
+COMMON_LTLIB='$(top_builddir)/src/common/libguac_common.la'
+
+COMMON_INCLUDE='-I$(top_srcdir)/src/common'
+
+
+# Common base SSH client
+COMMON_SSH_LTLIB='$(top_builddir)/src/common-ssh/libguac_common_ssh.la'
+
+COMMON_SSH_INCLUDE='-I$(top_srcdir)/src/common-ssh'
+
+
+# Terminal emulator
+TERMINAL_LTLIB='$(top_builddir)/src/terminal/libguac_terminal.la'
+
+TERMINAL_INCLUDE='-I$(top_srcdir)/src/terminal $(PANGO_CFLAGS) $(PANGOCAIRO_CFLAGS) $(COMMON_INCLUDE)'
+
+
+# Init directory
 
 # Check whether --with-init_dir was given.
 if test "${with_init_dir+set}" = set; then :
@@ -12690,14 +13201,41 @@ fi
 
 
 
+# guacd config file
+
+# Check whether --with-guacd_conf was given.
+if test "${with_guacd_conf+set}" = set; then :
+  withval=$with_guacd_conf; guacd_conf=$withval
+else
+  guacd_conf=/etc/guacamole/guacd.conf
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define GUACD_CONF_FILE "$guacd_conf"
+_ACEOF
+
+
 #
 # libssl
 #
 
-have_ssl=yes
+have_ssl=disabled
 SSL_LIBS=
 
-ac_fn_c_check_header_mongrel "$LINENO" "openssl/ssl.h" "ac_cv_header_openssl_ssl_h" "$ac_includes_default"
+# Check whether --with-ssl was given.
+if test "${with_ssl+set}" = set; then :
+  withval=$with_ssl;
+else
+  with_ssl=check
+fi
+
+
+if test "x$with_ssl" != "xno"
+then
+    have_ssl=yes
+
+    ac_fn_c_check_header_mongrel "$LINENO" "openssl/ssl.h" "ac_cv_header_openssl_ssl_h" "$ac_includes_default"
 if test "x$ac_cv_header_openssl_ssl_h" = xyes; then :
 
 else
@@ -12705,7 +13243,7 @@ else
 fi
 
 
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_CTX_new in -lssl" >&5
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_CTX_new in -lssl" >&5
 $as_echo_n "checking for SSL_CTX_new in -lssl... " >&6; }
 if ${ac_cv_lib_ssl_SSL_CTX_new+:} false; then :
   $as_echo_n "(cached) " >&6
@@ -12747,18 +13285,10 @@ else
   have_ssl=no
 fi
 
- if test "x${have_ssl}" = "xyes"; then
-  ENABLE_SSL_TRUE=
-  ENABLE_SSL_FALSE='#'
-else
-  ENABLE_SSL_TRUE='#'
-  ENABLE_SSL_FALSE=
-fi
-
 
-if test "x${have_ssl}" = "xno"
-then
-    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
+    if test "x${have_ssl}" = "xno"
+    then
+        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
   --------------------------------------------
    Unable to find libssl.
    guacd will not support SSL connections.
@@ -12768,9 +13298,19 @@ $as_echo "$as_me: WARNING:
    Unable to find libssl.
    guacd will not support SSL connections.
   --------------------------------------------" >&2;}
-else
-    $as_echo "#define ENABLE_SSL 1" >>confdefs.h
+    else
+
+$as_echo "#define ENABLE_SSL /**/" >>confdefs.h
+
+    fi
+fi
 
+ if test "x${have_ssl}" = "xyes"; then
+  ENABLE_SSL_TRUE=
+  ENABLE_SSL_FALSE='#'
+else
+  ENABLE_SSL_TRUE='#'
+  ENABLE_SSL_FALSE=
 fi
 
 
@@ -12780,10 +13320,22 @@ fi
 # Ogg Vorbis
 #
 
-have_vorbis=yes
+have_vorbis=disabled
 VORBIS_LIBS=
 
-ac_fn_c_check_header_mongrel "$LINENO" "vorbis/vorbisenc.h" "ac_cv_header_vorbis_vorbisenc_h" "$ac_includes_default"
+# Check whether --with-vorbis was given.
+if test "${with_vorbis+set}" = set; then :
+  withval=$with_vorbis;
+else
+  with_vorbis=check
+fi
+
+
+if test "x$with_vorbis" != "xno"
+then
+    have_vorbis=yes
+
+    ac_fn_c_check_header_mongrel "$LINENO" "vorbis/vorbisenc.h" "ac_cv_header_vorbis_vorbisenc_h" "$ac_includes_default"
 if test "x$ac_cv_header_vorbis_vorbisenc_h" = xyes; then :
 
 else
@@ -12791,7 +13343,7 @@ else
 fi
 
 
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ogg_stream_init in -logg" >&5
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ogg_stream_init in -logg" >&5
 $as_echo_n "checking for ogg_stream_init in -logg... " >&6; }
 if ${ac_cv_lib_ogg_ogg_stream_init+:} false; then :
   $as_echo_n "(cached) " >&6
@@ -12833,7 +13385,7 @@ else
   have_vorbis=no
 fi
 
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for vorbis_block_init in -lvorbis" >&5
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for vorbis_block_init in -lvorbis" >&5
 $as_echo_n "checking for vorbis_block_init in -lvorbis... " >&6; }
 if ${ac_cv_lib_vorbis_vorbis_block_init+:} false; then :
   $as_echo_n "(cached) " >&6
@@ -12875,7 +13427,7 @@ else
   have_vorbis=no
 fi
 
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for vorbis_encode_init in -lvorbisenc" >&5
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for vorbis_encode_init in -lvorbisenc" >&5
 $as_echo_n "checking for vorbis_encode_init in -lvorbisenc... " >&6; }
 if ${ac_cv_lib_vorbisenc_vorbis_encode_init+:} false; then :
   $as_echo_n "(cached) " >&6
@@ -12917,18 +13469,10 @@ else
   have_vorbis=no
 fi
 
- if test "x${have_vorbis}" = "xyes"; then
-  ENABLE_OGG_TRUE=
-  ENABLE_OGG_FALSE='#'
-else
-  ENABLE_OGG_TRUE='#'
-  ENABLE_OGG_FALSE=
-fi
-
 
-if test "x${have_vorbis}" = "xno"
-then
-    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
+    if test "x${have_vorbis}" = "xno"
+    then
+        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
   --------------------------------------------
    Unable to find libogg / libvorbis / libvorbisenc.
    Sound will not be encoded with Ogg Vorbis.
@@ -12938,9 +13482,19 @@ $as_echo "$as_me: WARNING:
    Unable to find libogg / libvorbis / libvorbisenc.
    Sound will not be encoded with Ogg Vorbis.
   --------------------------------------------" >&2;}
-else
-    $as_echo "#define ENABLE_OGG 1" >>confdefs.h
+    else
+
+$as_echo "#define ENABLE_OGG /**/" >>confdefs.h
+
+    fi
+fi
 
+ if test "x${have_vorbis}" = "xyes"; then
+  ENABLE_OGG_TRUE=
+  ENABLE_OGG_FALSE='#'
+else
+  ENABLE_OGG_TRUE='#'
+  ENABLE_OGG_FALSE=
 fi
 
 
@@ -12949,10 +13503,22 @@ fi
 # PulseAudio
 #
 
-have_pulse=yes
+have_pulse=disabled
 PULSE_LIBS=
 
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pa_context_new in -lpulse" >&5
+# Check whether --with-pulse was given.
+if test "${with_pulse+set}" = set; then :
+  withval=$with_pulse;
+else
+  with_pulse=check
+fi
+
+
+if test "x$with_pulse" != "xno"
+then
+    have_pulse=yes
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pa_context_new in -lpulse" >&5
 $as_echo_n "checking for pa_context_new in -lpulse... " >&6; }
 if ${ac_cv_lib_pulse_pa_context_new+:} false; then :
   $as_echo_n "(cached) " >&6
@@ -12994,18 +13560,10 @@ else
   have_pulse=no
 fi
 
- if test "x${have_pulse}" = "xyes"; then
-  ENABLE_PULSE_TRUE=
-  ENABLE_PULSE_FALSE='#'
-else
-  ENABLE_PULSE_TRUE='#'
-  ENABLE_PULSE_FALSE=
-fi
-
 
-if test "x${have_pulse}" = "xno"
-then
-    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
+    if test "x${have_pulse}" = "xno"
+    then
+        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
   --------------------------------------------
    Unable to find libpulse
    Sound support for VNC will be disabled
@@ -13015,9 +13573,19 @@ $as_echo "$as_me: WARNING:
    Unable to find libpulse
    Sound support for VNC will be disabled
   --------------------------------------------" >&2;}
-else
-    $as_echo "#define ENABLE_PULSE 1" >>confdefs.h
+    else
+
+$as_echo "#define ENABLE_PULSE /**/" >>confdefs.h
 
+    fi
+fi
+
+ if test "x${have_pulse}" = "xyes"; then
+  ENABLE_PULSE_TRUE=
+  ENABLE_PULSE_FALSE='#'
+else
+  ENABLE_PULSE_TRUE='#'
+  ENABLE_PULSE_FALSE=
 fi
 
 
@@ -13026,7 +13594,19 @@ fi
 # PANGO
 #
 
-have_pango=yes
+have_pango=disabled
+
+# Check whether --with-pango was given.
+if test "${with_pango+set}" = set; then :
+  withval=$with_pango;
+else
+  with_pango=check
+fi
+
+
+if test "x$with_pango" != "xno"
+then
+    have_pango=yes
 
 
 
@@ -13289,15 +13869,59 @@ else
 $as_echo "yes" >&6; }
 
 fi;
+fi
+
+#
+# Terminal emulator
+#
+
+have_terminal=disabled
+
+# Check whether --with-terminal was given.
+if test "${with_terminal+set}" = set; then :
+  withval=$with_terminal;
+else
+  with_terminal=check
+fi
+
+
+if test "x$with_terminal" != "xno"
+then
+    have_terminal=yes
+    if test "x${have_pango}" = "xno"
+    then
+        have_terminal=no
+    fi
+fi
+
+ if test "x${have_terminal}" = "xyes"; then
+  ENABLE_TERMINAL_TRUE=
+  ENABLE_TERMINAL_FALSE='#'
+else
+  ENABLE_TERMINAL_TRUE='#'
+  ENABLE_TERMINAL_FALSE=
+fi
+
 
 #
 # libVNCServer
 #
 
-have_libvncserver=yes
+have_libvncserver=disabled
 VNC_LIBS=
 
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for rfbInitClient in -lvncclient" >&5
+# Check whether --with-vnc was given.
+if test "${with_vnc+set}" = set; then :
+  withval=$with_vnc;
+else
+  with_vnc=check
+fi
+
+
+if test "x$with_vnc" != "xno"
+then
+    have_libvncserver=yes
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for rfbInitClient in -lvncclient" >&5
 $as_echo_n "checking for rfbInitClient in -lvncclient... " >&6; }
 if ${ac_cv_lib_vncclient_rfbInitClient+:} false; then :
   $as_echo_n "(cached) " >&6
@@ -13339,6 +13963,8 @@ else
   have_libvncserver=no
 fi
 
+fi
+
  if test "x${have_libvncserver}" = "xyes"; then
   ENABLE_VNC_TRUE=
   ENABLE_VNC_FALSE='#'
@@ -13349,7 +13975,6 @@ fi
 
 
 
-
 #
 # Repeater support within libVNCServer
 #
@@ -13397,7 +14022,45 @@ $as_echo "$as_me: WARNING:
        Support for VNC repeaters will not be built.
       --------------------------------------------" >&2;}
     else
-        $as_echo "#define ENABLE_VNC_REPEATER 1" >>confdefs.h
+
+$as_echo "#define ENABLE_VNC_REPEATER /**/" >>confdefs.h
+
+    fi
+
+fi
+
+#
+# Listening support within libVNCServer
+#
+
+if test "x${have_libvncserver}" = "xyes"
+then
+
+    have_vnc_listen=yes
+    ac_fn_c_check_decl "$LINENO" "listenForIncomingConnectionsNoFork" "ac_cv_have_decl_listenForIncomingConnectionsNoFork" "#include <rfb/rfbclient.h>
+"
+if test "x$ac_cv_have_decl_listenForIncomingConnectionsNoFork" = xyes; then :
+
+else
+  have_vnc_listen=no
+fi
+
+
+    if test "x${have_vnc_listen}" = "xno"
+    then
+        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
+      --------------------------------------------
+       No listening support found in libvncclient.
+       Support for listen-mode connections will not be built.
+      --------------------------------------------" >&5
+$as_echo "$as_me: WARNING:
+      --------------------------------------------
+       No listening support found in libvncclient.
+       Support for listen-mode connections will not be built.
+      --------------------------------------------" >&2;}
+    else
+
+$as_echo "#define ENABLE_VNC_LISTEN /**/" >>confdefs.h
 
     fi
 
@@ -13407,22 +14070,37 @@ fi
 # FreeRDP
 #
 
-have_winpr=yes
-have_freerdp=yes
-legacy_freerdp_extensions=no
-rdpsettings_interface=unknown
-freerdp_interface=unknown
-event_interface=unknown
+have_freerdp=disabled
 RDP_LIBS=
 
-# libfreerdp-cache
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for glyph_cache_register_callbacks in -lfreerdp-cache" >&5
-$as_echo_n "checking for glyph_cache_register_callbacks in -lfreerdp-cache... " >&6; }
-if ${ac_cv_lib_freerdp_cache_glyph_cache_register_callbacks+:} false; then :
+# Check whether --with-rdp was given.
+if test "${with_rdp+set}" = set; then :
+  withval=$with_rdp;
+else
+  with_rdp=check
+fi
+
+
+if test "x$with_rdp" != "xno"
+then
+    have_winpr=yes
+    have_freerdp=yes
+    have_disp=yes
+    legacy_freerdp_extensions=no
+    rdpsettings_interface=unknown
+    rdpsettings_audioplayback=yes
+    rdpsettings_deviceredirection=yes
+    freerdp_interface=unknown
+    event_interface=unknown
+
+    # libfreerdp-core / libfreerdp
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for freerdp_new in -lfreerdp-core" >&5
+$as_echo_n "checking for freerdp_new in -lfreerdp-core... " >&6; }
+if ${ac_cv_lib_freerdp_core_freerdp_new+:} false; then :
   $as_echo_n "(cached) " >&6
 else
   ac_check_lib_save_LIBS=$LIBS
-LIBS="-lfreerdp-cache  $LIBS"
+LIBS="-lfreerdp-core  $LIBS"
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -13432,51 +14110,36 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 #ifdef __cplusplus
 extern "C"
 #endif
-char glyph_cache_register_callbacks ();
+char freerdp_new ();
 int
 main ()
 {
-return glyph_cache_register_callbacks ();
+return freerdp_new ();
   ;
   return 0;
 }
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
-  ac_cv_lib_freerdp_cache_glyph_cache_register_callbacks=yes
+  ac_cv_lib_freerdp_core_freerdp_new=yes
 else
-  ac_cv_lib_freerdp_cache_glyph_cache_register_callbacks=no
+  ac_cv_lib_freerdp_core_freerdp_new=no
 fi
 rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
 LIBS=$ac_check_lib_save_LIBS
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_freerdp_cache_glyph_cache_register_callbacks" >&5
-$as_echo "$ac_cv_lib_freerdp_cache_glyph_cache_register_callbacks" >&6; }
-if test "x$ac_cv_lib_freerdp_cache_glyph_cache_register_callbacks" = xyes; then :
-  RDP_LIBS="$RDP_LIBS -lfreerdp-cache"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_freerdp_core_freerdp_new" >&5
+$as_echo "$ac_cv_lib_freerdp_core_freerdp_new" >&6; }
+if test "x$ac_cv_lib_freerdp_core_freerdp_new" = xyes; then :
+  RDP_LIBS="$RDP_LIBS -lfreerdp-core"
 else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
-  --------------------------------------------
-   Unable to find libfreerdp-cache
-   RDP will be disabled.
-  --------------------------------------------" >&5
-$as_echo "$as_me: WARNING:
-  --------------------------------------------
-   Unable to find libfreerdp-cache
-   RDP will be disabled.
-  --------------------------------------------" >&2;}
-              have_freerdp=no
-fi
-
-
-# libfreerdp-core
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for freerdp_new in -lfreerdp-core" >&5
-$as_echo_n "checking for freerdp_new in -lfreerdp-core... " >&6; }
-if ${ac_cv_lib_freerdp_core_freerdp_new+:} false; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for freerdp_new in -lfreerdp" >&5
+$as_echo_n "checking for freerdp_new in -lfreerdp... " >&6; }
+if ${ac_cv_lib_freerdp_freerdp_new+:} false; then :
   $as_echo_n "(cached) " >&6
 else
   ac_check_lib_save_LIBS=$LIBS
-LIBS="-lfreerdp-core  $LIBS"
+LIBS="-lfreerdp  $LIBS"
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -13496,41 +14159,92 @@ return freerdp_new ();
 }
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
-  ac_cv_lib_freerdp_core_freerdp_new=yes
+  ac_cv_lib_freerdp_freerdp_new=yes
 else
-  ac_cv_lib_freerdp_core_freerdp_new=no
+  ac_cv_lib_freerdp_freerdp_new=no
 fi
 rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
 LIBS=$ac_check_lib_save_LIBS
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_freerdp_core_freerdp_new" >&5
-$as_echo "$ac_cv_lib_freerdp_core_freerdp_new" >&6; }
-if test "x$ac_cv_lib_freerdp_core_freerdp_new" = xyes; then :
-  RDP_LIBS="$RDP_LIBS -lfreerdp-core"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_freerdp_freerdp_new" >&5
+$as_echo "$ac_cv_lib_freerdp_freerdp_new" >&6; }
+if test "x$ac_cv_lib_freerdp_freerdp_new" = xyes; then :
+  RDP_LIBS="$RDP_LIBS -lfreerdp -lfreerdp-client"
 else
   { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
   --------------------------------------------
-   Unable to find libfreerdp-core
+   Unable to find libfreerdp-core / libfreerdp
    RDP will be disabled.
   --------------------------------------------" >&5
 $as_echo "$as_me: WARNING:
   --------------------------------------------
-   Unable to find libfreerdp-core
+   Unable to find libfreerdp-core / libfreerdp
    RDP will be disabled.
   --------------------------------------------" >&2;}
-              have_freerdp=no
+                  have_freerdp=no
+fi
+
+fi
+
+fi
+
+
+# libfreerdp-cache
+if test "x${have_freerdp}" = "xyes"
+then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for glyph_cache_register_callbacks in -lfreerdp-cache" >&5
+$as_echo_n "checking for glyph_cache_register_callbacks in -lfreerdp-cache... " >&6; }
+if ${ac_cv_lib_freerdp_cache_glyph_cache_register_callbacks+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lfreerdp-cache  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char glyph_cache_register_callbacks ();
+int
+main ()
+{
+return glyph_cache_register_callbacks ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_freerdp_cache_glyph_cache_register_callbacks=yes
+else
+  ac_cv_lib_freerdp_cache_glyph_cache_register_callbacks=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_freerdp_cache_glyph_cache_register_callbacks" >&5
+$as_echo "$ac_cv_lib_freerdp_cache_glyph_cache_register_callbacks" >&6; }
+if test "x$ac_cv_lib_freerdp_cache_glyph_cache_register_callbacks" = xyes; then :
+  RDP_LIBS="$RDP_LIBS -lfreerdp-cache"
 fi
 
+fi
 
-# libfreerdp-channels (1.0) / libfreerdp-client (1.1+)
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for freerdp_channels_new in -lfreerdp-client" >&5
+# libfreerdp-channels (1.0) / libfreerdp-client + libfreerdp-core (1.1)
+if test "x${have_freerdp}" = "xyes"
+then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for freerdp_channels_new in -lfreerdp-client" >&5
 $as_echo_n "checking for freerdp_channels_new in -lfreerdp-client... " >&6; }
 if ${ac_cv_lib_freerdp_client_freerdp_channels_new+:} false; then :
   $as_echo_n "(cached) " >&6
 else
   ac_check_lib_save_LIBS=$LIBS
-LIBS="-lfreerdp-client  $LIBS"
+LIBS="-lfreerdp-client -lfreerdp-core $LIBS"
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -13601,26 +14315,17 @@ fi
 $as_echo "$ac_cv_lib_freerdp_channels_freerdp_channels_new" >&6; }
 if test "x$ac_cv_lib_freerdp_channels_freerdp_channels_new" = xyes; then :
   RDP_LIBS="$RDP_LIBS -lfreerdp-channels"
-                            legacy_freerdp_extensions=yes
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
-  --------------------------------------------
-   Unable to find libfreerdp-client / libfreerdp-channels
-   RDP will be disabled.
-  --------------------------------------------" >&5
-$as_echo "$as_me: WARNING:
-  --------------------------------------------
-   Unable to find libfreerdp-client / libfreerdp-channels
-   RDP will be disabled.
-  --------------------------------------------" >&2;}
-                            have_freerdp=no
+                               legacy_freerdp_extensions=yes
 fi
 
 fi
 
+fi
 
 # libfreerdp-utils
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for svc_plugin_init in -lfreerdp-utils" >&5
+if test "x${have_freerdp}" = "xyes"
+then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for svc_plugin_init in -lfreerdp-utils" >&5
 $as_echo_n "checking for svc_plugin_init in -lfreerdp-utils... " >&6; }
 if ${ac_cv_lib_freerdp_utils_svc_plugin_init+:} false; then :
   $as_echo_n "(cached) " >&6
@@ -13658,23 +14363,14 @@ fi
 $as_echo "$ac_cv_lib_freerdp_utils_svc_plugin_init" >&6; }
 if test "x$ac_cv_lib_freerdp_utils_svc_plugin_init" = xyes; then :
   RDP_LIBS="$RDP_LIBS -lfreerdp-utils"
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
-  --------------------------------------------
-   Unable to find libfreerdp-utils
-   RDP will be disabled.
-  --------------------------------------------" >&5
-$as_echo "$as_me: WARNING:
-  --------------------------------------------
-   Unable to find libfreerdp-utils
-   RDP will be disabled.
-  --------------------------------------------" >&2;}
-              have_freerdp=no
 fi
 
+fi
 
 # libfreerdp-codec
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for freerdp_image_convert in -lfreerdp-codec" >&5
+if test "x${have_freerdp}" = "xyes"
+then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for freerdp_image_convert in -lfreerdp-codec" >&5
 $as_echo_n "checking for freerdp_image_convert in -lfreerdp-codec... " >&6; }
 if ${ac_cv_lib_freerdp_codec_freerdp_image_convert+:} false; then :
   $as_echo_n "(cached) " >&6
@@ -13712,23 +14408,36 @@ fi
 $as_echo "$ac_cv_lib_freerdp_codec_freerdp_image_convert" >&6; }
 if test "x$ac_cv_lib_freerdp_codec_freerdp_image_convert" = xyes; then :
   RDP_LIBS="$RDP_LIBS -lfreerdp-codec"
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
-  --------------------------------------------
-   Unable to find libfreerdp-codec
-   RDP will be disabled.
-  --------------------------------------------" >&5
-$as_echo "$as_me: WARNING:
-  --------------------------------------------
-   Unable to find libfreerdp-codec
-   RDP will be disabled.
-  --------------------------------------------" >&2;}
-              have_freerdp=no
 fi
 
+fi
+
+# Available color conversion functions
+if test "x${have_freerdp}" = "xyes"
+then
+    ac_fn_c_check_decl "$LINENO" "freerdp_convert_gdi_order_color" "ac_cv_have_decl_freerdp_convert_gdi_order_color" "#include <freerdp/codec/color.h>
+"
+if test "x$ac_cv_have_decl_freerdp_convert_gdi_order_color" = xyes; then :
+
+$as_echo "#define HAVE_FREERDP_CONVERT_GDI_ORDER_COLOR /**/" >>confdefs.h
+
+fi
+
+
+    ac_fn_c_check_decl "$LINENO" "freerdp_color_convert_drawing_order_color_to_gdi_color" "ac_cv_have_decl_freerdp_color_convert_drawing_order_color_to_gdi_color" "#include <freerdp/codec/color.h>
+"
+if test "x$ac_cv_have_decl_freerdp_color_convert_drawing_order_color_to_gdi_color" = xyes; then :
+
+$as_echo "#define HAVE_FREERDP_COLOR_CONVERT_DRAWING_ORDER_COLOR_TO_GDI_COLOR /**/" >>confdefs.h
+
+fi
+
+fi
 
 # Check for interval polling in plugins
-ac_fn_c_check_member "$LINENO" "rdpSvcPlugin" "interval_ms" "ac_cv_member_rdpSvcPlugin_interval_ms" "#include <freerdp/utils/svc_plugin.h>
+if test "x${have_freerdp}" = "xyes"
+then
+    ac_fn_c_check_member "$LINENO" "rdpSvcPlugin" "interval_ms" "ac_cv_member_rdpSvcPlugin_interval_ms" "#include <freerdp/utils/svc_plugin.h>
 "
 if test "x$ac_cv_member_rdpSvcPlugin_interval_ms" = xyes; then :
 
@@ -13739,9 +14448,12 @@ _ACEOF
 
 fi
 
+fi
 
 # Keyboard layout header
-for ac_header in freerdp/locale/keyboard.h
+if test "x${have_freerdp}" = "xyes"
+then
+    for ac_header in freerdp/locale/keyboard.h
 do :
   ac_fn_c_check_header_mongrel "$LINENO" "freerdp/locale/keyboard.h" "ac_cv_header_freerdp_locale_keyboard_h" "$ac_includes_default"
 if test "x$ac_cv_header_freerdp_locale_keyboard_h" = xyes; then :
@@ -13769,7 +14481,7 @@ $as_echo "$as_me: WARNING:
    Unable to find keyboard layout headers
    RDP will be disabled.
   --------------------------------------------" >&2;}
-                                    have_freerdp=no
+                                        have_freerdp=no
 fi
 
 done
@@ -13778,9 +14490,12 @@ fi
 
 done
 
+fi
 
 # New headers defining addins
-for ac_header in freerdp/addin.h freerdp/client/channels.h
+if test "x${have_freerdp}" = "xyes"
+then
+    for ac_header in freerdp/addin.h freerdp/client/channels.h
 do :
   as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
 ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
@@ -13793,12 +14508,15 @@ fi
 
 done
 
+fi
 
 # Header defining cliprdr
-for ac_header in freerdp/client/cliprdr.h
+if test "x${have_freerdp}" = "xyes"
+then
+    for ac_header in freerdp/client/cliprdr.h
 do :
   ac_fn_c_check_header_compile "$LINENO" "freerdp/client/cliprdr.h" "ac_cv_header_freerdp_client_cliprdr_h" "#include <winpr/wtypes.h>
-                  #include <winpr/collections.h>
+                      #include <winpr/collections.h>
 "
 if test "x$ac_cv_header_freerdp_client_cliprdr_h" = xyes; then :
   cat >>confdefs.h <<_ACEOF
@@ -13826,35 +14544,99 @@ $as_echo "$as_me: WARNING:
    Unable to find cliprdr headers
    RDP will be disabled.
   --------------------------------------------" >&2;}
-                                    have_freerdp=no
+                                        have_freerdp=no
+fi
+
+done
+
 fi
 
 done
 
 fi
 
+# Header defining display update channel
+if test "x${have_freerdp}" = "xyes"
+then
+    for ac_header in freerdp/client/disp.h
+do :
+  ac_fn_c_check_header_compile "$LINENO" "freerdp/client/disp.h" "ac_cv_header_freerdp_client_disp_h" "#include <winpr/wtypes.h>
+                      #include <winpr/collections.h>
+"
+if test "x$ac_cv_header_freerdp_client_disp_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_FREERDP_CLIENT_DISP_H 1
+_ACEOF
+
+$as_echo "#define HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT /**/" >>confdefs.h
+
+                     ac_fn_c_check_member "$LINENO" "rdpSettings" "SupportDisplayControl" "ac_cv_member_rdpSettings_SupportDisplayControl" "#include <freerdp/freerdp.h>
+"
+if test "x$ac_cv_member_rdpSettings_SupportDisplayControl" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_RDPSETTINGS_SUPPORTDISPLAYCONTROL 1
+_ACEOF
+
+
+fi
+
+else
+  have_disp=no
+fi
+
 done
 
+fi
+
+# Support for "PubSub" event system
+if test "x${have_freerdp}" = "xyes"
+then
+    ac_fn_c_check_decl "$LINENO" "PubSub_SubscribeChannelConnected" "ac_cv_have_decl_PubSub_SubscribeChannelConnected" "#include <freerdp/event.h>
+"
+if test "x$ac_cv_have_decl_PubSub_SubscribeChannelConnected" = xyes; then :
+
+$as_echo "#define HAVE_FREERDP_EVENT_PUBSUB /**/" >>confdefs.h
+
+fi
+
+fi
 
-ac_fn_c_check_decl "$LINENO" "freerdp_register_addin_provider" "ac_cv_have_decl_freerdp_register_addin_provider" "#include <freerdp/addin.h>
+# Addin registration variations
+if test "x${have_freerdp}" = "xyes"
+then
+    ac_fn_c_check_decl "$LINENO" "freerdp_register_addin_provider" "ac_cv_have_decl_freerdp_register_addin_provider" "#include <freerdp/addin.h>
 "
 if test "x$ac_cv_have_decl_freerdp_register_addin_provider" = xyes; then :
-  $as_echo "#define HAVE_FREERDP_REGISTER_ADDIN_PROVIDER 1" >>confdefs.h
+
+$as_echo "#define HAVE_FREERDP_REGISTER_ADDIN_PROVIDER /**/" >>confdefs.h
+
+fi
+
+
+    ac_fn_c_check_decl "$LINENO" "freerdp_channels_global_init" "ac_cv_have_decl_freerdp_channels_global_init" "#include <freerdp/channels/channels.h>
+"
+if test "x$ac_cv_have_decl_freerdp_channels_global_init" = xyes; then :
+
+$as_echo "#define HAVE_FREERDP_CHANNELS_GLOBAL_INIT /**/" >>confdefs.h
 
 fi
 
+fi
 
 #
 # FreeRDP: WinPR
 #
 
 # Check for stream support via WinPR
-ac_fn_c_check_header_mongrel "$LINENO" "winpr/stream.h" "ac_cv_header_winpr_stream_h" "$ac_includes_default"
+if test "x${have_freerdp}" = "xyes"
+then
+    ac_fn_c_check_header_mongrel "$LINENO" "winpr/stream.h" "ac_cv_header_winpr_stream_h" "$ac_includes_default"
 if test "x$ac_cv_header_winpr_stream_h" = xyes; then :
 
 else
   have_winpr=no,
-                 ac_fn_c_check_decl "$LINENO" "stream_write_uint8" "ac_cv_have_decl_stream_write_uint8" "#include <freerdp/utils/stream.h>
+                     ac_fn_c_check_decl "$LINENO" "stream_write_uint8" "ac_cv_have_decl_stream_write_uint8" "#include <freerdp/utils/stream.h>
 "
 if test "x$ac_cv_have_decl_stream_write_uint8" = xyes; then :
 
@@ -13869,20 +14651,23 @@ $as_echo "$as_me: WARNING:
    Unable to find stream support
    RDP will be disabled.
   --------------------------------------------" >&2;}
-                               have_freerdp=no
+                                   have_freerdp=no
 fi
 
 fi
 
 
+fi
 
 # Check for types in WinPR
-ac_fn_c_check_header_mongrel "$LINENO" "winpr/wtypes.h" "ac_cv_header_winpr_wtypes_h" "$ac_includes_default"
+if test "x${have_freerdp}" = "xyes"
+then
+    ac_fn_c_check_header_mongrel "$LINENO" "winpr/wtypes.h" "ac_cv_header_winpr_wtypes_h" "$ac_includes_default"
 if test "x$ac_cv_header_winpr_wtypes_h" = xyes; then :
 
 else
   have_winpr=no,
-                 ac_fn_c_check_header_mongrel "$LINENO" "freerdp/types.h" "ac_cv_header_freerdp_types_h" "$ac_includes_default"
+                     ac_fn_c_check_header_mongrel "$LINENO" "freerdp/types.h" "ac_cv_header_freerdp_types_h" "$ac_includes_default"
 if test "x$ac_cv_header_freerdp_types_h" = xyes; then :
 
 else
@@ -13896,17 +14681,19 @@ $as_echo "$as_me: WARNING:
    Unable to find type definitions
    RDP will be disabled.
   --------------------------------------------" >&2;}
-                                  have_freerdp=no
+                                      have_freerdp=no
 fi
 
 
 fi
 
 
+fi
 
-if test "x${have_winpr}" = "xyes"
+if test "x${have_freerdp}" = "xyes" -a "x${have_winpr}" = "xyes"
 then
-    $as_echo "#define ENABLE_WINPR 1" >>confdefs.h
+
+$as_echo "#define ENABLE_WINPR /**/" >>confdefs.h
 
 fi
 
@@ -13914,8 +14701,10 @@ fi
 # FreeRDP: freerdp
 #
 
-# Check for current (as of 1.1) freerdp interface
-ac_fn_c_check_member "$LINENO" "freerdp" "ContextSize" "ac_cv_member_freerdp_ContextSize" "#include <freerdp/freerdp.h>
+if test "x${have_freerdp}" = "xyes"
+then
+    # Check for current (as of 1.1) freerdp interface
+    ac_fn_c_check_member "$LINENO" "freerdp" "ContextSize" "ac_cv_member_freerdp_ContextSize" "#include <freerdp/freerdp.h>
 "
 if test "x$ac_cv_member_freerdp_ContextSize" = xyes; then :
 
@@ -13927,10 +14716,10 @@ freerdp_interface=stable
 fi
 
 
-# If not current, check for legacy interface
-if test "x${freerdp_interface}" = "xunknown"
-then
-    ac_fn_c_check_member "$LINENO" "freerdp" "context_size" "ac_cv_member_freerdp_context_size" "#include <freerdp/freerdp.h>
+    # If not current, check for legacy interface
+    if test "x${freerdp_interface}" = "xunknown"
+    then
+        ac_fn_c_check_member "$LINENO" "freerdp" "context_size" "ac_cv_member_freerdp_context_size" "#include <freerdp/freerdp.h>
 "
 if test "x$ac_cv_member_freerdp_context_size" = xyes; then :
 
@@ -13941,14 +14730,15 @@ _ACEOF
 freerdp_interface=legacy
 fi
 
-fi
+    fi
+
+    # Set defines based on interface type, warn if unknown
+    if test "x${freerdp_interface}" = "xlegacy"; then
 
-# Set defines based on interface type, warn if unknown
-if test "x${freerdp_interface}" = "xlegacy"; then
-    $as_echo "#define LEGACY_FREERDP 1" >>confdefs.h
+$as_echo "#define LEGACY_FREERDP /**/" >>confdefs.h
 
-elif test "x${freerdp_interface}" = "xunknown"; then
-    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
+    elif test "x${freerdp_interface}" = "xunknown"; then
+        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
   --------------------------------------------
    Unknown FreeRDP interface
    RDP will be disabled.
@@ -13958,15 +14748,18 @@ $as_echo "$as_me: WARNING:
    Unknown FreeRDP interface
    RDP will be disabled.
   --------------------------------------------" >&2;}
-    have_freerdp=no
+        have_freerdp=no
+    fi
 fi
 
 #
 # FreeRDP: rdpSettings
 #
 
-# Check for current (as of 1.1) rdpSettings interface
-ac_fn_c_check_member "$LINENO" "rdpSettings" "Width" "ac_cv_member_rdpSettings_Width" "#include <freerdp/freerdp.h>
+if test "x${have_freerdp}" = "xyes"
+then
+    # Check for current (as of 1.1) rdpSettings interface
+    ac_fn_c_check_member "$LINENO" "rdpSettings" "Width" "ac_cv_member_rdpSettings_Width" "#include <freerdp/freerdp.h>
 "
 if test "x$ac_cv_member_rdpSettings_Width" = xyes; then :
 
@@ -13986,6 +14779,36 @@ _ACEOF
 
 rdpsettings_interface=stable
 fi
+ac_fn_c_check_member "$LINENO" "rdpSettings" "FastPathInput" "ac_cv_member_rdpSettings_FastPathInput" "#include <freerdp/freerdp.h>
+"
+if test "x$ac_cv_member_rdpSettings_FastPathInput" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_RDPSETTINGS_FASTPATHINPUT 1
+_ACEOF
+
+rdpsettings_interface=stable
+fi
+ac_fn_c_check_member "$LINENO" "rdpSettings" "FastPathOutput" "ac_cv_member_rdpSettings_FastPathOutput" "#include <freerdp/freerdp.h>
+"
+if test "x$ac_cv_member_rdpSettings_FastPathOutput" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_RDPSETTINGS_FASTPATHOUTPUT 1
+_ACEOF
+
+rdpsettings_interface=stable
+fi
+ac_fn_c_check_member "$LINENO" "rdpSettings" "SendPreconnectionPdu" "ac_cv_member_rdpSettings_SendPreconnectionPdu" "#include <freerdp/freerdp.h>
+"
+if test "x$ac_cv_member_rdpSettings_SendPreconnectionPdu" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_RDPSETTINGS_SENDPRECONNECTIONPDU 1
+_ACEOF
+
+rdpsettings_interface=stable
+fi
 ac_fn_c_check_member "$LINENO" "rdpSettings" "OrderSupport" "ac_cv_member_rdpSettings_OrderSupport" "#include <freerdp/freerdp.h>
 "
 if test "x$ac_cv_member_rdpSettings_OrderSupport" = xyes; then :
@@ -13998,10 +14821,10 @@ rdpsettings_interface=stable
 fi
 
 
-# If not current, check for legacy interface
-if test "x${rdpsettings_interface}" = "xunknown"
-then
-    ac_fn_c_check_member "$LINENO" "rdpSettings" "width" "ac_cv_member_rdpSettings_width" "#include <freerdp/freerdp.h>
+    # If not current, check for legacy interface
+    if test "x${rdpsettings_interface}" = "xunknown"
+    then
+        ac_fn_c_check_member "$LINENO" "rdpSettings" "width" "ac_cv_member_rdpSettings_width" "#include <freerdp/freerdp.h>
 "
 if test "x$ac_cv_member_rdpSettings_width" = xyes; then :
 
@@ -14025,181 +14848,690 @@ ac_fn_c_check_member "$LINENO" "rdpSettings" "order_support" "ac_cv_member_rdpSe
 "
 if test "x$ac_cv_member_rdpSettings_order_support" = xyes; then :
 
-cat >>confdefs.h <<_ACEOF
-#define HAVE_RDPSETTINGS_ORDER_SUPPORT 1
+cat >>confdefs.h <<_ACEOF
+#define HAVE_RDPSETTINGS_ORDER_SUPPORT 1
+_ACEOF
+
+rdpsettings_interface=legacy
+fi
+
+    fi
+
+    # Set defines based on interface type, warn if unknown
+    if test "x${rdpsettings_interface}" = "xlegacy"; then
+
+$as_echo "#define LEGACY_RDPSETTINGS /**/" >>confdefs.h
+
+
+        # Legacy interface may not have AudioPlayback settings
+        ac_fn_c_check_member "$LINENO" "rdpSettings" "audio_playback" "ac_cv_member_rdpSettings_audio_playback" "#include <freerdp/freerdp.h>
+"
+if test "x$ac_cv_member_rdpSettings_audio_playback" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_RDPSETTINGS_AUDIO_PLAYBACK 1
+_ACEOF
+
+
+else
+  rdpsettings_audioplayback=no
+fi
+
+
+        # Legacy interface may not have DeviceRedirection settings
+        ac_fn_c_check_member "$LINENO" "rdpSettings" "device_redirection" "ac_cv_member_rdpSettings_device_redirection" "#include <freerdp/freerdp.h>
+"
+if test "x$ac_cv_member_rdpSettings_device_redirection" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_RDPSETTINGS_DEVICE_REDIRECTION 1
+_ACEOF
+
+
+else
+  rdpsettings_deviceredirection=no
+fi
+
+
+    elif test "x${rdpsettings_interface}" = "xunknown"; then
+        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
+  --------------------------------------------
+   Unknown rdpSettings interface
+   RDP will be disabled.
+  --------------------------------------------" >&5
+$as_echo "$as_me: WARNING:
+  --------------------------------------------
+   Unknown rdpSettings interface
+   RDP will be disabled.
+  --------------------------------------------" >&2;}
+        have_freerdp=no
+    fi
+fi
+
+# Activate audio playback settings if present
+if test "x${have_freerdp}" = "xyes" -a "x${rdpsettings_audioplayback}" = "xyes"; then
+
+$as_echo "#define HAVE_RDPSETTINGS_AUDIOPLAYBACK /**/" >>confdefs.h
+
+fi
+
+# Activate device redirection settings if present
+if test "x${have_freerdp}" = "xyes" -a "x${rdpsettings_deviceredirection}" = "xyes"; then
+
+$as_echo "#define HAVE_RDPSETTINGS_DEVICEREDIRECTION /**/" >>confdefs.h
+
+fi
+
+# Check if the type CHANNEL_ENTRY_POINTS_FREERDP exists, if not define it to CHANNEL_ENTRY_POINTS_EX
+if test "x${have_freerdp}" = "xyes"
+then
+    ac_fn_c_check_type "$LINENO" "CHANNEL_ENTRY_POINTS_FREERDP" "ac_cv_type_CHANNEL_ENTRY_POINTS_FREERDP" "#include <freerdp/svc.h>
+"
+if test "x$ac_cv_type_CHANNEL_ENTRY_POINTS_FREERDP" = xyes; then :
+
+else
+
+$as_echo "#define CHANNEL_ENTRY_POINTS_FREERDP CHANNEL_ENTRY_POINTS_EX" >>confdefs.h
+
+fi
+
+fi
+
+# Check if the freerdp version header exists
+if test "x${have_freerdp}" = "xyes"
+then
+    for ac_header in freerdp/version.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "freerdp/version.h" "ac_cv_header_freerdp_version_h" "$ac_includes_default"
+if test "x$ac_cv_header_freerdp_version_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_FREERDP_VERSION_H 1
+_ACEOF
+
+fi
+
+done
+
+fi
+
+#
+# FreeRDP: rdpBitmap
+#
+
+if test "x${have_freerdp}" = "xyes"
+then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether rdpBitmap.Decompress() requires the codec_id" >&5
+$as_echo_n "checking whether rdpBitmap.Decompress() requires the codec_id... " >&6; }
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <winpr/wtypes.h>
+                                        #include <freerdp/freerdp.h>
+                                        void __decompress(rdpContext* context,
+                                                          rdpBitmap* bitmap,
+                                                          UINT8* data,
+                                                          int width,
+                                                          int height,
+                                                          int bpp,
+                                                          int length,
+                                                          BOOL compressed,
+                                                          int codec_id);
+                                        rdpBitmap b = { .Decompress = __decompress };
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+$as_echo "#define LEGACY_RDPBITMAP /**/" >>confdefs.h
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+#
+# FreeRDP: Decompression function variants
+#
+
+# Check whether interleaved_decompress() can handle the palette
+if test "x${have_freerdp}" = "xyes"
+then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether interleaved_decompress() accepts an additional palette parameter" >&5
+$as_echo_n "checking whether interleaved_decompress() accepts an additional palette parameter... " >&6; }
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <freerdp/codec/interleaved.h>
+
+                                        int main() {
+                                            BYTE* palette = NULL;
+                                            interleaved_decompress(NULL, NULL, 0, 0, NULL, 0, 0, 0, 0, 0, 0, palette);
+                                        }
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+$as_echo "#define INTERLEAVED_DECOMPRESS_TAKES_PALETTE /**/" >>confdefs.h
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+# Check whether planar_decompress() will handle flipping
+if test "x${have_freerdp}" = "xyes"
+then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether planar_decompress() can flip" >&5
+$as_echo_n "checking whether planar_decompress() can flip... " >&6; }
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <freerdp/codec/planar.h>
+
+                                        int main() {
+                                            BOOL* flip = TRUE;
+                                            planar_decompress(NULL, NULL, 0, NULL, 0, 0, 0, 0, 0, 0, flip);
+                                        }
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+$as_echo "#define PLANAR_DECOMPRESS_CAN_FLIP /**/" >>confdefs.h
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+#
+# FreeRDP: rdpContext
+#
+
+# Check for rdpContext.codecs
+if test "x${have_freerdp}" = "xyes"
+then
+    ac_fn_c_check_member "$LINENO" "rdpContext" "codecs" "ac_cv_member_rdpContext_codecs" "#include <freerdp/freerdp.h>
+"
+if test "x$ac_cv_member_rdpContext_codecs" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_RDPCONTEXT_CODECS 1
+_ACEOF
+
+
+$as_echo "#define FREERDP_BITMAP_REQUIRES_ALIGNED_MALLOC /**/" >>confdefs.h
+
+fi
+
+fi
+
+#
+# FreeRDP: rdpPalette
+#
+
+if test "x${have_freerdp}" = "xyes"
+then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether rdpPalette.entries is static" >&5
+$as_echo_n "checking whether rdpPalette.entries is static... " >&6; }
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <freerdp/update.h>
+                                        rdpPalette p;
+                                        PALETTE_ENTRY* foo = p.entries;
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+$as_echo "#define LEGACY_RDPPALETTE /**/" >>confdefs.h
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+#
+# FreeRDP: rdpPointer
+#
+
+# Check for SetDefault and SetNull members of rdpPointer
+if test "x${have_freerdp}" = "xyes"
+then
+    ac_fn_c_check_member "$LINENO" "rdpPointer" "SetDefault" "ac_cv_member_rdpPointer_SetDefault" "#include <freerdp/freerdp.h>
+"
+if test "x$ac_cv_member_rdpPointer_SetDefault" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_RDPPOINTER_SETDEFAULT 1
+_ACEOF
+
+
+fi
+ac_fn_c_check_member "$LINENO" "rdpPointer" "SetNull" "ac_cv_member_rdpPointer_SetNull" "#include <freerdp/freerdp.h>
+"
+if test "x$ac_cv_member_rdpPointer_SetNull" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_RDPPOINTER_SETNULL 1
+_ACEOF
+
+
+fi
+
+fi
+
+#
+# FreeRDP: wMessage / RDP_EVENT
+#
+
+# Check for current (as of 1.1) wMessage interface
+if test "x${have_freerdp}" = "xyes"
+then
+    ac_fn_c_check_member "$LINENO" "wMessage" "id" "ac_cv_member_wMessage_id" "#include <winpr/collections.h>
+"
+if test "x$ac_cv_member_wMessage_id" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_WMESSAGE_ID 1
+_ACEOF
+
+event_interface=stable
+fi
+
+
+    # If not current, check for legacy (RDP_EVENT) interface
+    if test "x${event_interface}" = "xunknown"
+    then
+        ac_fn_c_check_member "$LINENO" "RDP_EVENT" "event_class" "ac_cv_member_RDP_EVENT_event_class" "#include <freerdp/types.h>
+"
+if test "x$ac_cv_member_RDP_EVENT_event_class" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_RDP_EVENT_EVENT_CLASS 1
+_ACEOF
+
+event_interface=legacy
+fi
+
+    fi
+
+    # Set defines based on interface type, warn if unknown
+    if test "x${event_interface}" = "xlegacy"; then
+
+$as_echo "#define LEGACY_EVENT /**/" >>confdefs.h
+
+    elif test "x${event_interface}" = "xunknown"; then
+        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
+  --------------------------------------------
+   Unknown event interface
+   RDP will be disabled.
+  --------------------------------------------" >&5
+$as_echo "$as_me: WARNING:
+  --------------------------------------------
+   Unknown event interface
+   RDP will be disabled.
+  --------------------------------------------" >&2;}
+        have_freerdp=no
+    fi
+fi
+
+ if test "x${legacy_freerdp_extensions}" = "xyes"; then
+  LEGACY_FREERDP_EXTENSIONS_TRUE=
+  LEGACY_FREERDP_EXTENSIONS_FALSE='#'
+else
+  LEGACY_FREERDP_EXTENSIONS_TRUE='#'
+  LEGACY_FREERDP_EXTENSIONS_FALSE=
+fi
+
+ if test "x${have_disp}" = "xyes"; then
+  ENABLE_DISPLAY_UPDATE_TRUE=
+  ENABLE_DISPLAY_UPDATE_FALSE='#'
+else
+  ENABLE_DISPLAY_UPDATE_TRUE='#'
+  ENABLE_DISPLAY_UPDATE_FALSE=
+fi
+
+ if test "x${have_winpr}"   = "xyes"; then
+  ENABLE_WINPR_TRUE=
+  ENABLE_WINPR_FALSE='#'
+else
+  ENABLE_WINPR_TRUE='#'
+  ENABLE_WINPR_FALSE=
+fi
+
+ if test "x${have_freerdp}" = "xyes"; then
+  ENABLE_RDP_TRUE=
+  ENABLE_RDP_FALSE='#'
+else
+  ENABLE_RDP_TRUE='#'
+  ENABLE_RDP_FALSE=
+fi
+
+
+
+
+#
+# libssh2
+#
+
+have_libssh2=disabled
+SSH_LIBS=
+
+# Check whether --with-ssh was given.
+if test "${with_ssh+set}" = set; then :
+  withval=$with_ssh;
+else
+  with_ssh=check
+fi
+
+
+# Check whether --enable-ssh_agent was given.
+if test "${enable_ssh_agent+set}" = set; then :
+  enableval=$enable_ssh_agent; enable_ssh_agent=yes
+fi
+
+
+if test "x$with_ssh" != "xno"
+then
+    have_libssh2=yes
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libssh2_session_init_ex in -lssh2" >&5
+$as_echo_n "checking for libssh2_session_init_ex in -lssh2... " >&6; }
+if ${ac_cv_lib_ssh2_libssh2_session_init_ex+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lssh2  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char libssh2_session_init_ex ();
+int
+main ()
+{
+return libssh2_session_init_ex ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_ssh2_libssh2_session_init_ex=yes
+else
+  ac_cv_lib_ssh2_libssh2_session_init_ex=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssh2_libssh2_session_init_ex" >&5
+$as_echo "$ac_cv_lib_ssh2_libssh2_session_init_ex" >&6; }
+if test "x$ac_cv_lib_ssh2_libssh2_session_init_ex" = xyes; then :
+  SSH_LIBS="$SSH_LIBS -lssh2"
+else
+  have_libssh2=no
+fi
+
+fi
+
+ if test "x${have_libssh2}"  = "xyes" \
+                                       -a "x${have_ssl}"      = "xyes"; then
+  ENABLE_COMMON_SSH_TRUE=
+  ENABLE_COMMON_SSH_FALSE='#'
+else
+  ENABLE_COMMON_SSH_TRUE='#'
+  ENABLE_COMMON_SSH_FALSE=
+fi
+
+if test -z "$ENABLE_COMMON_SSH_TRUE"; then :
+
+$as_echo "#define ENABLE_COMMON_SSH /**/" >>confdefs.h
+
+fi
+
+ if test "x${have_libssh2}"  = "xyes" \
+                                -a "x${have_terminal}" = "xyes" \
+                                -a "x${have_ssl}"      = "xyes"; then
+  ENABLE_SSH_TRUE=
+  ENABLE_SSH_FALSE='#'
+else
+  ENABLE_SSH_TRUE='#'
+  ENABLE_SSH_FALSE=
+fi
+
+
+
+
+#
+# Underlying crypto library usage of libssh2
+#
+
+if test "x${have_libssh2}" = "xyes"
+then
+
+    # Whether libssh2 was built against libgcrypt
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for gcry_control in -lssh2" >&5
+$as_echo_n "checking for gcry_control in -lssh2... " >&6; }
+if ${ac_cv_lib_ssh2_gcry_control+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lssh2  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char gcry_control ();
+int
+main ()
+{
+return gcry_control ();
+  ;
+  return 0;
+}
 _ACEOF
-
-rdpsettings_interface=legacy
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_ssh2_gcry_control=yes
+else
+  ac_cv_lib_ssh2_gcry_control=no
 fi
-
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
 fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssh2_gcry_control" >&5
+$as_echo "$ac_cv_lib_ssh2_gcry_control" >&6; }
+if test "x$ac_cv_lib_ssh2_gcry_control" = xyes; then :
+  ac_fn_c_check_header_mongrel "$LINENO" "gcrypt.h" "ac_cv_header_gcrypt_h" "$ac_includes_default"
+if test "x$ac_cv_header_gcrypt_h" = xyes; then :
 
-# Set defines based on interface type, warn if unknown
-if test "x${rdpsettings_interface}" = "xlegacy"; then
-    $as_echo "#define LEGACY_RDPSETTINGS 1" >>confdefs.h
+$as_echo "#define LIBSSH2_USES_GCRYPT /**/" >>confdefs.h
 
-elif test "x${rdpsettings_interface}" = "xunknown"; then
-    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
   --------------------------------------------
-   Unknown rdpSettings interface
-   RDP will be disabled.
+   libssh2 appears to be built against libgcrypt, but the libgcrypt headers
+   could not be found. SSH will be disabled.
   --------------------------------------------" >&5
 $as_echo "$as_me: WARNING:
   --------------------------------------------
-   Unknown rdpSettings interface
-   RDP will be disabled.
+   libssh2 appears to be built against libgcrypt, but the libgcrypt headers
+   could not be found. SSH will be disabled.
   --------------------------------------------" >&2;}
-    have_freerdp=no
+                                           have_libssh2=no
 fi
 
-#
-# FreeRDP: rdpBitmap
-#
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether rdpBitmap.Decompress() requires the codec_id" >&5
-$as_echo_n "checking whether rdpBitmap.Decompress() requires the codec_id... " >&6; }
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#include <winpr/wtypes.h>
-                                    #include <freerdp/freerdp.h>
-                                    void __decompress(rdpContext* context,
-                                                      rdpBitmap* bitmap,
-                                                      UINT8* data,
-                                                      int width,
-                                                      int height,
-                                                      int bpp,
-                                                      int length,
-                                                      BOOL compressed,
-                                                      int codec_id);
-                                    rdpBitmap b = { .Decompress = __decompress };
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-                   $as_echo "#define LEGACY_RDPBITMAP 1" >>confdefs.h
 
 fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-
-#
-# FreeRDP: rdpPalette
-#
 
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether rdpPalette.entries is static" >&5
-$as_echo_n "checking whether rdpPalette.entries is static... " >&6; }
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#include <freerdp/update.h>
-                                    rdpPalette p;
-                                    PALETTE_ENTRY* foo = p.entries;
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-                   $as_echo "#define LEGACY_RDPPALETTE 1" >>confdefs.h
 
 fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 
 #
-# FreeRDP: wMessage / RDP_EVENT
+# Agent forwarding support within libssh2
 #
 
-# Check for current (as of 1.1) wMessage interface
-ac_fn_c_check_member "$LINENO" "wMessage" "id" "ac_cv_member_wMessage_id" "#include <winpr/collections.h>
-"
-if test "x$ac_cv_member_wMessage_id" = xyes; then :
-
-cat >>confdefs.h <<_ACEOF
-#define HAVE_WMESSAGE_ID 1
-_ACEOF
+have_ssh_agent=no
+if test "x${have_libssh2}" = "xyes" -a "x${enable_ssh_agent}" = "xyes"
+then
 
-event_interface=stable
+    ac_fn_c_check_decl "$LINENO" "libssh2_channel_request_auth_agent" "ac_cv_have_decl_libssh2_channel_request_auth_agent" "#include <libssh2.h>
+"
+if test "x$ac_cv_have_decl_libssh2_channel_request_auth_agent" = xyes; then :
+  have_ssh_agent=yes
 fi
 
 
-# If not current, check for legacy (RDP_EVENT) interface
-if test "x${event_interface}" = "xunknown"
-then
-    ac_fn_c_check_member "$LINENO" "RDP_EVENT" "event_class" "ac_cv_member_RDP_EVENT_event_class" "#include <freerdp/types.h>
-"
-if test "x$ac_cv_member_RDP_EVENT_event_class" = xyes; then :
+    if test "x${have_ssh_agent}" = "xno"
+    then
+        as_fn_error $? "
+      --------------------------------------------
+       Agent forwarding support was requested but no such support was found
+       in libssh2.
+      --------------------------------------------" "$LINENO" 5
+    else
 
-cat >>confdefs.h <<_ACEOF
-#define HAVE_RDP_EVENT_EVENT_CLASS 1
-_ACEOF
+$as_echo "#define ENABLE_SSH_AGENT /**/" >>confdefs.h
+
+    fi
 
-event_interface=legacy
 fi
 
+ if test "x${have_ssh_agent}"   = "xyes" \
+	      -a "x${enable_ssh_agent}" = "xyes"; then
+  ENABLE_SSH_AGENT_TRUE=
+  ENABLE_SSH_AGENT_FALSE='#'
+else
+  ENABLE_SSH_AGENT_TRUE='#'
+  ENABLE_SSH_AGENT_FALSE=
 fi
 
-# Set defines based on interface type, warn if unknown
-if test "x${event_interface}" = "xlegacy"; then
-    $as_echo "#define LEGACY_EVENT 1" >>confdefs.h
 
-elif test "x${event_interface}" = "xunknown"; then
-    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
-  --------------------------------------------
-   Unknown event interface
-   RDP will be disabled.
-  --------------------------------------------" >&5
-$as_echo "$as_me: WARNING:
-  --------------------------------------------
-   Unknown event interface
-   RDP will be disabled.
-  --------------------------------------------" >&2;}
-    have_freerdp=no
-fi
+#
+# libtelnet
+#
 
+have_libtelnet=disabled
+TELNET_LIBS=
 
- if test "x${legacy_freerdp_extensions}" = "xyes"; then
-  LEGACY_FREERDP_EXTENSIONS_TRUE=
-  LEGACY_FREERDP_EXTENSIONS_FALSE='#'
+# Check whether --with-telnet was given.
+if test "${with_telnet+set}" = set; then :
+  withval=$with_telnet;
 else
-  LEGACY_FREERDP_EXTENSIONS_TRUE='#'
-  LEGACY_FREERDP_EXTENSIONS_FALSE=
+  with_telnet=check
 fi
 
- if test "x${have_winpr}"   = "xyes"; then
-  ENABLE_WINPR_TRUE=
-  ENABLE_WINPR_FALSE='#'
+
+if test "x$with_telnet" != "xno"
+then
+    have_libtelnet=yes
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for telnet_init in -ltelnet" >&5
+$as_echo_n "checking for telnet_init in -ltelnet... " >&6; }
+if ${ac_cv_lib_telnet_telnet_init+:} false; then :
+  $as_echo_n "(cached) " >&6
 else
-  ENABLE_WINPR_TRUE='#'
-  ENABLE_WINPR_FALSE=
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ltelnet  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char telnet_init ();
+int
+main ()
+{
+return telnet_init ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_telnet_telnet_init=yes
+else
+  ac_cv_lib_telnet_telnet_init=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_telnet_telnet_init" >&5
+$as_echo "$ac_cv_lib_telnet_telnet_init" >&6; }
+if test "x$ac_cv_lib_telnet_telnet_init" = xyes; then :
+  TELNET_LIBS="$TELNET_LIBS -ltelnet"
+else
+  have_libtelnet=no
 fi
 
- if test "x${have_freerdp}" = "xyes"; then
-  ENABLE_RDP_TRUE=
-  ENABLE_RDP_FALSE='#'
+fi
+
+ if test "x${have_libtelnet}"  = "xyes" \
+                                   -a "x${have_terminal}" = "xyes"; then
+  ENABLE_TELNET_TRUE=
+  ENABLE_TELNET_FALSE='#'
 else
-  ENABLE_RDP_TRUE='#'
-  ENABLE_RDP_FALSE=
+  ENABLE_TELNET_TRUE='#'
+  ENABLE_TELNET_FALSE=
 fi
 
 
 
 
 #
-# libssh
+# libwebp
 #
 
-have_libssh=yes
-SSH_LIBS=
+have_webp=disabled
+WEBP_LIBS=
+
+# Check whether --with-webp was given.
+if test "${with_webp+set}" = set; then :
+  withval=$with_webp;
+else
+  with_webp=check
+fi
+
+
+if test "x$with_webp" != "xno"
+then
+    have_webp=yes
+
+    ac_fn_c_check_header_mongrel "$LINENO" "webp/encode.h" "ac_cv_header_webp_encode_h" "$ac_includes_default"
+if test "x$ac_cv_header_webp_encode_h" = xyes; then :
+
+else
+  have_webp=no
+fi
 
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ssh_new in -lssh" >&5
-$as_echo_n "checking for ssh_new in -lssh... " >&6; }
-if ${ac_cv_lib_ssh_ssh_new+:} false; then :
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for WebPEncode in -lwebp" >&5
+$as_echo_n "checking for WebPEncode in -lwebp... " >&6; }
+if ${ac_cv_lib_webp_WebPEncode+:} false; then :
   $as_echo_n "(cached) " >&6
 else
   ac_check_lib_save_LIBS=$LIBS
-LIBS="-lssh  $LIBS"
+LIBS="-lwebp  $LIBS"
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -14209,44 +15541,64 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 #ifdef __cplusplus
 extern "C"
 #endif
-char ssh_new ();
+char WebPEncode ();
 int
 main ()
 {
-return ssh_new ();
+return WebPEncode ();
   ;
   return 0;
 }
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
-  ac_cv_lib_ssh_ssh_new=yes
+  ac_cv_lib_webp_WebPEncode=yes
 else
-  ac_cv_lib_ssh_ssh_new=no
+  ac_cv_lib_webp_WebPEncode=no
 fi
 rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
 LIBS=$ac_check_lib_save_LIBS
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssh_ssh_new" >&5
-$as_echo "$ac_cv_lib_ssh_ssh_new" >&6; }
-if test "x$ac_cv_lib_ssh_ssh_new" = xyes; then :
-  SSH_LIBS="$SSH_LIBS -lssh"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_webp_WebPEncode" >&5
+$as_echo "$ac_cv_lib_webp_WebPEncode" >&6; }
+if test "x$ac_cv_lib_webp_WebPEncode" = xyes; then :
+  WEBP_LIBS="$WEBP_LIBS -lwebp"
 else
-  have_libssh=no
+  have_webp=no
 fi
 
- if test "x${have_libssh}" = "xyes" -a "x${have_pango}" = "xyes"; then
-  ENABLE_SSH_TRUE=
-  ENABLE_SSH_FALSE='#'
+
+    if test "x${have_webp}" = "xno"
+    then
+        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
+  --------------------------------------------
+   Unable to find libwebp.
+   Images will not be encoded using WebP.
+  --------------------------------------------" >&5
+$as_echo "$as_me: WARNING:
+  --------------------------------------------
+   Unable to find libwebp.
+   Images will not be encoded using WebP.
+  --------------------------------------------" >&2;}
+    else
+
+$as_echo "#define ENABLE_WEBP /**/" >>confdefs.h
+
+    fi
+fi
+
+ if test "x${have_webp}" = "xyes"; then
+  ENABLE_WEBP_TRUE=
+  ENABLE_WEBP_FALSE='#'
 else
-  ENABLE_SSH_TRUE='#'
-  ENABLE_SSH_FALSE=
+  ENABLE_WEBP_TRUE='#'
+  ENABLE_WEBP_FALSE=
 fi
 
 
 
 
-ac_config_files="$ac_config_files Makefile tests/Makefile src/libguac/Makefile src/guacd/Makefile src/protocols/rdp/Makefile src/protocols/ssh/Makefile src/protocols/vnc/Makefile"
+ac_config_files="$ac_config_files Makefile tests/Makefile src/common/Makefile src/common-ssh/Makefile src/terminal/Makefile src/libguac/Makefile src/guacd/Makefile src/protocols/rdp/Makefile src/protocols/ssh/Makefile src/protocols/telnet/Makefile src/protocols/vnc/Makefile"
 
 cat >confcache <<\_ACEOF
 # This file is a shell script that caches the results of configure
@@ -14338,43 +15690,7 @@ test "x$prefix" = xNONE && prefix=$ac_default_prefix
 # Let make expand exec_prefix.
 test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
 
-# Transform confdefs.h into DEFS.
-# Protect against shell expansion while executing Makefile rules.
-# Protect against Makefile macro expansion.
-#
-# If the first sed substitution is executed (which looks for macros that
-# take arguments), then branch to the quote section.  Otherwise,
-# look for a macro that doesn't take arguments.
-ac_script='
-:mline
-/\\$/{
- N
- s,\\\n,,
- b mline
-}
-t clear
-:clear
-s/^[	 ]*#[	 ]*define[	 ][	 ]*\([^	 (][^	 (]*([^)]*)\)[	 ]*\(.*\)/-D\1=\2/g
-t quote
-s/^[	 ]*#[	 ]*define[	 ][	 ]*\([^	 ][^	 ]*\)[	 ]*\(.*\)/-D\1=\2/g
-t quote
-b any
-:quote
-s/[	 `~#$^&*(){}\\|;'\''"<>?]/\\&/g
-s/\[/\\&/g
-s/\]/\\&/g
-s/\$/$$/g
-H
-:any
-${
-	g
-	s/^\n//
-	s/\n/ /g
-	p
-}
-'
-DEFS=`sed -n "$ac_script" confdefs.h`
-
+DEFS=-DHAVE_CONFIG_H
 
 ac_libobjs=
 ac_ltlibobjs=
@@ -14393,6 +15709,14 @@ LIBOBJS=$ac_libobjs
 LTLIBOBJS=$ac_ltlibobjs
 
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5
+$as_echo_n "checking that generated files are newer than configure... " >&6; }
+   if test -n "$am_sleep_pid"; then
+     # Hide warnings about reused PIDs.
+     wait $am_sleep_pid 2>/dev/null
+   fi
+   { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5
+$as_echo "done" >&6; }
  if test -n "$EXEEXT"; then
   am__EXEEXT_TRUE=
   am__EXEEXT_FALSE='#'
@@ -14429,6 +15753,10 @@ if test -z "${ENABLE_PULSE_TRUE}" && test -z "${ENABLE_PULSE_FALSE}"; then
   as_fn_error $? "conditional \"ENABLE_PULSE\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${ENABLE_TERMINAL_TRUE}" && test -z "${ENABLE_TERMINAL_FALSE}"; then
+  as_fn_error $? "conditional \"ENABLE_TERMINAL\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${ENABLE_VNC_TRUE}" && test -z "${ENABLE_VNC_FALSE}"; then
   as_fn_error $? "conditional \"ENABLE_VNC\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -14437,6 +15765,10 @@ if test -z "${LEGACY_FREERDP_EXTENSIONS_TRUE}" && test -z "${LEGACY_FREERDP_EXTE
   as_fn_error $? "conditional \"LEGACY_FREERDP_EXTENSIONS\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${ENABLE_DISPLAY_UPDATE_TRUE}" && test -z "${ENABLE_DISPLAY_UPDATE_FALSE}"; then
+  as_fn_error $? "conditional \"ENABLE_DISPLAY_UPDATE\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${ENABLE_WINPR_TRUE}" && test -z "${ENABLE_WINPR_FALSE}"; then
   as_fn_error $? "conditional \"ENABLE_WINPR\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -14445,10 +15777,26 @@ if test -z "${ENABLE_RDP_TRUE}" && test -z "${ENABLE_RDP_FALSE}"; then
   as_fn_error $? "conditional \"ENABLE_RDP\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${ENABLE_COMMON_SSH_TRUE}" && test -z "${ENABLE_COMMON_SSH_FALSE}"; then
+  as_fn_error $? "conditional \"ENABLE_COMMON_SSH\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${ENABLE_SSH_TRUE}" && test -z "${ENABLE_SSH_FALSE}"; then
   as_fn_error $? "conditional \"ENABLE_SSH\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${ENABLE_SSH_AGENT_TRUE}" && test -z "${ENABLE_SSH_AGENT_FALSE}"; then
+  as_fn_error $? "conditional \"ENABLE_SSH_AGENT\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${ENABLE_TELNET_TRUE}" && test -z "${ENABLE_TELNET_FALSE}"; then
+  as_fn_error $? "conditional \"ENABLE_TELNET\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${ENABLE_WEBP_TRUE}" && test -z "${ENABLE_WEBP_FALSE}"; then
+  as_fn_error $? "conditional \"ENABLE_WEBP\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 
 : "${CONFIG_STATUS=./config.status}"
 ac_write_fail=0
@@ -14846,7 +16194,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by guacamole-server $as_me 0.8.3, which was
+This file was extended by guacamole-server $as_me 0.9.9, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -14864,11 +16212,15 @@ case $ac_config_files in *"
 "*) set x $ac_config_files; shift; ac_config_files=$*;;
 esac
 
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
 
 
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 # Files that config.status was made for.
 config_files="$ac_config_files"
+config_headers="$ac_config_headers"
 config_commands="$ac_config_commands"
 
 _ACEOF
@@ -14890,10 +16242,15 @@ Usage: $0 [OPTION]... [TAG]...
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
                    instantiate the configuration file FILE
+      --header=FILE[:TEMPLATE]
+                   instantiate the configuration header FILE
 
 Configuration files:
 $config_files
 
+Configuration headers:
+$config_headers
+
 Configuration commands:
 $config_commands
 
@@ -14903,7 +16260,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-guacamole-server config.status 0.8.3
+guacamole-server config.status 0.9.9
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
@@ -14960,7 +16317,18 @@ do
     esac
     as_fn_append CONFIG_FILES " '$ac_optarg'"
     ac_need_defaults=false;;
-  --he | --h |  --help | --hel | -h )
+  --header | --heade | --head | --hea )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --he | --h)
+    # Conflict between --help and --header
+    as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+  --help | --hel | -h )
     $as_echo "$ac_cs_usage"; exit ;;
   -q | -quiet | --quiet | --quie | --qui | --qu | --q \
   | -silent | --silent | --silen | --sile | --sil | --si | --s)
@@ -15300,12 +16668,17 @@ do
   case $ac_config_target in
     "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
     "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;;
+    "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
     "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
     "tests/Makefile") CONFIG_FILES="$CONFIG_FILES tests/Makefile" ;;
+    "src/common/Makefile") CONFIG_FILES="$CONFIG_FILES src/common/Makefile" ;;
+    "src/common-ssh/Makefile") CONFIG_FILES="$CONFIG_FILES src/common-ssh/Makefile" ;;
+    "src/terminal/Makefile") CONFIG_FILES="$CONFIG_FILES src/terminal/Makefile" ;;
     "src/libguac/Makefile") CONFIG_FILES="$CONFIG_FILES src/libguac/Makefile" ;;
     "src/guacd/Makefile") CONFIG_FILES="$CONFIG_FILES src/guacd/Makefile" ;;
     "src/protocols/rdp/Makefile") CONFIG_FILES="$CONFIG_FILES src/protocols/rdp/Makefile" ;;
     "src/protocols/ssh/Makefile") CONFIG_FILES="$CONFIG_FILES src/protocols/ssh/Makefile" ;;
+    "src/protocols/telnet/Makefile") CONFIG_FILES="$CONFIG_FILES src/protocols/telnet/Makefile" ;;
     "src/protocols/vnc/Makefile") CONFIG_FILES="$CONFIG_FILES src/protocols/vnc/Makefile" ;;
 
   *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
@@ -15319,6 +16692,7 @@ done
 # bizarre bug on SunOS 4.1.3.
 if $ac_need_defaults; then
   test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
   test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
 fi
 
@@ -15507,8 +16881,116 @@ fi
 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 fi # test -n "$CONFIG_FILES"
 
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+  ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+  if test -z "$ac_tt"; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any.  Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[	 ]*#[	 ]*define[	 ][	 ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[	 ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[	 ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[	 ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[	 ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  for (key in D) D_is_set[key] = 1
+  FS = ""
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+  line = \$ 0
+  split(line, arg, " ")
+  if (arg[1] == "#") {
+    defundef = arg[2]
+    mac1 = arg[3]
+  } else {
+    defundef = substr(arg[1], 2)
+    mac1 = arg[2]
+  }
+  split(mac1, mac2, "(") #)
+  macro = mac2[1]
+  prefix = substr(line, 1, index(line, defundef) - 1)
+  if (D_is_set[macro]) {
+    # Preserve the white space surrounding the "#".
+    print prefix "define", macro P[macro] D[macro]
+    next
+  } else {
+    # Replace #undef with comments.  This is necessary, for example,
+    # in the case of _POSIX_SOURCE, which is predefined and required
+    # on some systems where configure will not decide to define it.
+    if (defundef == "undef") {
+      print "/*", prefix defundef, macro, "*/"
+      next
+    }
+  }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+  as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
 
-eval set X "  :F $CONFIG_FILES      :C $CONFIG_COMMANDS"
+eval set X "  :F $CONFIG_FILES  :H $CONFIG_HEADERS    :C $CONFIG_COMMANDS"
 shift
 for ac_tag
 do
@@ -15727,7 +17209,64 @@ which seems to be undefined.  Please make sure it is defined" >&2;}
   esac \
   || as_fn_error $? "could not create $ac_file" "$LINENO" 5
  ;;
-
+  :H)
+  #
+  # CONFIG_HEADER
+  #
+  if test x"$ac_file" != x-; then
+    {
+      $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+    } >"$ac_tmp/config.h" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+    else
+      rm -f "$ac_file"
+      mv "$ac_tmp/config.h" "$ac_file" \
+	|| as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    fi
+  else
+    $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+      || as_fn_error $? "could not create -" "$LINENO" 5
+  fi
+# Compute "$ac_file"'s index in $config_headers.
+_am_arg="$ac_file"
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+  case $_am_header in
+    $_am_arg | $_am_arg:* )
+      break ;;
+    * )
+      _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+  esac
+done
+echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" ||
+$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$_am_arg" : 'X\(//\)[^/]' \| \
+	 X"$_am_arg" : 'X\(//\)$' \| \
+	 X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$_am_arg" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`/stamp-h$_am_stamp_count
+ ;;
 
   :C)  { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5
 $as_echo "$as_me: executing $ac_file commands" >&6;}
@@ -15737,7 +17276,7 @@ $as_echo "$as_me: executing $ac_file commands" >&6;}
 
   case $ac_file$ac_mode in
     "depfiles":C) test x"$AMDEP_TRUE" != x"" || {
-  # Autoconf 2.62 quotes --file arguments for eval, but not when files
+  # Older Autoconf quotes --file arguments for eval, but not when files
   # are listed without --file.  Let's play safe and only enable the eval
   # if we detect the quoting.
   case $CONFIG_FILES in
@@ -15750,7 +17289,7 @@ $as_echo "$as_me: executing $ac_file commands" >&6;}
     # Strip MF so we end up with the name of the file.
     mf=`echo "$mf" | sed -e 's/:.*$//'`
     # Check whether this is an Automake generated Makefile or not.
-    # We used to match only the files named `Makefile.in', but
+    # We used to match only the files named 'Makefile.in', but
     # some people rename them; so instead we look at the file content.
     # Grep'ing the first line is not enough: some people post-process
     # each Makefile.in and add a new line on top of each file to say so.
@@ -15784,21 +17323,19 @@ $as_echo X"$mf" |
       continue
     fi
     # Extract the definition of DEPDIR, am__include, and am__quote
-    # from the Makefile without running `make'.
+    # from the Makefile without running 'make'.
     DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
     test -z "$DEPDIR" && continue
     am__include=`sed -n 's/^am__include = //p' < "$mf"`
-    test -z "am__include" && continue
+    test -z "$am__include" && continue
     am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
-    # When using ansi2knr, U may be empty or an underscore; expand it
-    U=`sed -n 's/^U = //p' < "$mf"`
     # Find all dependency output files, they are included files with
     # $(DEPDIR) in their names.  We invoke sed twice because it is the
     # simplest approach to changing $(DEPDIR) to its actual value in the
     # expansion.
     for file in `sed -n "
       s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
-	 sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+	 sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do
       # Make sure the directory exists.
       test -f "$dirpart/$file" && continue
       fdir=`$as_dirname -- "$file" ||
@@ -16510,6 +18047,11 @@ if test -z "$ENABLE_SSH_TRUE"; then :
 else
   build_ssh=no
 fi
+if test -z "$ENABLE_TELNET_TRUE"; then :
+  build_telnet=yes
+else
+  build_telnet=no
+fi
 if test -z "$ENABLE_VNC_TRUE"; then :
   build_vnc=yes
 else
@@ -16531,16 +18073,19 @@ $PACKAGE_NAME version $PACKAGE_VERSION
 
      freerdp ............. ${have_freerdp}
      pango ............... ${have_pango}
-     libssh .............. ${have_libssh}
+     libssh2 ............. ${have_libssh2}
      libssl .............. ${have_ssl}
+     libtelnet ........... ${have_libtelnet}
      libVNCServer ........ ${have_libvncserver}
      libvorbis ........... ${have_vorbis}
      libpulse ............ ${have_pulse}
+     libwebp ............. ${have_webp}
 
    Protocol support:
 
       RDP ....... ${build_rdp}
       SSH ....... ${build_ssh}
+      Telnet .... ${build_telnet}
       VNC ....... ${build_vnc}
 
    Init scripts: ${build_init}
diff --git a/configure.ac b/configure.ac
index 598a452..1617c29 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,47 +1,34 @@
-# ***** BEGIN LICENSE BLOCK *****
-# Version: MPL 1.1/GPL 2.0/LGPL 2.1
 #
-# The contents of this file are subject to the Mozilla Public License Version
-# 1.1 (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.mozilla.org/MPL/
+# Copyright (C) 2015 Glyptodon LLC
 #
-# Software distributed under the License is distributed on an "AS IS" basis,
-# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-# for the specific language governing rights and limitations under the
-# License.
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
 #
-# The Original Code is guacamole-server.
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
 #
-# The Initial Developer of the Original Code is
-# Michael Jumper.
-# Portions created by the Initial Developer are Copyright (C) 2010
-# the Initial Developer. All Rights Reserved.
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
 #
-# Contributor(s):
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either the GNU General Public License Version 2 or later (the "GPL"), or
-# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-# in which case the provisions of the GPL or the LGPL are applicable instead
-# of those above. If you wish to allow use of your version of this file only
-# under the terms of either the GPL or the LGPL, and not to allow others to
-# use your version of this file under the terms of the MPL, indicate your
-# decision by deleting the provisions above and replace them with the notice
-# and other provisions required by the GPL or the LGPL. If you do not delete
-# the provisions above, a recipient may use your version of this file under
-# the terms of any one of the MPL, the GPL or the LGPL.
-#
-# ***** END LICENSE BLOCK *****
-
 
 AC_PREREQ([2.61])
-AC_INIT([guacamole-server], [0.8.3])
-AM_INIT_AUTOMAKE([-Wall -Werror foreign])
+AC_INIT([guacamole-server], [0.9.9])
+AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects])
+AM_SILENT_RULES([yes])
 
 LT_PREREQ([2.2])
 LT_INIT([dlopen])
 
+AC_CONFIG_HEADER([config.h])
 AC_CONFIG_MACRO_DIR([m4])
 
 # Programs
@@ -53,24 +40,47 @@ AC_PROG_LIBTOOL
 AC_CHECK_HEADERS([fcntl.h stdlib.h string.h sys/socket.h time.h sys/time.h syslog.h unistd.h cairo/cairo.h pngstruct.h])
 
 # Source characteristics
-AC_DEFINE([_POSIX_C_SOURCE], [199309L], [Uses POSIX APIs])
-AC_DEFINE([_BSD_SOURCE],     [],        [Uses BSD APIs])
+AC_DEFINE([_XOPEN_SOURCE], [700], [Uses X/Open and POSIX APIs])
 
-# libdl
-AC_CHECK_LIB([dl], [dlopen], [DL_LIBS=-ldl],
-             AC_MSG_ERROR("libdl is required for loading client plugins"))
+# Check for whether math library is required
+AC_CHECK_LIB([m], [cos],
+             [MATH_LIBS=-lm],
+             [AC_CHECK_DECL([cos],,
+                            AC_MSG_ERROR("Complex math functions are missing and no libm was found")
+                            [#include <math.h>])])
 
 # libpng
 AC_CHECK_LIB([png], [png_write_png], [PNG_LIBS=-lpng],
              AC_MSG_ERROR("libpng is required for writing png messages"))
 
+# libjpeg
+AC_CHECK_LIB([jpeg], [jpeg_start_compress], [JPEG_LIBS=-ljpeg],
+             AC_MSG_ERROR("libjpeg is required for writing jpeg messages"))
+
 # Cairo
 AC_CHECK_LIB([cairo], [cairo_create], [CAIRO_LIBS=-lcairo],
              AC_MSG_ERROR("Cairo is required for drawing instructions"))
 
 # libpthread
 AC_CHECK_LIB([pthread], [pthread_create], [PTHREAD_LIBS=-lpthread
-              AC_DEFINE([HAVE_LIBPTHREAD])])
+              AC_DEFINE([HAVE_LIBPTHREAD],,
+                        [Whether libpthread was found])])
+
+# OSSP UUID
+AC_CHECK_LIB([ossp-uuid], [uuid_make], [UUID_LIBS=-lossp-uuid],
+             AC_CHECK_LIB([uuid], [uuid_make], [UUID_LIBS=-luuid],
+                          AC_MSG_ERROR("The OSSP UUID library is required")))
+
+# Check for and validate OSSP uuid.h header
+AC_CHECK_HEADERS([ossp/uuid.h])
+AC_CHECK_DECL([uuid_make],,
+              AC_MSG_ERROR("No OSSP uuid.h found in include path"),
+              [#ifdef HAVE_OSSP_UUID_H
+               #include <ossp/uuid.h>
+               #else
+               #include <uuid.h>
+               #endif
+               ])
 
 # cunit
 AC_CHECK_LIB([cunit], [CU_run_test], [CUNIT_LIBS=-lcunit])
@@ -78,21 +88,26 @@ AC_CHECK_LIB([cunit], [CU_run_test], [CUNIT_LIBS=-lcunit])
 # WinSock
 AC_CHECK_LIB([wsock32], [main])
 
-AC_SUBST(DL_LIBS)
+AC_SUBST(LIBADD_DLOPEN)
+AC_SUBST(MATH_LIBS)
 AC_SUBST(PNG_LIBS)
+AC_SUBST(JPEG_LIBS)
 AC_SUBST(CAIRO_LIBS)
 AC_SUBST(PTHREAD_LIBS)
+AC_SUBST(UUID_LIBS)
 AC_SUBST(CUNIT_LIBS)
 
 # Library functions
 AC_CHECK_FUNCS([clock_gettime gettimeofday memmove memset select strdup nanosleep])
 
 AC_CHECK_DECL([png_get_io_ptr],
-	[AC_DEFINE([HAVE_PNG_GET_IO_PTR])],,
+	[AC_DEFINE([HAVE_PNG_GET_IO_PTR],,
+               [Whether png_get_io_ptr() is defined])],,
 	[#include <png.h>])
 
 AC_CHECK_DECL([cairo_format_stride_for_width],
-	[AC_DEFINE([HAVE_CAIRO_FORMAT_STRIDE_FOR_WIDTH])],,
+	[AC_DEFINE([HAVE_CAIRO_FORMAT_STRIDE_FOR_WIDTH],,
+               [Whether cairo_format_stride_for_width() is defined])],,
 	[#include <cairo/cairo.h>])
 
 # Typedefs
@@ -103,7 +118,19 @@ AC_TYPE_SSIZE_T
 AC_SUBST([LIBGUAC_LTLIB],   '$(top_builddir)/src/libguac/libguac.la')
 AC_SUBST([LIBGUAC_INCLUDE], '-I$(top_srcdir)/src/libguac')
 
-# Options
+# Common non-libguac utility library
+AC_SUBST([COMMON_LTLIB],   '$(top_builddir)/src/common/libguac_common.la')
+AC_SUBST([COMMON_INCLUDE], '-I$(top_srcdir)/src/common')
+
+# Common base SSH client
+AC_SUBST([COMMON_SSH_LTLIB],   '$(top_builddir)/src/common-ssh/libguac_common_ssh.la')
+AC_SUBST([COMMON_SSH_INCLUDE], '-I$(top_srcdir)/src/common-ssh')
+
+# Terminal emulator
+AC_SUBST([TERMINAL_LTLIB],   '$(top_builddir)/src/terminal/libguac_terminal.la')
+AC_SUBST([TERMINAL_INCLUDE], '-I$(top_srcdir)/src/terminal $(PANGO_CFLAGS) $(PANGOCAIRO_CFLAGS) $(COMMON_INCLUDE)')
+
+# Init directory
 AC_ARG_WITH(init_dir,
             [AS_HELP_STRING([--with-init-dir=<path>],
                             [install init scripts to the given directory])
@@ -111,28 +138,46 @@ AC_ARG_WITH(init_dir,
 AM_CONDITIONAL([ENABLE_INIT], [test "x${init_dir}" != "x"])
 AC_SUBST(init_dir)
 
+# guacd config file
+AC_ARG_WITH(guacd_conf,
+            [AS_HELP_STRING([--with-guacd-conf=<path>],
+                            [the full path to the guacd config file @<:@default=/etc/guacamole/guacd.conf@:>@])],
+            [guacd_conf=$withval],
+            [guacd_conf=/etc/guacamole/guacd.conf])
+AC_DEFINE_UNQUOTED([GUACD_CONF_FILE], ["$guacd_conf"], [The full path to the guacd config file])
+
 #
 # libssl
 #
 
-have_ssl=yes
+have_ssl=disabled
 SSL_LIBS=
+AC_ARG_WITH([ssl],
+            [AS_HELP_STRING([--with-ssl],
+                            [support SSL encryption @<:@default=check@:>@])],
+            [],
+            [with_ssl=check])
 
-AC_CHECK_HEADER(openssl/ssl.h,, [have_ssl=no])
-AC_CHECK_LIB([ssl], [SSL_CTX_new], [SSL_LIBS="$SSL_LIBS -lssl"], [have_ssl=no])
-AM_CONDITIONAL([ENABLE_SSL], [test "x${have_ssl}" = "xyes"])
-
-if test "x${have_ssl}" = "xno"
+if test "x$with_ssl" != "xno"
 then
-    AC_MSG_WARN([
+    have_ssl=yes
+
+    AC_CHECK_HEADER(openssl/ssl.h,, [have_ssl=no])
+    AC_CHECK_LIB([ssl], [SSL_CTX_new], [SSL_LIBS="$SSL_LIBS -lssl"], [have_ssl=no])
+
+    if test "x${have_ssl}" = "xno"
+    then
+        AC_MSG_WARN([
   --------------------------------------------
    Unable to find libssl.
    guacd will not support SSL connections.
   --------------------------------------------])
-else
-    AC_DEFINE([ENABLE_SSL])
+    else
+        AC_DEFINE([ENABLE_SSL],, [Whether SSL-related support is enabled])
+    fi
 fi
 
+AM_CONDITIONAL([ENABLE_SSL], [test "x${have_ssl}" = "xyes"])
 AC_SUBST(SSL_LIBS)
 
 
@@ -140,69 +185,131 @@ AC_SUBST(SSL_LIBS)
 # Ogg Vorbis
 #
 
-have_vorbis=yes
+have_vorbis=disabled
 VORBIS_LIBS=
+AC_ARG_WITH([vorbis],
+            [AS_HELP_STRING([--with-vorbis],
+                            [support Ogg Vorbis @<:@default=check@:>@])],
+            [],
+            [with_vorbis=check])
 
-AC_CHECK_HEADER(vorbis/vorbisenc.h,, [have_vorbis=no])
-AC_CHECK_LIB([ogg], [ogg_stream_init], [VORBIS_LIBS="$VORBIS_LIBS -logg"], [have_vorbis=no])
-AC_CHECK_LIB([vorbis], [vorbis_block_init], [VORBIS_LIBS="$VORBIS_LIBS -lvorbis"], [have_vorbis=no])
-AC_CHECK_LIB([vorbisenc], [vorbis_encode_init], [VORBIS_LIBS="$VORBIS_LIBS -lvorbisenc"], [have_vorbis=no])
-AM_CONDITIONAL([ENABLE_OGG], [test "x${have_vorbis}" = "xyes"])
-
-if test "x${have_vorbis}" = "xno"
+if test "x$with_vorbis" != "xno"
 then
-    AC_MSG_WARN([
+    have_vorbis=yes
+
+    AC_CHECK_HEADER(vorbis/vorbisenc.h,, [have_vorbis=no])
+    AC_CHECK_LIB([ogg], [ogg_stream_init], [VORBIS_LIBS="$VORBIS_LIBS -logg"], [have_vorbis=no])
+    AC_CHECK_LIB([vorbis], [vorbis_block_init], [VORBIS_LIBS="$VORBIS_LIBS -lvorbis"], [have_vorbis=no])
+    AC_CHECK_LIB([vorbisenc], [vorbis_encode_init], [VORBIS_LIBS="$VORBIS_LIBS -lvorbisenc"], [have_vorbis=no])
+
+    if test "x${have_vorbis}" = "xno"
+    then
+        AC_MSG_WARN([
   --------------------------------------------
    Unable to find libogg / libvorbis / libvorbisenc.
    Sound will not be encoded with Ogg Vorbis.
   --------------------------------------------])
-else
-    AC_DEFINE([ENABLE_OGG])
+    else
+        AC_DEFINE([ENABLE_OGG],,
+                  [Whether support for Ogg Vorbis is enabled])
+    fi
 fi
 
+AM_CONDITIONAL([ENABLE_OGG], [test "x${have_vorbis}" = "xyes"])
 AC_SUBST(VORBIS_LIBS)
 
 #
 # PulseAudio
 #
 
-have_pulse=yes
+have_pulse=disabled
 PULSE_LIBS=
+AC_ARG_WITH([pulse],
+            [AS_HELP_STRING([--with-pulse],
+                            [support PulseAudio @<:@default=check@:>@])],
+            [],
+            [with_pulse=check])
 
-AC_CHECK_LIB([pulse], [pa_context_new], [PULSE_LIBS="$PULSE_LIBS -lpulse"], [have_pulse=no])
-AM_CONDITIONAL([ENABLE_PULSE], [test "x${have_pulse}" = "xyes"])
-
-if test "x${have_pulse}" = "xno"
+if test "x$with_pulse" != "xno"
 then
-    AC_MSG_WARN([
+    have_pulse=yes
+
+    AC_CHECK_LIB([pulse], [pa_context_new], [PULSE_LIBS="$PULSE_LIBS -lpulse"], [have_pulse=no])
+
+    if test "x${have_pulse}" = "xno"
+    then
+        AC_MSG_WARN([
   --------------------------------------------
    Unable to find libpulse
    Sound support for VNC will be disabled
   --------------------------------------------])
-else
-    AC_DEFINE([ENABLE_PULSE])
+    else
+        AC_DEFINE([ENABLE_PULSE],, [Whether PulseAudio support is enabled])
+    fi
 fi
 
+AM_CONDITIONAL([ENABLE_PULSE], [test "x${have_pulse}" = "xyes"])
 AC_SUBST(PULSE_LIBS)
 
 #
 # PANGO
 #
 
-have_pango=yes
-PKG_CHECK_MODULES([PANGO], [pango],, [have_pango=no]);
-PKG_CHECK_MODULES([PANGOCAIRO], [pangocairo],, [have_pango=no]);
+have_pango=disabled
+AC_ARG_WITH([pango],
+            [AS_HELP_STRING([--with-pango],
+                            [support Pango text layout @<:@default=check@:>@])],
+            [],
+            [with_pango=check])
+
+if test "x$with_pango" != "xno"
+then
+    have_pango=yes
+    PKG_CHECK_MODULES([PANGO], [pango],, [have_pango=no]);
+    PKG_CHECK_MODULES([PANGOCAIRO], [pangocairo],, [have_pango=no]);
+fi
+
+#
+# Terminal emulator
+#
+
+have_terminal=disabled
+AC_ARG_WITH([terminal],
+            [AS_HELP_STRING([--with-terminal],
+                            [support text-based protocols @<:@default=check@:>@])],
+            [],
+            [with_terminal=check])
+
+if test "x$with_terminal" != "xno"
+then
+    have_terminal=yes
+    if test "x${have_pango}" = "xno"
+    then
+        have_terminal=no
+    fi
+fi
+
+AM_CONDITIONAL([ENABLE_TERMINAL], [test "x${have_terminal}" = "xyes"])
 
 #
 # libVNCServer
 #
 
-have_libvncserver=yes
+have_libvncserver=disabled
 VNC_LIBS=
+AC_ARG_WITH([vnc],
+            [AS_HELP_STRING([--with-vnc],
+                            [support VNC @<:@default=check@:>@])],
+            [],
+            [with_vnc=check])
 
-AC_CHECK_LIB([vncclient], [rfbInitClient], [VNC_LIBS="$VNC_LIBS -lvncclient"], [have_libvncserver=no])
-AM_CONDITIONAL([ENABLE_VNC], [test "x${have_libvncserver}" = "xyes"])
+if test "x$with_vnc" != "xno"
+then
+    have_libvncserver=yes
+    AC_CHECK_LIB([vncclient], [rfbInitClient], [VNC_LIBS="$VNC_LIBS -lvncclient"], [have_libvncserver=no])
+fi
 
+AM_CONDITIONAL([ENABLE_VNC], [test "x${have_libvncserver}" = "xyes"])
 AC_SUBST(VNC_LIBS)
 
 #
@@ -225,7 +332,34 @@ then
        Support for VNC repeaters will not be built.
       --------------------------------------------])
     else
-        AC_DEFINE([ENABLE_VNC_REPEATER])
+        AC_DEFINE([ENABLE_VNC_REPEATER],,
+                  [Whether support for VNC repeaters is enabled.])
+    fi
+
+fi
+
+#
+# Listening support within libVNCServer
+#
+
+if test "x${have_libvncserver}" = "xyes"
+then
+
+    have_vnc_listen=yes
+    AC_CHECK_DECL([listenForIncomingConnectionsNoFork],
+                  [], [have_vnc_listen=no],
+                  [[#include <rfb/rfbclient.h>]])
+
+    if test "x${have_vnc_listen}" = "xno"
+    then
+        AC_MSG_WARN([
+      --------------------------------------------
+       No listening support found in libvncclient.
+       Support for listen-mode connections will not be built.
+      --------------------------------------------])
+    else
+        AC_DEFINE([ENABLE_VNC_LISTEN],,
+                  [Whether support for listen-mode VNC connections is enabled.])
     fi
 
 fi
@@ -234,288 +368,622 @@ fi
 # FreeRDP
 #
 
-have_winpr=yes
-have_freerdp=yes
-legacy_freerdp_extensions=no
-rdpsettings_interface=unknown
-freerdp_interface=unknown
-event_interface=unknown
+have_freerdp=disabled
 RDP_LIBS=
+AC_ARG_WITH([rdp],
+            [AS_HELP_STRING([--with-rdp],
+                            [support RDP @<:@default=check@:>@])],
+            [],
+            [with_rdp=check])
 
-# libfreerdp-cache
-AC_CHECK_LIB([freerdp-cache], [glyph_cache_register_callbacks],
-             [RDP_LIBS="$RDP_LIBS -lfreerdp-cache"],
-             [AC_MSG_WARN([
+if test "x$with_rdp" != "xno"
+then
+    have_winpr=yes
+    have_freerdp=yes
+    have_disp=yes
+    legacy_freerdp_extensions=no
+    rdpsettings_interface=unknown
+    rdpsettings_audioplayback=yes
+    rdpsettings_deviceredirection=yes
+    freerdp_interface=unknown
+    event_interface=unknown
+
+    # libfreerdp-core / libfreerdp
+    AC_CHECK_LIB([freerdp-core], [freerdp_new],
+                 [RDP_LIBS="$RDP_LIBS -lfreerdp-core"],
+                 [AC_CHECK_LIB([freerdp], [freerdp_new],
+                               [RDP_LIBS="$RDP_LIBS -lfreerdp -lfreerdp-client"],
+                               [AC_MSG_WARN([
   --------------------------------------------
-   Unable to find libfreerdp-cache
+   Unable to find libfreerdp-core / libfreerdp
    RDP will be disabled.
   --------------------------------------------])
-              have_freerdp=no])
+                  have_freerdp=no])])
+fi
 
-# libfreerdp-core
-AC_CHECK_LIB([freerdp-core], [freerdp_new],
-             [RDP_LIBS="$RDP_LIBS -lfreerdp-core"],
-             [AC_MSG_WARN([
-  --------------------------------------------
-   Unable to find libfreerdp-core
-   RDP will be disabled.
-  --------------------------------------------])
-              have_freerdp=no])
-
-# libfreerdp-channels (1.0) / libfreerdp-client (1.1+)
-AC_CHECK_LIB([freerdp-client], [freerdp_channels_new],
-             [RDP_LIBS="$RDP_LIBS -lfreerdp-client"],
-             [AC_CHECK_LIB([freerdp-channels], [freerdp_channels_new],
-                           [RDP_LIBS="$RDP_LIBS -lfreerdp-channels"
-                            legacy_freerdp_extensions=yes],
-                           [AC_MSG_WARN([
-  --------------------------------------------
-   Unable to find libfreerdp-client / libfreerdp-channels
-   RDP will be disabled.
-  --------------------------------------------])
-                            have_freerdp=no])])
+
+# libfreerdp-cache
+if test "x${have_freerdp}" = "xyes"
+then
+    AC_CHECK_LIB([freerdp-cache], [glyph_cache_register_callbacks],
+                 [RDP_LIBS="$RDP_LIBS -lfreerdp-cache"])
+fi
+
+# libfreerdp-channels (1.0) / libfreerdp-client + libfreerdp-core (1.1)
+if test "x${have_freerdp}" = "xyes"
+then
+    AC_CHECK_LIB([freerdp-client], [freerdp_channels_new],
+                 [RDP_LIBS="$RDP_LIBS -lfreerdp-client"],
+                 [AC_CHECK_LIB([freerdp-channels], [freerdp_channels_new],
+                               [RDP_LIBS="$RDP_LIBS -lfreerdp-channels"
+                               legacy_freerdp_extensions=yes])],
+                 [-lfreerdp-core])
+fi
 
 # libfreerdp-utils
-AC_CHECK_LIB([freerdp-utils], [svc_plugin_init],
-             [RDP_LIBS="$RDP_LIBS -lfreerdp-utils"],
-             [AC_MSG_WARN([
-  --------------------------------------------
-   Unable to find libfreerdp-utils
-   RDP will be disabled.
-  --------------------------------------------])
-              have_freerdp=no])
+if test "x${have_freerdp}" = "xyes"
+then
+    AC_CHECK_LIB([freerdp-utils], [svc_plugin_init],
+                 [RDP_LIBS="$RDP_LIBS -lfreerdp-utils"])
+fi
 
 # libfreerdp-codec
-AC_CHECK_LIB([freerdp-codec], [freerdp_image_convert],
-             [RDP_LIBS="$RDP_LIBS -lfreerdp-codec"],
-             [AC_MSG_WARN([
-  --------------------------------------------
-   Unable to find libfreerdp-codec
-   RDP will be disabled.
-  --------------------------------------------])
-              have_freerdp=no])
+if test "x${have_freerdp}" = "xyes"
+then
+    AC_CHECK_LIB([freerdp-codec], [freerdp_image_convert],
+                 [RDP_LIBS="$RDP_LIBS -lfreerdp-codec"])
+fi
+
+# Available color conversion functions
+if test "x${have_freerdp}" = "xyes"
+then
+    AC_CHECK_DECL([freerdp_convert_gdi_order_color],
+        [AC_DEFINE([HAVE_FREERDP_CONVERT_GDI_ORDER_COLOR],,
+                   [Whether freerdp_convert_gdi_order_color() is defined])],,
+        [#include <freerdp/codec/color.h>])
+
+    AC_CHECK_DECL([freerdp_color_convert_drawing_order_color_to_gdi_color],
+        [AC_DEFINE([HAVE_FREERDP_COLOR_CONVERT_DRAWING_ORDER_COLOR_TO_GDI_COLOR],,
+                   [Whether freerdp_color_convert_drawing_order_color_to_gdi_color() is defined])],,
+        [#include <freerdp/codec/color.h>])
+fi
 
 # Check for interval polling in plugins
-AC_CHECK_MEMBERS([rdpSvcPlugin.interval_ms],,,
-                 [[#include <freerdp/utils/svc_plugin.h>]])
+if test "x${have_freerdp}" = "xyes"
+then
+    AC_CHECK_MEMBERS([rdpSvcPlugin.interval_ms],,,
+                     [[#include <freerdp/utils/svc_plugin.h>]])
+fi
 
 # Keyboard layout header
-AC_CHECK_HEADERS([freerdp/locale/keyboard.h],,
-                  AC_CHECK_HEADERS([freerdp/kbd/layouts.h],,
-                                   [AC_MSG_WARN([
+if test "x${have_freerdp}" = "xyes"
+then
+    AC_CHECK_HEADERS([freerdp/locale/keyboard.h],,
+                      [AC_CHECK_HEADERS([freerdp/kbd/layouts.h],,
+                                       [AC_MSG_WARN([
   --------------------------------------------
    Unable to find keyboard layout headers
    RDP will be disabled.
   --------------------------------------------])
-                                    have_freerdp=no]))
+                                        have_freerdp=no])])
+fi
 
 # New headers defining addins
-AC_CHECK_HEADERS([freerdp/addin.h freerdp/client/channels.h])
+if test "x${have_freerdp}" = "xyes"
+then
+    AC_CHECK_HEADERS([freerdp/addin.h freerdp/client/channels.h])
+fi
 
 # Header defining cliprdr
-AC_CHECK_HEADERS([freerdp/client/cliprdr.h],,
-                  AC_CHECK_HEADERS([freerdp/plugins/cliprdr.h],,
-                                   [AC_MSG_WARN([
+if test "x${have_freerdp}" = "xyes"
+then
+    AC_CHECK_HEADERS([freerdp/client/cliprdr.h],,
+                     [AC_CHECK_HEADERS([freerdp/plugins/cliprdr.h],,
+                                       [AC_MSG_WARN([
   --------------------------------------------
    Unable to find cliprdr headers
    RDP will be disabled.
   --------------------------------------------])
-                                    have_freerdp=no],
-                                   [#include <freerdp/types.h>]),
-                 [#include <winpr/wtypes.h>
-                  #include <winpr/collections.h>])
+                                        have_freerdp=no],
+                                       [#include <freerdp/types.h>])],
+                     [#include <winpr/wtypes.h>
+                      #include <winpr/collections.h>])
+fi
+
+# Header defining display update channel
+if test "x${have_freerdp}" = "xyes"
+then
+    AC_CHECK_HEADERS([freerdp/client/disp.h],
+                     [AC_DEFINE([HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT],,
+                                [Whether FreeRDP supports the display update channel])]
+                     [AC_CHECK_MEMBERS([rdpSettings.SupportDisplayControl],,,
+                                       [[#include <freerdp/freerdp.h>]])],
+                     have_disp=no,
+                     [#include <winpr/wtypes.h>
+                      #include <winpr/collections.h>])
+fi
+
+# Support for "PubSub" event system
+if test "x${have_freerdp}" = "xyes"
+then
+    AC_CHECK_DECL([PubSub_SubscribeChannelConnected],
+        [AC_DEFINE([HAVE_FREERDP_EVENT_PUBSUB],,
+                   [Whether this version of FreeRDP provides the PubSub event system])],,
+        [#include <freerdp/event.h>])
+fi
 
-AC_CHECK_DECL([freerdp_register_addin_provider],
-	[AC_DEFINE([HAVE_FREERDP_REGISTER_ADDIN_PROVIDER])],,
-	[#include <freerdp/addin.h>])
+# Addin registration variations
+if test "x${have_freerdp}" = "xyes"
+then
+    AC_CHECK_DECL([freerdp_register_addin_provider],
+        [AC_DEFINE([HAVE_FREERDP_REGISTER_ADDIN_PROVIDER],,
+                   [Whether freerdp_register_addin_provider() is defined])],,
+        [#include <freerdp/addin.h>])
+
+    AC_CHECK_DECL([freerdp_channels_global_init],
+        [AC_DEFINE([HAVE_FREERDP_CHANNELS_GLOBAL_INIT],,
+                   [Whether freerdp_channels_global_init() is defined])],,
+        [#include <freerdp/channels/channels.h>])
+fi
 
 #
 # FreeRDP: WinPR
 #
 
 # Check for stream support via WinPR
-AC_CHECK_HEADER(winpr/stream.h,,
-                [have_winpr=no,
-                 AC_CHECK_DECL([stream_write_uint8],,
-                              [AC_MSG_WARN([
+if test "x${have_freerdp}" = "xyes"
+then
+    AC_CHECK_HEADER(winpr/stream.h,,
+                    [have_winpr=no,
+                     AC_CHECK_DECL([stream_write_uint8],,
+                                  [AC_MSG_WARN([
   --------------------------------------------
    Unable to find stream support
    RDP will be disabled.
   --------------------------------------------])
-                               have_freerdp=no],
-                              [#include <freerdp/utils/stream.h>])])
+                                   have_freerdp=no],
+                                  [#include <freerdp/utils/stream.h>])])
+fi
 
 # Check for types in WinPR
-AC_CHECK_HEADER(winpr/wtypes.h,,
-                [have_winpr=no,
-                 AC_CHECK_HEADER(freerdp/types.h,,
-                                 [AC_MSG_WARN([
+if test "x${have_freerdp}" = "xyes"
+then
+    AC_CHECK_HEADER(winpr/wtypes.h,,
+                    [have_winpr=no,
+                     AC_CHECK_HEADER(freerdp/types.h,,
+                                     [AC_MSG_WARN([
   --------------------------------------------
    Unable to find type definitions
    RDP will be disabled.
   --------------------------------------------])
-                                  have_freerdp=no])])
+                                      have_freerdp=no])])
+fi
 
-if test "x${have_winpr}" = "xyes"
+if test "x${have_freerdp}" = "xyes" -a "x${have_winpr}" = "xyes"
 then
-    AC_DEFINE([ENABLE_WINPR])
+    AC_DEFINE([ENABLE_WINPR],,
+              [Whether library support for WinPR types was found])
 fi
 
 #
 # FreeRDP: freerdp 
 #
 
-# Check for current (as of 1.1) freerdp interface
-AC_CHECK_MEMBERS([freerdp.ContextSize],
-                 [freerdp_interface=stable],,
-                 [[#include <freerdp/freerdp.h>]])
-
-# If not current, check for legacy interface
-if test "x${freerdp_interface}" = "xunknown"
+if test "x${have_freerdp}" = "xyes"
 then
-    AC_CHECK_MEMBERS([freerdp.context_size],
-                     [freerdp_interface=legacy],,
+    # Check for current (as of 1.1) freerdp interface
+    AC_CHECK_MEMBERS([freerdp.ContextSize],
+                     [freerdp_interface=stable],,
                      [[#include <freerdp/freerdp.h>]])
-fi
 
-# Set defines based on interface type, warn if unknown
-if test "x${freerdp_interface}" = "xlegacy"; then
-    AC_DEFINE([LEGACY_FREERDP])
-elif test "x${freerdp_interface}" = "xunknown"; then
-    AC_MSG_WARN([
+    # If not current, check for legacy interface
+    if test "x${freerdp_interface}" = "xunknown"
+    then
+        AC_CHECK_MEMBERS([freerdp.context_size],
+                         [freerdp_interface=legacy],,
+                         [[#include <freerdp/freerdp.h>]])
+    fi
+
+    # Set defines based on interface type, warn if unknown
+    if test "x${freerdp_interface}" = "xlegacy"; then
+        AC_DEFINE([LEGACY_FREERDP],,
+                  [Whether the older version of the FreeRDP API was found])
+    elif test "x${freerdp_interface}" = "xunknown"; then
+        AC_MSG_WARN([
   --------------------------------------------
    Unknown FreeRDP interface
    RDP will be disabled.
   --------------------------------------------])
-    have_freerdp=no
+        have_freerdp=no
+    fi
 fi
 
 #
 # FreeRDP: rdpSettings
 #
 
-# Check for current (as of 1.1) rdpSettings interface
-AC_CHECK_MEMBERS([rdpSettings.Width,
-                  rdpSettings.Height,
-                  rdpSettings.OrderSupport],
-                 [rdpsettings_interface=stable],,
-                 [[#include <freerdp/freerdp.h>]])
-
-# If not current, check for legacy interface
-if test "x${rdpsettings_interface}" = "xunknown"
+if test "x${have_freerdp}" = "xyes"
 then
-    AC_CHECK_MEMBERS([rdpSettings.width,
-                      rdpSettings.height,
-                      rdpSettings.order_support],
-                     [rdpsettings_interface=legacy],,
+    # Check for current (as of 1.1) rdpSettings interface
+    AC_CHECK_MEMBERS([rdpSettings.Width,
+                      rdpSettings.Height,
+                      rdpSettings.FastPathInput,
+                      rdpSettings.FastPathOutput,
+                      rdpSettings.SendPreconnectionPdu,
+                      rdpSettings.OrderSupport],
+                     [rdpsettings_interface=stable],,
                      [[#include <freerdp/freerdp.h>]])
-fi
 
-# Set defines based on interface type, warn if unknown
-if test "x${rdpsettings_interface}" = "xlegacy"; then
-    AC_DEFINE([LEGACY_RDPSETTINGS])
-elif test "x${rdpsettings_interface}" = "xunknown"; then
-    AC_MSG_WARN([
+    # If not current, check for legacy interface
+    if test "x${rdpsettings_interface}" = "xunknown"
+    then
+        AC_CHECK_MEMBERS([rdpSettings.width,
+                          rdpSettings.height,
+                          rdpSettings.order_support],
+                         [rdpsettings_interface=legacy],,
+                         [[#include <freerdp/freerdp.h>]])
+    fi
+
+    # Set defines based on interface type, warn if unknown
+    if test "x${rdpsettings_interface}" = "xlegacy"; then
+        AC_DEFINE([LEGACY_RDPSETTINGS],,
+                  [Whether the legacy version of the rdpSettings API was found])
+
+        # Legacy interface may not have AudioPlayback settings
+        AC_CHECK_MEMBERS([rdpSettings.audio_playback],,
+                         [rdpsettings_audioplayback=no],
+                         [[#include <freerdp/freerdp.h>]])
+
+        # Legacy interface may not have DeviceRedirection settings
+        AC_CHECK_MEMBERS([rdpSettings.device_redirection],,
+                         [rdpsettings_deviceredirection=no],
+                         [[#include <freerdp/freerdp.h>]])
+
+    elif test "x${rdpsettings_interface}" = "xunknown"; then
+        AC_MSG_WARN([
   --------------------------------------------
    Unknown rdpSettings interface
    RDP will be disabled.
   --------------------------------------------]) 
-    have_freerdp=no
+        have_freerdp=no
+    fi
+fi
+
+# Activate audio playback settings if present
+if test "x${have_freerdp}" = "xyes" -a "x${rdpsettings_audioplayback}" = "xyes"; then
+    AC_DEFINE([HAVE_RDPSETTINGS_AUDIOPLAYBACK],,
+              [Whether the rdpSettings structure has AudioPlayback settings])
+fi
+
+# Activate device redirection settings if present
+if test "x${have_freerdp}" = "xyes" -a "x${rdpsettings_deviceredirection}" = "xyes"; then
+    AC_DEFINE([HAVE_RDPSETTINGS_DEVICEREDIRECTION],,
+              [Whether the rdpSettings structure has DeviceRedirection settings])
+fi
+
+# Check if the type CHANNEL_ENTRY_POINTS_FREERDP exists, if not define it to CHANNEL_ENTRY_POINTS_EX
+if test "x${have_freerdp}" = "xyes"
+then
+    AC_CHECK_TYPE([CHANNEL_ENTRY_POINTS_FREERDP],,
+                  AC_DEFINE([CHANNEL_ENTRY_POINTS_FREERDP],[CHANNEL_ENTRY_POINTS_EX], [Type compatibility]),
+                  [[#include <freerdp/svc.h>]])
+fi
+
+# Check if the freerdp version header exists
+if test "x${have_freerdp}" = "xyes"
+then
+    AC_CHECK_HEADERS([freerdp/version.h])
 fi
 
 #
 # FreeRDP: rdpBitmap
 #
 
-AC_MSG_CHECKING([whether rdpBitmap.Decompress() requires the codec_id])
-AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#include <winpr/wtypes.h>
-                                    #include <freerdp/freerdp.h>
-                                    void __decompress(rdpContext* context,
-                                                      rdpBitmap* bitmap,
-                                                      UINT8* data,
-                                                      int width,
-                                                      int height,
-                                                      int bpp,
-                                                      int length,
-                                                      BOOL compressed,
-                                                      int codec_id);
-                                    rdpBitmap b = { .Decompress = __decompress };]])],
-                  [AC_MSG_RESULT([yes])],
-                  [AC_MSG_RESULT([no])
-                   AC_DEFINE([LEGACY_RDPBITMAP])])
+if test "x${have_freerdp}" = "xyes"
+then
+    AC_MSG_CHECKING([whether rdpBitmap.Decompress() requires the codec_id])
+    AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#include <winpr/wtypes.h>
+                                        #include <freerdp/freerdp.h>
+                                        void __decompress(rdpContext* context,
+                                                          rdpBitmap* bitmap,
+                                                          UINT8* data,
+                                                          int width,
+                                                          int height,
+                                                          int bpp,
+                                                          int length,
+                                                          BOOL compressed,
+                                                          int codec_id);
+                                        rdpBitmap b = { .Decompress = __decompress };]])],
+                      [AC_MSG_RESULT([yes])],
+                      [AC_MSG_RESULT([no])
+                       AC_DEFINE([LEGACY_RDPBITMAP],,
+                                 [Whether the legacy rdpBitmap API was found])])
+fi
+
+#
+# FreeRDP: Decompression function variants
+#
+
+# Check whether interleaved_decompress() can handle the palette
+if test "x${have_freerdp}" = "xyes"
+then
+    AC_MSG_CHECKING([whether interleaved_decompress() accepts an additional palette parameter])
+    AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#include <freerdp/codec/interleaved.h>
+
+                                        int main() {
+                                            BYTE* palette = NULL;
+                                            interleaved_decompress(NULL, NULL, 0, 0, NULL, 0, 0, 0, 0, 0, 0, palette);
+                                        }]])],
+                      [AC_MSG_RESULT([yes])
+                       AC_DEFINE([INTERLEAVED_DECOMPRESS_TAKES_PALETTE],,
+                                 [Whether interleaved_decompress() accepts an additional palette parameter])],
+                      [AC_MSG_RESULT([no])])
+fi
+
+# Check whether planar_decompress() will handle flipping
+if test "x${have_freerdp}" = "xyes"
+then
+    AC_MSG_CHECKING([whether planar_decompress() can flip])
+    AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#include <freerdp/codec/planar.h>
+
+                                        int main() {
+                                            BOOL* flip = TRUE;
+                                            planar_decompress(NULL, NULL, 0, NULL, 0, 0, 0, 0, 0, 0, flip);
+                                        }]])],
+                      [AC_MSG_RESULT([yes])
+                       AC_DEFINE([PLANAR_DECOMPRESS_CAN_FLIP],,
+                                 [Whether planar_decompress() can flip])],
+                      [AC_MSG_RESULT([no])])
+fi
+
+#
+# FreeRDP: rdpContext
+#
+
+# Check for rdpContext.codecs
+if test "x${have_freerdp}" = "xyes"
+then
+    AC_CHECK_MEMBERS([rdpContext.codecs],
+                     [AC_DEFINE([FREERDP_BITMAP_REQUIRES_ALIGNED_MALLOC],,
+                                [Whether this version of FreeRDP requires _aligned_malloc() for bitmap data])],,
+                     [[#include <freerdp/freerdp.h>]])
+fi
 
 #
 # FreeRDP: rdpPalette
 #
 
-AC_MSG_CHECKING([whether rdpPalette.entries is static])
-AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#include <freerdp/update.h>
-                                    rdpPalette p;
-                                    PALETTE_ENTRY* foo = p.entries;]])],
-                  [AC_MSG_RESULT([yes])],
-                  [AC_MSG_RESULT([no])
-                   AC_DEFINE([LEGACY_RDPPALETTE])])
+if test "x${have_freerdp}" = "xyes"
+then
+    AC_MSG_CHECKING([whether rdpPalette.entries is static])
+    AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#include <freerdp/update.h>
+                                        rdpPalette p;
+                                        PALETTE_ENTRY* foo = p.entries;]])],
+                      [AC_MSG_RESULT([yes])],
+                      [AC_MSG_RESULT([no])
+                       AC_DEFINE([LEGACY_RDPPALETTE],,
+                                 [Whether the legacy rdpPalette API was found])])
+fi
+
+#
+# FreeRDP: rdpPointer
+#
+
+# Check for SetDefault and SetNull members of rdpPointer
+if test "x${have_freerdp}" = "xyes"
+then
+    AC_CHECK_MEMBERS([rdpPointer.SetDefault,
+                      rdpPointer.SetNull],
+                     ,,
+                     [[#include <freerdp/freerdp.h>]])
+fi
 
 #
 # FreeRDP: wMessage / RDP_EVENT
 #
 
 # Check for current (as of 1.1) wMessage interface
-AC_CHECK_MEMBERS([wMessage.id],
-                 [event_interface=stable],,
-                 [[#include <winpr/collections.h>]])
-
-# If not current, check for legacy (RDP_EVENT) interface
-if test "x${event_interface}" = "xunknown"
+if test "x${have_freerdp}" = "xyes"
 then
-    AC_CHECK_MEMBERS([RDP_EVENT.event_class],
-                     [event_interface=legacy],,
-                     [[#include <freerdp/types.h>]])
-fi
+    AC_CHECK_MEMBERS([wMessage.id],
+                     [event_interface=stable],,
+                     [[#include <winpr/collections.h>]])
+
+    # If not current, check for legacy (RDP_EVENT) interface
+    if test "x${event_interface}" = "xunknown"
+    then
+        AC_CHECK_MEMBERS([RDP_EVENT.event_class],
+                         [event_interface=legacy],,
+                         [[#include <freerdp/types.h>]])
+    fi
 
-# Set defines based on interface type, warn if unknown
-if test "x${event_interface}" = "xlegacy"; then
-    AC_DEFINE([LEGACY_EVENT])
-elif test "x${event_interface}" = "xunknown"; then
-    AC_MSG_WARN([
+    # Set defines based on interface type, warn if unknown
+    if test "x${event_interface}" = "xlegacy"; then
+        AC_DEFINE([LEGACY_EVENT],,
+                  [Whether the legacy RDP_EVENT API was found])
+    elif test "x${event_interface}" = "xunknown"; then
+        AC_MSG_WARN([
   --------------------------------------------
    Unknown event interface
    RDP will be disabled.
   --------------------------------------------])
-    have_freerdp=no
+        have_freerdp=no
+    fi
 fi
 
-
 AM_CONDITIONAL([LEGACY_FREERDP_EXTENSIONS], [test "x${legacy_freerdp_extensions}" = "xyes"])
+AM_CONDITIONAL([ENABLE_DISPLAY_UPDATE], [test "x${have_disp}" = "xyes"])
 AM_CONDITIONAL([ENABLE_WINPR], [test "x${have_winpr}"   = "xyes"])
 AM_CONDITIONAL([ENABLE_RDP],   [test "x${have_freerdp}" = "xyes"])
 
 AC_SUBST(RDP_LIBS)
 
 #
-# libssh
+# libssh2
 #
 
-have_libssh=yes
+have_libssh2=disabled
 SSH_LIBS=
+AC_ARG_WITH([ssh],
+            [AS_HELP_STRING([--with-ssh],
+                            [support SSH @<:@default=check@:>@])],
+            [],
+            [with_ssh=check])
+
+AC_ARG_ENABLE(ssh_agent,
+              [AS_HELP_STRING([--enable-ssh-agent],
+                              [enable built-in ssh-agent])
+              ],enable_ssh_agent=yes)
+
+if test "x$with_ssh" != "xno"
+then
+    have_libssh2=yes
 
-AC_CHECK_LIB([ssh], [ssh_new], [SSH_LIBS="$SSH_LIBS -lssh"], [have_libssh=no])
-AM_CONDITIONAL([ENABLE_SSH], [test "x${have_libssh}" = "xyes" -a "x${have_pango}" = "xyes"])
+    AC_CHECK_LIB([ssh2], [libssh2_session_init_ex],
+                         [SSH_LIBS="$SSH_LIBS -lssh2"],
+                         [have_libssh2=no])
+fi
+
+AM_CONDITIONAL([ENABLE_COMMON_SSH], [test "x${have_libssh2}"  = "xyes" \
+                                       -a "x${have_ssl}"      = "xyes"])
+AM_COND_IF([ENABLE_COMMON_SSH],
+           [AC_DEFINE([ENABLE_COMMON_SSH],,
+                      [Whether support for the common SSH core is enabled])])
+
+AM_CONDITIONAL([ENABLE_SSH], [test "x${have_libssh2}"  = "xyes" \
+                                -a "x${have_terminal}" = "xyes" \
+                                -a "x${have_ssl}"      = "xyes"])
 
 AC_SUBST(SSH_LIBS)
 
+#
+# Underlying crypto library usage of libssh2
+#
+
+if test "x${have_libssh2}" = "xyes"
+then
+
+    # Whether libssh2 was built against libgcrypt
+    AC_CHECK_LIB([ssh2], [gcry_control],
+                         [AC_CHECK_HEADER(gcrypt.h,
+                                          [AC_DEFINE([LIBSSH2_USES_GCRYPT],,
+                                                     [Whether libssh2 was built against libgcrypt])],
+                                          [AC_MSG_WARN([
+  --------------------------------------------
+   libssh2 appears to be built against libgcrypt, but the libgcrypt headers
+   could not be found. SSH will be disabled.
+  --------------------------------------------])
+                                           have_libssh2=no])])
+
+fi
+
+#
+# Agent forwarding support within libssh2
+#
+
+have_ssh_agent=no
+if test "x${have_libssh2}" = "xyes" -a "x${enable_ssh_agent}" = "xyes"
+then
+
+    AC_CHECK_DECL([libssh2_channel_request_auth_agent],
+                  [have_ssh_agent=yes], [],
+                  [[#include <libssh2.h>]])
+
+    if test "x${have_ssh_agent}" = "xno"
+    then
+        AC_MSG_ERROR([
+      --------------------------------------------
+       Agent forwarding support was requested but no such support was found
+       in libssh2.
+      --------------------------------------------])
+    else
+        AC_DEFINE([ENABLE_SSH_AGENT],,
+                  [Whether agent forwarding support for SSH is enabled])
+    fi
+
+fi
+
+AM_CONDITIONAL([ENABLE_SSH_AGENT],
+	   [test "x${have_ssh_agent}"   = "xyes" \
+	      -a "x${enable_ssh_agent}" = "xyes"])
+
+#
+# libtelnet
+#
+
+have_libtelnet=disabled
+TELNET_LIBS=
+AC_ARG_WITH([telnet],
+            [AS_HELP_STRING([--with-telnet],
+                            [support Telnet @<:@default=check@:>@])],
+            [],
+            [with_telnet=check])
+
+if test "x$with_telnet" != "xno"
+then
+    have_libtelnet=yes
+    AC_CHECK_LIB([telnet], [telnet_init],
+                           [TELNET_LIBS="$TELNET_LIBS -ltelnet"],
+                           [have_libtelnet=no])
+fi
+
+AM_CONDITIONAL([ENABLE_TELNET], [test "x${have_libtelnet}"  = "xyes" \
+                                   -a "x${have_terminal}" = "xyes"])
+
+AC_SUBST(TELNET_LIBS)
+
+#
+# libwebp
+#
+
+have_webp=disabled
+WEBP_LIBS=
+AC_ARG_WITH([webp],
+            [AS_HELP_STRING([--with-webp],
+                            [support WebP image encoding @<:@default=check@:>@])],
+            [],
+            [with_webp=check])
+
+if test "x$with_webp" != "xno"
+then
+    have_webp=yes
+
+    AC_CHECK_HEADER(webp/encode.h,, [have_webp=no])
+    AC_CHECK_LIB([webp], [WebPEncode], [WEBP_LIBS="$WEBP_LIBS -lwebp"], [have_webp=no])
+
+    if test "x${have_webp}" = "xno"
+    then
+        AC_MSG_WARN([
+  --------------------------------------------
+   Unable to find libwebp.
+   Images will not be encoded using WebP.
+  --------------------------------------------])
+    else
+        AC_DEFINE([ENABLE_WEBP],, [Whether WebP support is enabled])
+    fi
+fi
+
+AM_CONDITIONAL([ENABLE_WEBP], [test "x${have_webp}" = "xyes"])
+AC_SUBST(WEBP_LIBS)
+
+
 AC_CONFIG_FILES([Makefile
                  tests/Makefile
+                 src/common/Makefile
+                 src/common-ssh/Makefile
+                 src/terminal/Makefile
                  src/libguac/Makefile
                  src/guacd/Makefile
                  src/protocols/rdp/Makefile
                  src/protocols/ssh/Makefile
+                 src/protocols/telnet/Makefile
                  src/protocols/vnc/Makefile])
 AC_OUTPUT
 
-AM_COND_IF([ENABLE_RDP],  [build_rdp=yes], [build_rdp=no])
-AM_COND_IF([ENABLE_SSH],  [build_ssh=yes], [build_ssh=no])
-AM_COND_IF([ENABLE_VNC],  [build_vnc=yes], [build_vnc=no])
+AM_COND_IF([ENABLE_RDP],    [build_rdp=yes],    [build_rdp=no])
+AM_COND_IF([ENABLE_SSH],    [build_ssh=yes],    [build_ssh=no])
+AM_COND_IF([ENABLE_TELNET], [build_telnet=yes], [build_telnet=no])
+AM_COND_IF([ENABLE_VNC],    [build_vnc=yes],    [build_vnc=no])
 
 AM_COND_IF([ENABLE_INIT], [build_init="${init_dir}"], [build_init=no])
 
@@ -528,16 +996,19 @@ $PACKAGE_NAME version $PACKAGE_VERSION
 
      freerdp ............. ${have_freerdp}
      pango ............... ${have_pango}
-     libssh .............. ${have_libssh}
+     libssh2 ............. ${have_libssh2}
      libssl .............. ${have_ssl}
+     libtelnet ........... ${have_libtelnet}
      libVNCServer ........ ${have_libvncserver}
      libvorbis ........... ${have_vorbis}
      libpulse ............ ${have_pulse}
+     libwebp ............. ${have_webp}
 
    Protocol support:
 
       RDP ....... ${build_rdp}
       SSH ....... ${build_ssh}
+      Telnet .... ${build_telnet}
       VNC ....... ${build_vnc}
 
    Init scripts: ${build_init}
diff --git a/depcomp b/depcomp
index 25a39e6..4ebd5b3 100755
--- a/depcomp
+++ b/depcomp
@@ -1,10 +1,9 @@
 #! /bin/sh
 # depcomp - compile a program generating dependencies as side-effects
 
-scriptversion=2012-03-27.16; # UTC
+scriptversion=2013-05-30.07; # UTC
 
-# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006, 2007, 2009, 2010,
-# 2011, 2012 Free Software Foundation, Inc.
+# Copyright (C) 1999-2013 Free Software Foundation, Inc.
 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -28,9 +27,9 @@ scriptversion=2012-03-27.16; # UTC
 
 case $1 in
   '')
-     echo "$0: No command.  Try '$0 --help' for more information." 1>&2
-     exit 1;
-     ;;
+    echo "$0: No command.  Try '$0 --help' for more information." 1>&2
+    exit 1;
+    ;;
   -h | --h*)
     cat <<\EOF
 Usage: depcomp [--help] [--version] PROGRAM [ARGS]
@@ -57,11 +56,65 @@ EOF
     ;;
 esac
 
+# Get the directory component of the given path, and save it in the
+# global variables '$dir'.  Note that this directory component will
+# be either empty or ending with a '/' character.  This is deliberate.
+set_dir_from ()
+{
+  case $1 in
+    */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;;
+      *) dir=;;
+  esac
+}
+
+# Get the suffix-stripped basename of the given path, and save it the
+# global variable '$base'.
+set_base_from ()
+{
+  base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'`
+}
+
+# If no dependency file was actually created by the compiler invocation,
+# we still have to create a dummy depfile, to avoid errors with the
+# Makefile "include basename.Plo" scheme.
+make_dummy_depfile ()
+{
+  echo "#dummy" > "$depfile"
+}
+
+# Factor out some common post-processing of the generated depfile.
+# Requires the auxiliary global variable '$tmpdepfile' to be set.
+aix_post_process_depfile ()
+{
+  # If the compiler actually managed to produce a dependency file,
+  # post-process it.
+  if test -f "$tmpdepfile"; then
+    # Each line is of the form 'foo.o: dependency.h'.
+    # Do two passes, one to just change these to
+    #   $object: dependency.h
+    # and one to simply output
+    #   dependency.h:
+    # which is needed to avoid the deleted-header problem.
+    { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile"
+      sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile"
+    } > "$depfile"
+    rm -f "$tmpdepfile"
+  else
+    make_dummy_depfile
+  fi
+}
+
 # A tabulation character.
 tab='	'
 # A newline character.
 nl='
 '
+# Character ranges might be problematic outside the C locale.
+# These definitions help.
+upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ
+lower=abcdefghijklmnopqrstuvwxyz
+digits=0123456789
+alpha=${upper}${lower}
 
 if test -z "$depmode" || test -z "$source" || test -z "$object"; then
   echo "depcomp: Variables source, object and depmode must be set" 1>&2
@@ -75,6 +128,9 @@ tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
 
 rm -f "$tmpdepfile"
 
+# Avoid interferences from the environment.
+gccflag= dashmflag=
+
 # Some modes work just like other modes, but use different flags.  We
 # parameterize here, but still list the modes in the big case below,
 # to make depend.m4 easier to write.  Note that we *cannot* use a case
@@ -86,32 +142,32 @@ if test "$depmode" = hp; then
 fi
 
 if test "$depmode" = dashXmstdout; then
-   # This is just like dashmstdout with a different argument.
-   dashmflag=-xM
-   depmode=dashmstdout
+  # This is just like dashmstdout with a different argument.
+  dashmflag=-xM
+  depmode=dashmstdout
 fi
 
 cygpath_u="cygpath -u -f -"
 if test "$depmode" = msvcmsys; then
-   # This is just like msvisualcpp but w/o cygpath translation.
-   # Just convert the backslash-escaped backslashes to single forward
-   # slashes to satisfy depend.m4
-   cygpath_u='sed s,\\\\,/,g'
-   depmode=msvisualcpp
+  # This is just like msvisualcpp but w/o cygpath translation.
+  # Just convert the backslash-escaped backslashes to single forward
+  # slashes to satisfy depend.m4
+  cygpath_u='sed s,\\\\,/,g'
+  depmode=msvisualcpp
 fi
 
 if test "$depmode" = msvc7msys; then
-   # This is just like msvc7 but w/o cygpath translation.
-   # Just convert the backslash-escaped backslashes to single forward
-   # slashes to satisfy depend.m4
-   cygpath_u='sed s,\\\\,/,g'
-   depmode=msvc7
+  # This is just like msvc7 but w/o cygpath translation.
+  # Just convert the backslash-escaped backslashes to single forward
+  # slashes to satisfy depend.m4
+  cygpath_u='sed s,\\\\,/,g'
+  depmode=msvc7
 fi
 
 if test "$depmode" = xlc; then
-   # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency informations.
-   gccflag=-qmakedep=gcc,-MF
-   depmode=gcc
+  # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information.
+  gccflag=-qmakedep=gcc,-MF
+  depmode=gcc
 fi
 
 case "$depmode" in
@@ -134,8 +190,7 @@ gcc3)
   done
   "$@"
   stat=$?
-  if test $stat -eq 0; then :
-  else
+  if test $stat -ne 0; then
     rm -f "$tmpdepfile"
     exit $stat
   fi
@@ -143,13 +198,17 @@ gcc3)
   ;;
 
 gcc)
+## Note that this doesn't just cater to obsosete pre-3.x GCC compilers.
+## but also to in-use compilers like IMB xlc/xlC and the HP C compiler.
+## (see the conditional assignment to $gccflag above).
 ## There are various ways to get dependency output from gcc.  Here's
 ## why we pick this rather obscure method:
 ## - Don't want to use -MD because we'd like the dependencies to end
 ##   up in a subdir.  Having to rename by hand is ugly.
 ##   (We might end up doing this anyway to support other compilers.)
 ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
-##   -MM, not -M (despite what the docs say).
+##   -MM, not -M (despite what the docs say).  Also, it might not be
+##   supported by the other compilers which use the 'gcc' depmode.
 ## - Using -M directly means running the compiler twice (even worse
 ##   than renaming).
   if test -z "$gccflag"; then
@@ -157,15 +216,14 @@ gcc)
   fi
   "$@" -Wp,"$gccflag$tmpdepfile"
   stat=$?
-  if test $stat -eq 0; then :
-  else
+  if test $stat -ne 0; then
     rm -f "$tmpdepfile"
     exit $stat
   fi
   rm -f "$depfile"
   echo "$object : \\" > "$depfile"
-  alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
-## The second -e expression handles DOS-style file names with drive letters.
+  # The second -e expression handles DOS-style file names with drive
+  # letters.
   sed -e 's/^[^:]*: / /' \
       -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
 ## This next piece of magic avoids the "deleted header file" problem.
@@ -174,15 +232,15 @@ gcc)
 ## typically no way to rebuild the header).  We avoid this by adding
 ## dummy dependencies for each header file.  Too bad gcc doesn't do
 ## this for us directly.
-  tr ' ' "$nl" < "$tmpdepfile" |
 ## Some versions of gcc put a space before the ':'.  On the theory
 ## that the space means something, we add a space to the output as
 ## well.  hp depmode also adds that space, but also prefixes the VPATH
 ## to the object.  Take care to not repeat it in the output.
 ## Some versions of the HPUX 10.20 sed can't process this invocation
 ## correctly.  Breaking it into two sed invocations is a workaround.
-    sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
-      | sed -e 's/$/ :/' >> "$depfile"
+  tr ' ' "$nl" < "$tmpdepfile" \
+    | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
+    | sed -e 's/$/ :/' >> "$depfile"
   rm -f "$tmpdepfile"
   ;;
 
@@ -200,8 +258,7 @@ sgi)
     "$@" -MDupdate "$tmpdepfile"
   fi
   stat=$?
-  if test $stat -eq 0; then :
-  else
+  if test $stat -ne 0; then
     rm -f "$tmpdepfile"
     exit $stat
   fi
@@ -209,7 +266,6 @@ sgi)
 
   if test -f "$tmpdepfile"; then  # yes, the sourcefile depend on other files
     echo "$object : \\" > "$depfile"
-
     # Clip off the initial element (the dependent).  Don't try to be
     # clever and replace this with sed code, as IRIX sed won't handle
     # lines with more than a fixed number of characters (4096 in
@@ -217,19 +273,15 @@ sgi)
     # the IRIX cc adds comments like '#:fec' to the end of the
     # dependency line.
     tr ' ' "$nl" < "$tmpdepfile" \
-    | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
-    tr "$nl" ' ' >> "$depfile"
+      | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \
+      | tr "$nl" ' ' >> "$depfile"
     echo >> "$depfile"
-
     # The second pass generates a dummy entry for each header file.
     tr ' ' "$nl" < "$tmpdepfile" \
-   | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
-   >> "$depfile"
+      | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
+      >> "$depfile"
   else
-    # The sourcefile does not contain any dependencies, so just
-    # store a dummy comment line, to avoid errors with the Makefile
-    # "include basename.Plo" scheme.
-    echo "#dummy" > "$depfile"
+    make_dummy_depfile
   fi
   rm -f "$tmpdepfile"
   ;;
@@ -247,9 +299,8 @@ aix)
   # current directory.  Also, the AIX compiler puts '$object:' at the
   # start of each line; $object doesn't have directory information.
   # Version 6 uses the directory in both cases.
-  dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
-  test "x$dir" = "x$object" && dir=
-  base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+  set_dir_from "$object"
+  set_base_from "$object"
   if test "$libtool" = yes; then
     tmpdepfile1=$dir$base.u
     tmpdepfile2=$base.u
@@ -262,9 +313,7 @@ aix)
     "$@" -M
   fi
   stat=$?
-
-  if test $stat -eq 0; then :
-  else
+  if test $stat -ne 0; then
     rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
     exit $stat
   fi
@@ -273,65 +322,113 @@ aix)
   do
     test -f "$tmpdepfile" && break
   done
-  if test -f "$tmpdepfile"; then
-    # Each line is of the form 'foo.o: dependent.h'.
-    # Do two passes, one to just change these to
-    # '$object: dependent.h' and one to simply 'dependent.h:'.
-    sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
-    sed -e 's,^.*\.[a-z]*:['"$tab"' ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
-  else
-    # The sourcefile does not contain any dependencies, so just
-    # store a dummy comment line, to avoid errors with the Makefile
-    # "include basename.Plo" scheme.
-    echo "#dummy" > "$depfile"
+  aix_post_process_depfile
+  ;;
+
+tcc)
+  # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26
+  # FIXME: That version still under development at the moment of writing.
+  #        Make that this statement remains true also for stable, released
+  #        versions.
+  # It will wrap lines (doesn't matter whether long or short) with a
+  # trailing '\', as in:
+  #
+  #   foo.o : \
+  #    foo.c \
+  #    foo.h \
+  #
+  # It will put a trailing '\' even on the last line, and will use leading
+  # spaces rather than leading tabs (at least since its commit 0394caf7
+  # "Emit spaces for -MD").
+  "$@" -MD -MF "$tmpdepfile"
+  stat=$?
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile"
+    exit $stat
   fi
+  rm -f "$depfile"
+  # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'.
+  # We have to change lines of the first kind to '$object: \'.
+  sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile"
+  # And for each line of the second kind, we have to emit a 'dep.h:'
+  # dummy dependency, to avoid the deleted-header problem.
+  sed -n -e 's|^  *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile"
   rm -f "$tmpdepfile"
   ;;
 
-icc)
-  # Intel's C compiler anf tcc (Tiny C Compiler) understand '-MD -MF file'.
-  # However on
-  #    $CC -MD -MF foo.d -c -o sub/foo.o sub/foo.c
-  # ICC 7.0 will fill foo.d with something like
-  #    foo.o: sub/foo.c
-  #    foo.o: sub/foo.h
-  # which is wrong.  We want
-  #    sub/foo.o: sub/foo.c
-  #    sub/foo.o: sub/foo.h
-  #    sub/foo.c:
-  #    sub/foo.h:
-  # ICC 7.1 will output
+## The order of this option in the case statement is important, since the
+## shell code in configure will try each of these formats in the order
+## listed in this file.  A plain '-MD' option would be understood by many
+## compilers, so we must ensure this comes after the gcc and icc options.
+pgcc)
+  # Portland's C compiler understands '-MD'.
+  # Will always output deps to 'file.d' where file is the root name of the
+  # source file under compilation, even if file resides in a subdirectory.
+  # The object file name does not affect the name of the '.d' file.
+  # pgcc 10.2 will output
   #    foo.o: sub/foo.c sub/foo.h
-  # and will wrap long lines using '\':
+  # and will wrap long lines using '\' :
   #    foo.o: sub/foo.c ... \
   #     sub/foo.h ... \
   #     ...
-  # tcc 0.9.26 (FIXME still under development at the moment of writing)
-  # will emit a similar output, but also prepend the continuation lines
-  # with horizontal tabulation characters.
-  "$@" -MD -MF "$tmpdepfile"
-  stat=$?
-  if test $stat -eq 0; then :
-  else
+  set_dir_from "$object"
+  # Use the source, not the object, to determine the base name, since
+  # that's sadly what pgcc will do too.
+  set_base_from "$source"
+  tmpdepfile=$base.d
+
+  # For projects that build the same source file twice into different object
+  # files, the pgcc approach of using the *source* file root name can cause
+  # problems in parallel builds.  Use a locking strategy to avoid stomping on
+  # the same $tmpdepfile.
+  lockdir=$base.d-lock
+  trap "
+    echo '$0: caught signal, cleaning up...' >&2
+    rmdir '$lockdir'
+    exit 1
+  " 1 2 13 15
+  numtries=100
+  i=$numtries
+  while test $i -gt 0; do
+    # mkdir is a portable test-and-set.
+    if mkdir "$lockdir" 2>/dev/null; then
+      # This process acquired the lock.
+      "$@" -MD
+      stat=$?
+      # Release the lock.
+      rmdir "$lockdir"
+      break
+    else
+      # If the lock is being held by a different process, wait
+      # until the winning process is done or we timeout.
+      while test -d "$lockdir" && test $i -gt 0; do
+        sleep 1
+        i=`expr $i - 1`
+      done
+    fi
+    i=`expr $i - 1`
+  done
+  trap - 1 2 13 15
+  if test $i -le 0; then
+    echo "$0: failed to acquire lock after $numtries attempts" >&2
+    echo "$0: check lockdir '$lockdir'" >&2
+    exit 1
+  fi
+
+  if test $stat -ne 0; then
     rm -f "$tmpdepfile"
     exit $stat
   fi
   rm -f "$depfile"
-  # Each line is of the form 'foo.o: dependent.h',
-  # or 'foo.o: dep1.h dep2.h \', or ' dep3.h dep4.h \'.
+  # Each line is of the form `foo.o: dependent.h',
+  # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
   # Do two passes, one to just change these to
-  # '$object: dependent.h' and one to simply 'dependent.h:'.
-  sed -e "s/^[ $tab][ $tab]*/  /" -e "s,^[^:]*:,$object :," \
-    < "$tmpdepfile" > "$depfile"
-  sed '
-    s/[ '"$tab"'][ '"$tab"']*/ /g
-    s/^ *//
-    s/ *\\*$//
-    s/^[^:]*: *//
-    /^$/d
-    /:$/d
-    s/$/ :/
-  ' < "$tmpdepfile" >> "$depfile"
+  # `$object: dependent.h' and one to simply `dependent.h:'.
+  sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
+  # Some versions of the HPUX 10.20 sed can't process this invocation
+  # correctly.  Breaking it into two sed invocations is a workaround.
+  sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \
+    | sed -e 's/$/ :/' >> "$depfile"
   rm -f "$tmpdepfile"
   ;;
 
@@ -342,9 +439,8 @@ hp2)
   # 'foo.d', which lands next to the object file, wherever that
   # happens to be.
   # Much of this is similar to the tru64 case; see comments there.
-  dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
-  test "x$dir" = "x$object" && dir=
-  base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+  set_dir_from  "$object"
+  set_base_from "$object"
   if test "$libtool" = yes; then
     tmpdepfile1=$dir$base.d
     tmpdepfile2=$dir.libs/$base.d
@@ -355,8 +451,7 @@ hp2)
     "$@" +Maked
   fi
   stat=$?
-  if test $stat -eq 0; then :
-  else
+  if test $stat -ne 0; then
      rm -f "$tmpdepfile1" "$tmpdepfile2"
      exit $stat
   fi
@@ -366,76 +461,61 @@ hp2)
     test -f "$tmpdepfile" && break
   done
   if test -f "$tmpdepfile"; then
-    sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile"
+    sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile"
     # Add 'dependent.h:' lines.
     sed -ne '2,${
-	       s/^ *//
-	       s/ \\*$//
-	       s/$/:/
-	       p
-	     }' "$tmpdepfile" >> "$depfile"
+               s/^ *//
+               s/ \\*$//
+               s/$/:/
+               p
+             }' "$tmpdepfile" >> "$depfile"
   else
-    echo "#dummy" > "$depfile"
+    make_dummy_depfile
   fi
   rm -f "$tmpdepfile" "$tmpdepfile2"
   ;;
 
 tru64)
-   # The Tru64 compiler uses -MD to generate dependencies as a side
-   # effect.  'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
-   # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
-   # dependencies in 'foo.d' instead, so we check for that too.
-   # Subdirectories are respected.
-   dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
-   test "x$dir" = "x$object" && dir=
-   base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
-
-   if test "$libtool" = yes; then
-      # With Tru64 cc, shared objects can also be used to make a
-      # static library.  This mechanism is used in libtool 1.4 series to
-      # handle both shared and static libraries in a single compilation.
-      # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d.
-      #
-      # With libtool 1.5 this exception was removed, and libtool now
-      # generates 2 separate objects for the 2 libraries.  These two
-      # compilations output dependencies in $dir.libs/$base.o.d and
-      # in $dir$base.o.d.  We have to check for both files, because
-      # one of the two compilations can be disabled.  We should prefer
-      # $dir$base.o.d over $dir.libs/$base.o.d because the latter is
-      # automatically cleaned when .libs/ is deleted, while ignoring
-      # the former would cause a distcleancheck panic.
-      tmpdepfile1=$dir.libs/$base.lo.d   # libtool 1.4
-      tmpdepfile2=$dir$base.o.d          # libtool 1.5
-      tmpdepfile3=$dir.libs/$base.o.d    # libtool 1.5
-      tmpdepfile4=$dir.libs/$base.d      # Compaq CCC V6.2-504
-      "$@" -Wc,-MD
-   else
-      tmpdepfile1=$dir$base.o.d
-      tmpdepfile2=$dir$base.d
-      tmpdepfile3=$dir$base.d
-      tmpdepfile4=$dir$base.d
-      "$@" -MD
-   fi
-
-   stat=$?
-   if test $stat -eq 0; then :
-   else
-      rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
-      exit $stat
-   fi
-
-   for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
-   do
-     test -f "$tmpdepfile" && break
-   done
-   if test -f "$tmpdepfile"; then
-      sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
-      sed -e 's,^.*\.[a-z]*:['"$tab"' ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
-   else
-      echo "#dummy" > "$depfile"
-   fi
-   rm -f "$tmpdepfile"
-   ;;
+  # The Tru64 compiler uses -MD to generate dependencies as a side
+  # effect.  'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
+  # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
+  # dependencies in 'foo.d' instead, so we check for that too.
+  # Subdirectories are respected.
+  set_dir_from  "$object"
+  set_base_from "$object"
+
+  if test "$libtool" = yes; then
+    # Libtool generates 2 separate objects for the 2 libraries.  These
+    # two compilations output dependencies in $dir.libs/$base.o.d and
+    # in $dir$base.o.d.  We have to check for both files, because
+    # one of the two compilations can be disabled.  We should prefer
+    # $dir$base.o.d over $dir.libs/$base.o.d because the latter is
+    # automatically cleaned when .libs/ is deleted, while ignoring
+    # the former would cause a distcleancheck panic.
+    tmpdepfile1=$dir$base.o.d          # libtool 1.5
+    tmpdepfile2=$dir.libs/$base.o.d    # Likewise.
+    tmpdepfile3=$dir.libs/$base.d      # Compaq CCC V6.2-504
+    "$@" -Wc,-MD
+  else
+    tmpdepfile1=$dir$base.d
+    tmpdepfile2=$dir$base.d
+    tmpdepfile3=$dir$base.d
+    "$@" -MD
+  fi
+
+  stat=$?
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+    exit $stat
+  fi
+
+  for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+  do
+    test -f "$tmpdepfile" && break
+  done
+  # Same post-processing that is required for AIX mode.
+  aix_post_process_depfile
+  ;;
 
 msvc7)
   if test "$libtool" = yes; then
@@ -446,8 +526,7 @@ msvc7)
   "$@" $showIncludes > "$tmpdepfile"
   stat=$?
   grep -v '^Note: including file: ' "$tmpdepfile"
-  if test "$stat" = 0; then :
-  else
+  if test $stat -ne 0; then
     rm -f "$tmpdepfile"
     exit $stat
   fi
@@ -473,6 +552,7 @@ $ {
   G
   p
 }' >> "$depfile"
+  echo >> "$depfile" # make sure the fragment doesn't end with a backslash
   rm -f "$tmpdepfile"
   ;;
 
@@ -524,13 +604,14 @@ dashmstdout)
   # in the target name.  This is to cope with DOS-style filenames:
   # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
   "$@" $dashmflag |
-    sed 's:^['"$tab"' ]*[^:'"$tab"' ][^:][^:]*\:['"$tab"' ]*:'"$object"'\: :' > "$tmpdepfile"
+    sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile"
   rm -f "$depfile"
   cat < "$tmpdepfile" > "$depfile"
-  tr ' ' "$nl" < "$tmpdepfile" | \
-## Some versions of the HPUX 10.20 sed can't process this invocation
-## correctly.  Breaking it into two sed invocations is a workaround.
-    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+  # Some versions of the HPUX 10.20 sed can't process this sed invocation
+  # correctly.  Breaking it into two sed invocations is a workaround.
+  tr ' ' "$nl" < "$tmpdepfile" \
+    | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
+    | sed -e 's/$/ :/' >> "$depfile"
   rm -f "$tmpdepfile"
   ;;
 
@@ -583,10 +664,12 @@ makedepend)
   # makedepend may prepend the VPATH from the source file name to the object.
   # No need to regex-escape $object, excess matching of '.' is harmless.
   sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
-  sed '1,2d' "$tmpdepfile" | tr ' ' "$nl" | \
-## Some versions of the HPUX 10.20 sed can't process this invocation
-## correctly.  Breaking it into two sed invocations is a workaround.
-    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+  # Some versions of the HPUX 10.20 sed can't process the last invocation
+  # correctly.  Breaking it into two sed invocations is a workaround.
+  sed '1,2d' "$tmpdepfile" \
+    | tr ' ' "$nl" \
+    | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
+    | sed -e 's/$/ :/' >> "$depfile"
   rm -f "$tmpdepfile" "$tmpdepfile".bak
   ;;
 
@@ -622,10 +705,10 @@ cpp)
     esac
   done
 
-  "$@" -E |
-    sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
-       -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
-    sed '$ s: \\$::' > "$tmpdepfile"
+  "$@" -E \
+    | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
+             -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
+    | sed '$ s: \\$::' > "$tmpdepfile"
   rm -f "$depfile"
   echo "$object : \\" > "$depfile"
   cat < "$tmpdepfile" >> "$depfile"
@@ -657,15 +740,15 @@ msvisualcpp)
       shift
       ;;
     "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
-	set fnord "$@"
-	shift
-	shift
-	;;
+        set fnord "$@"
+        shift
+        shift
+        ;;
     *)
-	set fnord "$@" "$arg"
-	shift
-	shift
-	;;
+        set fnord "$@" "$arg"
+        shift
+        shift
+        ;;
     esac
   done
   "$@" -E 2>/dev/null |
diff --git a/doc/Doxyfile b/doc/Doxyfile
index 82cec8b..5b25799 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -1,1551 +1,42 @@
-# Doxyfile 1.6.3
 
-# This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project
 #
-# All text after a hash (#) is considered a comment and will be ignored
-# The format is:
-#       TAG = value [value, ...]
-# For lists items can also be appended using:
-#       TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (" ")
-
-#---------------------------------------------------------------------------
-# Project related configuration options
-#---------------------------------------------------------------------------
-
-# This tag specifies the encoding used for all characters in the config file
-# that follow. The default is UTF-8 which is also the encoding used for all
-# text before the first occurrence of this tag. Doxygen uses libiconv (or the
-# iconv built into libc) for the transcoding. See
-# http://www.gnu.org/software/libiconv for the list of possible encodings.
-
-DOXYFILE_ENCODING      = UTF-8
-
-# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
-# by quotes) that should identify the project.
-
-PROJECT_NAME           = libguac
-
-# The PROJECT_NUMBER tag can be used to enter a project or revision number.
-# This could be handy for archiving the generated documentation or
-# if some version control system is used.
-
-PROJECT_NUMBER         = 0.8.2
-
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
-# base path where the generated documentation will be put.
-# If a relative path is entered, it will be relative to the location
-# where doxygen was started. If left blank the current directory will be used.
-
-OUTPUT_DIRECTORY       = doxygen-output
-
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
-# 4096 sub-directories (in 2 levels) under the output directory of each output
-# format and will distribute the generated files over these directories.
-# Enabling this option can be useful when feeding doxygen a huge amount of
-# source files, where putting all generated files in the same directory would
-# otherwise cause performance problems for the file system.
-
-CREATE_SUBDIRS         = NO
-
-# The OUTPUT_LANGUAGE tag is used to specify the language in which all
-# documentation generated by doxygen is written. Doxygen will use this
-# information to generate all constant output in the proper language.
-# The default language is English, other supported languages are:
-# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
-# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
-# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
-# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
-# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
-# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
-
-OUTPUT_LANGUAGE        = English
-
-# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
-# include brief member descriptions after the members that are listed in
-# the file and class documentation (similar to JavaDoc).
-# Set to NO to disable this.
-
-BRIEF_MEMBER_DESC      = YES
-
-# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
-# the brief description of a member or function before the detailed description.
-# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
-# brief descriptions will be completely suppressed.
-
-REPEAT_BRIEF           = YES
-
-# This tag implements a quasi-intelligent brief description abbreviator
-# that is used to form the text in various listings. Each string
-# in this list, if found as the leading text of the brief description, will be
-# stripped from the text and the result after processing the whole list, is
-# used as the annotated text. Otherwise, the brief description is used as-is.
-# If left blank, the following values are used ("$name" is automatically
-# replaced with the name of the entity): "The $name class" "The $name widget"
-# "The $name file" "is" "provides" "specifies" "contains"
-# "represents" "a" "an" "the"
-
-ABBREVIATE_BRIEF       =
-
-# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
-# Doxygen will generate a detailed section even if there is only a brief
-# description.
-
-ALWAYS_DETAILED_SEC    = NO
-
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
-# inherited members of a class in the documentation of that class as if those
-# members were ordinary class members. Constructors, destructors and assignment
-# operators of the base classes will not be shown.
-
-INLINE_INHERITED_MEMB  = NO
-
-# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
-# path before files name in the file list and in the header files. If set
-# to NO the shortest path that makes the file name unique will be used.
-
-FULL_PATH_NAMES        = YES
-
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
-# can be used to strip a user-defined part of the path. Stripping is
-# only done if one of the specified strings matches the left-hand part of
-# the path. The tag can be used to show relative paths in the file list.
-# If left blank the directory from which doxygen is run is used as the
-# path to strip.
-
-STRIP_FROM_PATH        = ../src/libguac
-
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
-# the path mentioned in the documentation of a class, which tells
-# the reader which header file to include in order to use a class.
-# If left blank only the name of the header file containing the class
-# definition is used. Otherwise one should specify the include paths that
-# are normally passed to the compiler using the -I flag.
-
-STRIP_FROM_INC_PATH    = 
-
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
-# (but less readable) file names. This can be useful is your file systems
-# doesn't support long names like on DOS, Mac, or CD-ROM.
-
-SHORT_NAMES            = NO
-
-# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
-# will interpret the first line (until the first dot) of a JavaDoc-style
-# comment as the brief description. If set to NO, the JavaDoc
-# comments will behave just like regular Qt-style comments
-# (thus requiring an explicit @brief command for a brief description.)
-
-JAVADOC_AUTOBRIEF      = YES
-
-# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
-# interpret the first line (until the first dot) of a Qt-style
-# comment as the brief description. If set to NO, the comments
-# will behave just like regular Qt-style comments (thus requiring
-# an explicit \brief command for a brief description.)
-
-QT_AUTOBRIEF           = NO
-
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
-# treat a multi-line C++ special comment block (i.e. a block of //! or ///
-# comments) as a brief description. This used to be the default behaviour.
-# The new default is to treat a multi-line C++ comment block as a detailed
-# description. Set this tag to YES if you prefer the old behaviour instead.
-
-MULTILINE_CPP_IS_BRIEF = NO
-
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
-# member inherits the documentation from any documented member that it
-# re-implements.
-
-INHERIT_DOCS           = YES
-
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
-# a new page for each member. If set to NO, the documentation of a member will
-# be part of the file/class/namespace that contains it.
-
-SEPARATE_MEMBER_PAGES  = NO
-
-# The TAB_SIZE tag can be used to set the number of spaces in a tab.
-# Doxygen uses this value to replace tabs by spaces in code fragments.
-
-TAB_SIZE               = 8
-
-# This tag can be used to specify a number of aliases that acts
-# as commands in the documentation. An alias has the form "name=value".
-# For example adding "sideeffect=\par Side Effects:\n" will allow you to
-# put the command \sideeffect (or @sideeffect) in the documentation, which
-# will result in a user-defined paragraph with heading "Side Effects:".
-# You can put \n's in the value part of an alias to insert newlines.
-
-ALIASES                =
-
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
-# sources only. Doxygen will then generate output that is more tailored for C.
-# For instance, some of the names that are used will be different. The list
-# of all members will be omitted, etc.
-
-OPTIMIZE_OUTPUT_FOR_C  = YES
-
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
-# sources only. Doxygen will then generate output that is more tailored for
-# Java. For instance, namespaces will be presented as packages, qualified
-# scopes will look different, etc.
-
-OPTIMIZE_OUTPUT_JAVA   = NO
-
-# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
-# sources only. Doxygen will then generate output that is more tailored for
-# Fortran.
-
-OPTIMIZE_FOR_FORTRAN   = NO
-
-# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
-# sources. Doxygen will then generate output that is tailored for
-# VHDL.
-
-OPTIMIZE_OUTPUT_VHDL   = NO
-
-# Doxygen selects the parser to use depending on the extension of the files it parses.
-# With this tag you can assign which parser to use for a given extension.
-# Doxygen has a built-in mapping, but you can override or extend it using this tag.
-# The format is ext=language, where ext is a file extension, and language is one of
-# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
-# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
-# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
-# use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
-
-EXTENSION_MAPPING      =
-
-# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
-# to include (a tag file for) the STL sources as input, then you should
-# set this tag to YES in order to let doxygen match functions declarations and
-# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
-# func(std::string) {}). This also make the inheritance and collaboration
-# diagrams that involve STL classes more complete and accurate.
-
-BUILTIN_STL_SUPPORT    = NO
-
-# If you use Microsoft's C++/CLI language, you should set this option to YES to
-# enable parsing support.
-
-CPP_CLI_SUPPORT        = NO
-
-# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
-# Doxygen will parse them like normal C++ but will assume all classes use public
-# instead of private inheritance when no explicit protection keyword is present.
-
-SIP_SUPPORT            = NO
-
-# For Microsoft's IDL there are propget and propput attributes to indicate getter
-# and setter methods for a property. Setting this option to YES (the default)
-# will make doxygen to replace the get and set methods by a property in the
-# documentation. This will only work if the methods are indeed getting or
-# setting a simple type. If this is not the case, or you want to show the
-# methods anyway, you should set this option to NO.
-
-IDL_PROPERTY_SUPPORT   = YES
-
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
-# tag is set to YES, then doxygen will reuse the documentation of the first
-# member in the group (if any) for the other members of the group. By default
-# all members of a group must be documented explicitly.
-
-DISTRIBUTE_GROUP_DOC   = NO
-
-# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
-# the same type (for instance a group of public functions) to be put as a
-# subgroup of that type (e.g. under the Public Functions section). Set it to
-# NO to prevent subgrouping. Alternatively, this can be done per class using
-# the \nosubgrouping command.
-
-SUBGROUPING            = YES
-
-# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
-# is documented as struct, union, or enum with the name of the typedef. So
-# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
-# with name TypeT. When disabled the typedef will appear as a member of a file,
-# namespace, or class. And the struct will be named TypeS. This can typically
-# be useful for C code in case the coding convention dictates that all compound
-# types are typedef'ed and only the typedef is referenced, never the tag name.
-
-TYPEDEF_HIDES_STRUCT   = YES
-
-# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
-# determine which symbols to keep in memory and which to flush to disk.
-# When the cache is full, less often used symbols will be written to disk.
-# For small to medium size projects (<1000 input files) the default value is
-# probably good enough. For larger projects a too small cache size can cause
-# doxygen to be busy swapping symbols to and from disk most of the time
-# causing a significant performance penality.
-# If the system has enough physical memory increasing the cache will improve the
-# performance by keeping more symbols in memory. Note that the value works on
-# a logarithmic scale so increasing the size by one will rougly double the
-# memory usage. The cache size is given by this formula:
-# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
-# corresponding to a cache size of 2^16 = 65536 symbols
-
-SYMBOL_CACHE_SIZE      = 0
-
-#---------------------------------------------------------------------------
-# Build related configuration options
-#---------------------------------------------------------------------------
-
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
-# documentation are documented, even if no documentation was available.
-# Private class members and static file members will be hidden unless
-# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
-
-EXTRACT_ALL            = NO
-
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
-# will be included in the documentation.
-
-EXTRACT_PRIVATE        = NO
-
-# If the EXTRACT_STATIC tag is set to YES all static members of a file
-# will be included in the documentation.
-
-EXTRACT_STATIC         = NO
-
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
-# defined locally in source files will be included in the documentation.
-# If set to NO only classes defined in header files are included.
-
-EXTRACT_LOCAL_CLASSES  = YES
-
-# This flag is only useful for Objective-C code. When set to YES local
-# methods, which are defined in the implementation section but not in
-# the interface are included in the documentation.
-# If set to NO (the default) only methods in the interface are included.
-
-EXTRACT_LOCAL_METHODS  = NO
-
-# If this flag is set to YES, the members of anonymous namespaces will be
-# extracted and appear in the documentation as a namespace called
-# 'anonymous_namespace{file}', where file will be replaced with the base
-# name of the file that contains the anonymous namespace. By default
-# anonymous namespace are hidden.
-
-EXTRACT_ANON_NSPACES   = NO
-
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
-# undocumented members of documented classes, files or namespaces.
-# If set to NO (the default) these members will be included in the
-# various overviews, but no documentation section is generated.
-# This option has no effect if EXTRACT_ALL is enabled.
-
-HIDE_UNDOC_MEMBERS     = NO
-
-# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
-# undocumented classes that are normally visible in the class hierarchy.
-# If set to NO (the default) these classes will be included in the various
-# overviews. This option has no effect if EXTRACT_ALL is enabled.
-
-HIDE_UNDOC_CLASSES     = NO
-
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
-# friend (class|struct|union) declarations.
-# If set to NO (the default) these declarations will be included in the
-# documentation.
-
-HIDE_FRIEND_COMPOUNDS  = NO
-
-# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
-# documentation blocks found inside the body of a function.
-# If set to NO (the default) these blocks will be appended to the
-# function's detailed documentation block.
-
-HIDE_IN_BODY_DOCS      = NO
-
-# The INTERNAL_DOCS tag determines if documentation
-# that is typed after a \internal command is included. If the tag is set
-# to NO (the default) then the documentation will be excluded.
-# Set it to YES to include the internal documentation.
-
-INTERNAL_DOCS          = NO
-
-# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
-# file names in lower-case letters. If set to YES upper-case letters are also
-# allowed. This is useful if you have classes or files whose names only differ
-# in case and if your file system supports case sensitive file names. Windows
-# and Mac users are advised to set this option to NO.
-
-CASE_SENSE_NAMES       = YES
-
-# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
-# will show members with their full class and namespace scopes in the
-# documentation. If set to YES the scope will be hidden.
-
-HIDE_SCOPE_NAMES       = NO
-
-# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
-# will put a list of the files that are included by a file in the documentation
-# of that file.
-
-SHOW_INCLUDE_FILES     = NO
-
-# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
-# will list include files with double quotes in the documentation
-# rather than with sharp brackets.
-
-FORCE_LOCAL_INCLUDES   = NO
-
-# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
-# is inserted in the documentation for inline members.
-
-INLINE_INFO            = YES
-
-# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
-# will sort the (detailed) documentation of file and class members
-# alphabetically by member name. If set to NO the members will appear in
-# declaration order.
-
-SORT_MEMBER_DOCS       = YES
-
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
-# brief documentation of file, namespace and class members alphabetically
-# by member name. If set to NO (the default) the members will appear in
-# declaration order.
-
-SORT_BRIEF_DOCS        = NO
-
-# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
-
-SORT_MEMBERS_CTORS_1ST = NO
-
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
-# hierarchy of group names into alphabetical order. If set to NO (the default)
-# the group names will appear in their defined order.
-
-SORT_GROUP_NAMES       = NO
-
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
-# sorted by fully-qualified names, including namespaces. If set to
-# NO (the default), the class list will be sorted only by class name,
-# not including the namespace part.
-# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
-# Note: This option applies only to the class list, not to the
-# alphabetical list.
-
-SORT_BY_SCOPE_NAME     = NO
-
-# The GENERATE_TODOLIST tag can be used to enable (YES) or
-# disable (NO) the todo list. This list is created by putting \todo
-# commands in the documentation.
-
-GENERATE_TODOLIST      = YES
-
-# The GENERATE_TESTLIST tag can be used to enable (YES) or
-# disable (NO) the test list. This list is created by putting \test
-# commands in the documentation.
-
-GENERATE_TESTLIST      = YES
-
-# The GENERATE_BUGLIST tag can be used to enable (YES) or
-# disable (NO) the bug list. This list is created by putting \bug
-# commands in the documentation.
-
-GENERATE_BUGLIST       = YES
-
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
-# disable (NO) the deprecated list. This list is created by putting
-# \deprecated commands in the documentation.
-
-GENERATE_DEPRECATEDLIST= YES
-
-# The ENABLED_SECTIONS tag can be used to enable conditional
-# documentation sections, marked by \if sectionname ... \endif.
-
-ENABLED_SECTIONS       =
-
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
-# the initial value of a variable or define consists of for it to appear in
-# the documentation. If the initializer consists of more lines than specified
-# here it will be hidden. Use a value of 0 to hide initializers completely.
-# The appearance of the initializer of individual variables and defines in the
-# documentation can be controlled using \showinitializer or \hideinitializer
-# command in the documentation regardless of this setting.
-
-MAX_INITIALIZER_LINES  = 30
-
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
-# at the bottom of the documentation of classes and structs. If set to YES the
-# list will mention the files that were used to generate the documentation.
-
-SHOW_USED_FILES        = YES
-
-# If the sources in your project are distributed over multiple directories
-# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
-# in the documentation. The default is NO.
-
-SHOW_DIRECTORIES       = NO
-
-# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
-# This will remove the Files entry from the Quick Index and from the
-# Folder Tree View (if specified). The default is YES.
-
-SHOW_FILES             = YES
-
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
-# Namespaces page.
-# This will remove the Namespaces entry from the Quick Index
-# and from the Folder Tree View (if specified). The default is YES.
-
-SHOW_NAMESPACES        = YES
-
-# The FILE_VERSION_FILTER tag can be used to specify a program or script that
-# doxygen should invoke to get the current version for each file (typically from
-# the version control system). Doxygen will invoke the program by executing (via
-# popen()) the command <command> <input-file>, where <command> is the value of
-# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
-# provided by doxygen. Whatever the program writes to standard output
-# is used as the file version. See the manual for examples.
-
-FILE_VERSION_FILTER    =
-
-# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
-# doxygen. The layout file controls the global structure of the generated output files
-# in an output format independent way. The create the layout file that represents
-# doxygen's defaults, run doxygen with the -l option. You can optionally specify a
-# file name after the option, if omitted DoxygenLayout.xml will be used as the name
-# of the layout file.
-
-LAYOUT_FILE            =
-
-#---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
-#---------------------------------------------------------------------------
-
-# The QUIET tag can be used to turn on/off the messages that are generated
-# by doxygen. Possible values are YES and NO. If left blank NO is used.
-
-QUIET                  = NO
-
-# The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated by doxygen. Possible values are YES and NO. If left blank
-# NO is used.
-
-WARNINGS               = YES
-
-# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
-# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
-# automatically be disabled.
-
-WARN_IF_UNDOCUMENTED   = YES
-
-# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some
-# parameters in a documented function, or documenting parameters that
-# don't exist or using markup commands wrongly.
-
-WARN_IF_DOC_ERROR      = YES
-
-# This WARN_NO_PARAMDOC option can be abled to get warnings for
-# functions that are documented, but have no documentation for their parameters
-# or return value. If set to NO (the default) doxygen will only warn about
-# wrong or incomplete parameter documentation, but not about the absence of
-# documentation.
-
-WARN_NO_PARAMDOC       = NO
-
-# The WARN_FORMAT tag determines the format of the warning messages that
-# doxygen can produce. The string should contain the $file, $line, and $text
-# tags, which will be replaced by the file and line number from which the
-# warning originated and the warning text. Optionally the format may contain
-# $version, which will be replaced by the version of the file (if it could
-# be obtained via FILE_VERSION_FILTER)
-
-WARN_FORMAT            = "$file:$line: $text"
-
-# The WARN_LOGFILE tag can be used to specify a file to which warning
-# and error messages should be written. If left blank the output is written
-# to stderr.
-
-WARN_LOGFILE           =
-
-#---------------------------------------------------------------------------
-# configuration options related to the input files
-#---------------------------------------------------------------------------
-
-# The INPUT tag can be used to specify the files and/or directories that contain
-# documented source files. You may enter file names like "myfile.cpp" or
-# directories like "/usr/src/myproject". Separate the files or directories
-# with spaces.
-
-INPUT                  = ../src/libguac/guacamole
-
-# This tag can be used to specify the character encoding of the source files
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
-# also the default input encoding. Doxygen uses libiconv (or the iconv built
-# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
-# the list of possible encodings.
-
-INPUT_ENCODING         = UTF-8
-
-# If the value of the INPUT tag contains directories, you can use the
-# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank the following patterns are tested:
-# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
-# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
-
-FILE_PATTERNS          = *.h
-
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories
-# should be searched for input files as well. Possible values are YES and NO.
-# If left blank NO is used.
-
-RECURSIVE              = YES 
-
-# The EXCLUDE tag can be used to specify files and/or directories that should
-# excluded from the INPUT source files. This way you can easily exclude a
-# subdirectory from a directory tree whose root is specified with the INPUT tag.
-
-EXCLUDE                =
-
-# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
-# directories that are symbolic links (a Unix filesystem feature) are excluded
-# from the input.
-
-EXCLUDE_SYMLINKS       = NO
-
-# If the value of the INPUT tag contains directories, you can use the
-# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
-# certain files from those directories. Note that the wildcards are matched
-# against the file with absolute path, so to exclude all test directories
-# for example use the pattern */test/*
-
-EXCLUDE_PATTERNS       =
-
-# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
-# (namespaces, classes, functions, etc.) that should be excluded from the
-# output. The symbol name can be a fully qualified name, a word, or if the
-# wildcard * is used, a substring. Examples: ANamespace, AClass,
-# AClass::ANamespace, ANamespace::*Test
-
-EXCLUDE_SYMBOLS        = __* guac_palette*
-
-# The EXAMPLE_PATH tag can be used to specify one or more files or
-# directories that contain example code fragments that are included (see
-# the \include command).
-
-EXAMPLE_PATH           =
-
-# If the value of the EXAMPLE_PATH tag contains directories, you can use the
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank all files are included.
-
-EXAMPLE_PATTERNS       =
-
-# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
-# searched for input files to be used with the \include or \dontinclude
-# commands irrespective of the value of the RECURSIVE tag.
-# Possible values are YES and NO. If left blank NO is used.
-
-EXAMPLE_RECURSIVE      = NO
-
-# The IMAGE_PATH tag can be used to specify one or more files or
-# directories that contain image that are included in the documentation (see
-# the \image command).
-
-IMAGE_PATH             =
-
-# The INPUT_FILTER tag can be used to specify a program that doxygen should
-# invoke to filter for each input file. Doxygen will invoke the filter program
-# by executing (via popen()) the command <filter> <input-file>, where <filter>
-# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
-# input file. Doxygen will then use the output that the filter program writes
-# to standard output.
-# If FILTER_PATTERNS is specified, this tag will be
-# ignored.
-
-INPUT_FILTER           =
-
-# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
-# basis.
-# Doxygen will compare the file name with each pattern and apply the
-# filter if there is a match.
-# The filters are a list of the form:
-# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
-# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
-# is applied to all files.
-
-FILTER_PATTERNS        =
-
-# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
-# INPUT_FILTER) will be used to filter the input files when producing source
-# files to browse (i.e. when SOURCE_BROWSER is set to YES).
-
-FILTER_SOURCE_FILES    = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to source browsing
-#---------------------------------------------------------------------------
-
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will
-# be generated. Documented entities will be cross-referenced with these sources.
-# Note: To get rid of all source code in the generated output, make sure also
-# VERBATIM_HEADERS is set to NO.
-
-SOURCE_BROWSER         = NO
-
-# Setting the INLINE_SOURCES tag to YES will include the body
-# of functions and classes directly in the documentation.
-
-INLINE_SOURCES         = NO
-
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
-# doxygen to hide any special comment blocks from generated source code
-# fragments. Normal C and C++ comments will always remain visible.
-
-STRIP_CODE_COMMENTS    = YES
-
-# If the REFERENCED_BY_RELATION tag is set to YES
-# then for each documented function all documented
-# functions referencing it will be listed.
-
-REFERENCED_BY_RELATION = NO
-
-# If the REFERENCES_RELATION tag is set to YES
-# then for each documented function all documented entities
-# called/used by that function will be listed.
-
-REFERENCES_RELATION    = NO
-
-# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
-# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
-# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
-# link to the source code.
-# Otherwise they will link to the documentation.
-
-REFERENCES_LINK_SOURCE = YES
-
-# If the USE_HTAGS tag is set to YES then the references to source code
-# will point to the HTML generated by the htags(1) tool instead of doxygen
-# built-in source browser. The htags tool is part of GNU's global source
-# tagging system (see http://www.gnu.org/software/global/global.html). You
-# will need version 4.8.6 or higher.
-
-USE_HTAGS              = NO
-
-# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
-# will generate a verbatim copy of the header file for each class for
-# which an include is specified. Set to NO to disable this.
-
-VERBATIM_HEADERS       = YES
-
-#---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
-#---------------------------------------------------------------------------
-
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
-# of all compounds will be generated. Enable this if the project
-# contains a lot of classes, structs, unions or interfaces.
-
-ALPHABETICAL_INDEX     = NO
-
-# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
-# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
-# in which this list will be split (can be a number in the range [1..20])
-
-COLS_IN_ALPHA_INDEX    = 5
-
-# In case all classes in a project start with a common prefix, all
-# classes will be put under the same header in the alphabetical index.
-# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
-# should be ignored while generating the index headers.
-
-IGNORE_PREFIX          =
-
-#---------------------------------------------------------------------------
-# configuration options related to the HTML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
-# generate HTML output.
-
-GENERATE_HTML          = YES
-
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `html' will be used as the default path.
-
-HTML_OUTPUT            = html
-
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
-# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
-# doxygen will generate files with .html extension.
-
-HTML_FILE_EXTENSION    = .html
-
-# The HTML_HEADER tag can be used to specify a personal HTML header for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard header.
-
-HTML_HEADER            =
-
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard footer.
-
-HTML_FOOTER            =
-
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
-# style sheet that is used by each HTML page. It can be used to
-# fine-tune the look of the HTML output. If the tag is left blank doxygen
-# will generate a default style sheet. Note that doxygen will try to copy
-# the style sheet file to the HTML output directory, so don't put your own
-# stylesheet in the HTML output directory as well, or it will be erased!
-
-HTML_STYLESHEET        =
-
-# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
-# page will contain the date and time when the page was generated. Setting
-# this to NO can help when comparing the output of multiple runs.
-
-HTML_TIMESTAMP         = YES
-
-# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
-# files or namespaces will be aligned in HTML using tables. If set to
-# NO a bullet list will be used.
-
-HTML_ALIGN_MEMBERS     = YES
-
-# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
-# documentation will contain sections that can be hidden and shown after the
-# page has loaded. For this to work a browser that supports
-# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
-# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
-
-HTML_DYNAMIC_SECTIONS  = NO
-
-# If the GENERATE_DOCSET tag is set to YES, additional index files
-# will be generated that can be used as input for Apple's Xcode 3
-# integrated development environment, introduced with OSX 10.5 (Leopard).
-# To create a documentation set, doxygen will generate a Makefile in the
-# HTML output directory. Running make will produce the docset in that
-# directory and running "make install" will install the docset in
-# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
-# it at startup.
-# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
-
-GENERATE_DOCSET        = NO
-
-# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
-# feed. A documentation feed provides an umbrella under which multiple
-# documentation sets from a single provider (such as a company or product suite)
-# can be grouped.
-
-DOCSET_FEEDNAME        = "Doxygen generated docs"
-
-# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
-# should uniquely identify the documentation set bundle. This should be a
-# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
-# will append .docset to the name.
-
-DOCSET_BUNDLE_ID       = org.doxygen.Project
-
-# If the GENERATE_HTMLHELP tag is set to YES, additional index files
-# will be generated that can be used as input for tools like the
-# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
-# of the generated HTML documentation.
-
-GENERATE_HTMLHELP      = NO
-
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
-# be used to specify the file name of the resulting .chm file. You
-# can add a path in front of the file if the result should not be
-# written to the html output directory.
-
-CHM_FILE               =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
-# be used to specify the location (absolute path including file name) of
-# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
-# the HTML help compiler on the generated index.hhp.
-
-HHC_LOCATION           =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
-# controls if a separate .chi index file is generated (YES) or that
-# it should be included in the master .chm file (NO).
-
-GENERATE_CHI           = NO
-
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
-# is used to encode HtmlHelp index (hhk), content (hhc) and project file
-# content.
-
-CHM_INDEX_ENCODING     =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
-# controls whether a binary table of contents is generated (YES) or a
-# normal table of contents (NO) in the .chm file.
-
-BINARY_TOC             = NO
-
-# The TOC_EXPAND flag can be set to YES to add extra items for group members
-# to the contents of the HTML help documentation and to the tree view.
-
-TOC_EXPAND             = NO
-
-# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
-# are set, an additional index file will be generated that can be used as input for
-# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
-# HTML documentation.
-
-GENERATE_QHP           = NO
-
-# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
-# be used to specify the file name of the resulting .qch file.
-# The path specified is relative to the HTML output folder.
-
-QCH_FILE               =
-
-# The QHP_NAMESPACE tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#namespace
-
-QHP_NAMESPACE          = org.doxygen.Project
-
-# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#virtual-folders
-
-QHP_VIRTUAL_FOLDER     = doc
-
-# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
-# For more information please see
-# http://doc.trolltech.com/qthelpproject.html#custom-filters
-
-QHP_CUST_FILTER_NAME   =
-
-# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see
-# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
-
-QHP_CUST_FILTER_ATTRS  =
-
-# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
-# filter section matches.
-# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
-
-QHP_SECT_FILTER_ATTRS  =
-
-# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
-# be used to specify the location of Qt's qhelpgenerator.
-# If non-empty doxygen will try to run qhelpgenerator on the generated
-# .qhp file.
-
-QHG_LOCATION           =
-
-# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
-#  will be generated, which together with the HTML files, form an Eclipse help
-#  plugin. To install this plugin and make it available under the help contents
-# menu in Eclipse, the contents of the directory containing the HTML and XML
-# files needs to be copied into the plugins directory of eclipse. The name of
-# the directory within the plugins directory should be the same as
-# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before the help appears.
-
-GENERATE_ECLIPSEHELP   = NO
-
-# A unique identifier for the eclipse help plugin. When installing the plugin
-# the directory name containing the HTML and XML files should also have
-# this name.
-
-ECLIPSE_DOC_ID         = org.doxygen.Project
-
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
-# top of each HTML page. The value NO (the default) enables the index and
-# the value YES disables it.
-
-DISABLE_INDEX          = NO
-
-# This tag can be used to set the number of enum values (range [1..20])
-# that doxygen will group on one line in the generated HTML documentation.
-
-ENUM_VALUES_PER_LINE   = 4
-
-# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
-# structure should be generated to display hierarchical information.
-# If the tag value is set to YES, a side panel will be generated
-# containing a tree-like index structure (just like the one that
-# is generated for HTML Help). For this to work a browser that supports
-# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
-# Windows users are probably better off using the HTML help feature.
-
-GENERATE_TREEVIEW      = NO
-
-# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
-# and Class Hierarchy pages using a tree view instead of an ordered list.
-
-USE_INLINE_TREES       = NO
-
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
-# used to set the initial width (in pixels) of the frame in which the tree
-# is shown.
-
-TREEVIEW_WIDTH         = 250
-
-# Use this tag to change the font size of Latex formulas included
-# as images in the HTML documentation. The default is 10. Note that
-# when you change the font size after a successful doxygen run you need
-# to manually remove any form_*.png images from the HTML output directory
-# to force them to be regenerated.
-
-FORMULA_FONTSIZE       = 10
-
-# When the SEARCHENGINE tag is enabled doxygen will generate a search box for the HTML output. The underlying search engine uses javascript
-# and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) there is already a search function so this one should
-# typically be disabled. For large projects the javascript based search engine
-# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
-
-SEARCHENGINE           = YES
-
-# When the SERVER_BASED_SEARCH tag is enabled the search engine will be implemented using a PHP enabled web server instead of at the web client using Javascript. Doxygen will generate the search PHP script and index
-# file to put on the web server. The advantage of the server based approach is that it scales better to large projects and allows full text search. The disadvances is that it is more difficult to setup
-# and does not have live searching capabilities.
-
-SERVER_BASED_SEARCH    = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
-# generate Latex output.
-
-GENERATE_LATEX         = YES
-
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `latex' will be used as the default path.
-
-LATEX_OUTPUT           = latex
-
-# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
-# invoked. If left blank `latex' will be used as the default command name.
-# Note that when enabling USE_PDFLATEX this option is only used for
-# generating bitmaps for formulas in the HTML output, but not in the
-# Makefile that is written to the output directory.
-
-LATEX_CMD_NAME         = latex
-
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
-# generate index for LaTeX. If left blank `makeindex' will be used as the
-# default command name.
-
-MAKEINDEX_CMD_NAME     = makeindex
-
-# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
-# LaTeX documents. This may be useful for small projects and may help to
-# save some trees in general.
-
-COMPACT_LATEX          = NO
-
-# The PAPER_TYPE tag can be used to set the paper type that is used
-# by the printer. Possible values are: a4, a4wide, letter, legal and
-# executive. If left blank a4wide will be used.
-
-PAPER_TYPE             = a4wide
-
-# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
-# packages that should be included in the LaTeX output.
-
-EXTRA_PACKAGES         =
-
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
-# the generated latex document. The header should contain everything until
-# the first chapter. If it is left blank doxygen will generate a
-# standard header. Notice: only use this tag if you know what you are doing!
-
-LATEX_HEADER           =
-
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
-# is prepared for conversion to pdf (using ps2pdf). The pdf file will
-# contain links (just like the HTML output) instead of page references
-# This makes the output suitable for online browsing using a pdf viewer.
-
-PDF_HYPERLINKS         = YES
-
-# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
-# plain latex in the generated Makefile. Set this option to YES to get a
-# higher quality PDF documentation.
-
-USE_PDFLATEX           = YES
-
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
-# command to the generated LaTeX files. This will instruct LaTeX to keep
-# running if errors occur, instead of asking the user for help.
-# This option is also used when generating formulas in HTML.
-
-LATEX_BATCHMODE        = NO
-
-# If LATEX_HIDE_INDICES is set to YES then doxygen will not
-# include the index chapters (such as File Index, Compound Index, etc.)
-# in the output.
-
-LATEX_HIDE_INDICES     = NO
-
-# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER.
-
-LATEX_SOURCE_CODE      = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the RTF output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
-# The RTF output is optimized for Word 97 and may not look very pretty with
-# other RTF readers or editors.
-
-GENERATE_RTF           = NO
-
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `rtf' will be used as the default path.
-
-RTF_OUTPUT             = rtf
-
-# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
-# RTF documents. This may be useful for small projects and may help to
-# save some trees in general.
-
-COMPACT_RTF            = NO
-
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
-# will contain hyperlink fields. The RTF file will
-# contain links (just like the HTML output) instead of page references.
-# This makes the output suitable for online browsing using WORD or other
-# programs which support those fields.
-# Note: wordpad (write) and others do not support links.
-
-RTF_HYPERLINKS         = NO
-
-# Load stylesheet definitions from file. Syntax is similar to doxygen's
-# config file, i.e. a series of assignments. You only have to provide
-# replacements, missing definitions are set to their default value.
-
-RTF_STYLESHEET_FILE    =
-
-# Set optional variables used in the generation of an rtf document.
-# Syntax is similar to doxygen's config file.
-
-RTF_EXTENSIONS_FILE    =
-
-#---------------------------------------------------------------------------
-# configuration options related to the man page output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
-# generate man pages
-
-GENERATE_MAN           = NO
-
-# The MAN_OUTPUT tag is used to specify where the man pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `man' will be used as the default path.
-
-MAN_OUTPUT             = man
-
-# The MAN_EXTENSION tag determines the extension that is added to
-# the generated man pages (default is the subroutine's section .3)
-
-MAN_EXTENSION          = .3
-
-# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
-# then it will generate one additional man file for each entity
-# documented in the real man page(s). These additional files
-# only source the real man page, but without them the man command
-# would be unable to find the correct page. The default is NO.
-
-MAN_LINKS              = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the XML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_XML tag is set to YES Doxygen will
-# generate an XML file that captures the structure of
-# the code including all documentation.
-
-GENERATE_XML           = YES
-
-# The XML_OUTPUT tag is used to specify where the XML pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `xml' will be used as the default path.
-
-XML_OUTPUT             = xml
-
-# The XML_SCHEMA tag can be used to specify an XML schema,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
-
-XML_SCHEMA             =
-
-# The XML_DTD tag can be used to specify an XML DTD,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
-
-XML_DTD                =
-
-# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
-# dump the program listings (including syntax highlighting
-# and cross-referencing information) to the XML output. Note that
-# enabling this will significantly increase the size of the XML output.
-
-XML_PROGRAMLISTING     = YES
-
-#---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
-# generate an AutoGen Definitions (see autogen.sf.net) file
-# that captures the structure of the code including all
-# documentation. Note that this feature is still experimental
-# and incomplete at the moment.
-
-GENERATE_AUTOGEN_DEF   = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the Perl module output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_PERLMOD tag is set to YES Doxygen will
-# generate a Perl module file that captures the structure of
-# the code including all documentation. Note that this
-# feature is still experimental and incomplete at the
-# moment.
-
-GENERATE_PERLMOD       = NO
-
-# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
-# the necessary Makefile rules, Perl scripts and LaTeX code to be able
-# to generate PDF and DVI output from the Perl module output.
-
-PERLMOD_LATEX          = NO
-
-# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
-# nicely formatted so it can be parsed by a human reader.
-# This is useful
-# if you want to understand what is going on.
-# On the other hand, if this
-# tag is set to NO the size of the Perl module output will be much smaller
-# and Perl will parse it just the same.
-
-PERLMOD_PRETTY         = YES
-
-# The names of the make variables in the generated doxyrules.make file
-# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
-# This is useful so different doxyrules.make files included by the same
-# Makefile don't overwrite each other's variables.
-
-PERLMOD_MAKEVAR_PREFIX =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the preprocessor
-#---------------------------------------------------------------------------
-
-# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
-# evaluate all C-preprocessor directives found in the sources and include
-# files.
-
-ENABLE_PREPROCESSING   = YES
-
-# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
-# names in the source code. If set to NO (the default) only conditional
-# compilation will be performed. Macro expansion can be done in a controlled
-# way by setting EXPAND_ONLY_PREDEF to YES.
-
-MACRO_EXPANSION        = NO
-
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
-# then the macro expansion is limited to the macros specified with the
-# PREDEFINED and EXPAND_AS_DEFINED tags.
-
-EXPAND_ONLY_PREDEF     = NO
-
-# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
-# in the INCLUDE_PATH (see below) will be search if a #include is found.
-
-SEARCH_INCLUDES        = YES
-
-# The INCLUDE_PATH tag can be used to specify one or more directories that
-# contain include files that are not input files but should be processed by
-# the preprocessor.
-
-INCLUDE_PATH           =
-
-# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
-# patterns (like *.h and *.hpp) to filter out the header-files in the
-# directories. If left blank, the patterns specified with FILE_PATTERNS will
-# be used.
-
-INCLUDE_FILE_PATTERNS  =
-
-# The PREDEFINED tag can be used to specify one or more macro names that
-# are defined before the preprocessor is started (similar to the -D option of
-# gcc). The argument of the tag is a list of macros of the form: name
-# or name=definition (no spaces). If the definition and the = are
-# omitted =1 is assumed. To prevent a macro definition from being
-# undefined via #undef or recursively expanded use the := operator
-# instead of the = operator.
-
-PREDEFINED             =
-
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
-# this tag can be used to specify a list of macro names that should be expanded.
-# The macro definition that is found in the sources will be used.
-# Use the PREDEFINED tag if you want to use a different macro definition.
-
-EXPAND_AS_DEFINED      =
-
-# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
-# doxygen's preprocessor will remove all function-like macros that are alone
-# on a line, have an all uppercase name, and do not end with a semicolon. Such
-# function macros are typically used for boiler-plate code, and will confuse
-# the parser if not removed.
-
-SKIP_FUNCTION_MACROS   = YES
+# Project name / version
+#
 
-#---------------------------------------------------------------------------
-# Configuration::additions related to external references
-#---------------------------------------------------------------------------
+PROJECT_NAME   = libguac
+PROJECT_NUMBER = 0.9.9
 
-# The TAGFILES option can be used to specify one or more tagfiles.
-# Optionally an initial location of the external documentation
-# can be added for each tagfile. The format of a tag file without
-# this location is as follows:
 #
-# TAGFILES = file1 file2 ...
-# Adding location for the tag files is done as follows:
+# Warn about undocumented parameters and return values, but do not fill output
+# with verbose progress info.
 #
-# TAGFILES = file1=loc1 "file2 = loc2" ...
-# where "loc1" and "loc2" can be relative or absolute paths or
-# URLs. If a location is present for each tag, the installdox tool
-# does not have to be run to correct the links.
-# Note that each tag file must have a unique name
-# (where the name does NOT include the path)
-# If a tag file is not located in the directory in which doxygen
-# is run, you must also specify the path to the tagfile here.
-
-TAGFILES               =
-
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create
-# a tag file that is based on the input files it reads.
-
-GENERATE_TAGFILE       =
-
-# If the ALLEXTERNALS tag is set to YES all external classes will be listed
-# in the class index. If set to NO only the inherited external classes
-# will be listed.
-
-ALLEXTERNALS           = NO
-
-# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
-# in the modules index. If set to NO, only the current project's groups will
-# be listed.
-
-EXTERNAL_GROUPS        = YES
-
-# The PERL_PATH should be the absolute path and name of the perl script
-# interpreter (i.e. the result of `which perl').
 
-PERL_PATH              = /usr/bin/perl
+QUIET            = YES
+WARN_NO_PARAMDOC = YES
 
-#---------------------------------------------------------------------------
-# Configuration options related to the dot tool
-#---------------------------------------------------------------------------
-
-# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
-# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
-# or super classes. Setting the tag to NO turns the diagrams off. Note that
-# this option is superseded by the HAVE_DOT option below. This is only a
-# fallback. It is recommended to install and use dot, since it yields more
-# powerful graphs.
-
-CLASS_DIAGRAMS         = YES
-
-# You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see
-# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
-# documentation. The MSCGEN_PATH tag allows you to specify the directory where
-# the mscgen tool resides. If left empty the tool is assumed to be found in the
-# default search path.
-
-MSCGEN_PATH            =
-
-# If set to YES, the inheritance and collaboration graphs will hide
-# inheritance and usage relations if the target is undocumented
-# or is not a class.
-
-HIDE_UNDOC_RELATIONS   = YES
-
-# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
-# available from the path. This tool is part of Graphviz, a graph visualization
-# toolkit from AT&T and Lucent Bell Labs. The other options in this section
-# have no effect if this option is set to NO (the default)
-
-HAVE_DOT               = NO
-
-# By default doxygen will write a font called FreeSans.ttf to the output
-# directory and reference it in all dot files that doxygen generates. This
-# font does not include all possible unicode characters however, so when you need
-# these (or just want a differently looking font) you can specify the font name
-# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
-# which can be done by putting it in a standard location or by setting the
-# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
-# containing the font.
-
-DOT_FONTNAME           = FreeSans
-
-# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
-# The default size is 10pt.
-
-DOT_FONTSIZE           = 10
-
-# By default doxygen will tell dot to use the output directory to look for the
-# FreeSans.ttf font (which doxygen will put there itself). If you specify a
-# different font using DOT_FONTNAME you can set the path where dot
-# can find it using this tag.
-
-DOT_FONTPATH           =
-
-# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect inheritance relations. Setting this tag to YES will force the
-# the CLASS_DIAGRAMS tag to NO.
-
-CLASS_GRAPH            = YES
-
-# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect implementation dependencies (inheritance, containment, and
-# class references variables) of the class with other documented classes.
-
-COLLABORATION_GRAPH    = YES
-
-# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for groups, showing the direct groups dependencies
-
-GROUP_GRAPHS           = YES
-
-# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
-# collaboration diagrams in a style similar to the OMG's Unified Modeling
-# Language.
-
-UML_LOOK               = NO
-
-# If set to YES, the inheritance and collaboration graphs will show the
-# relations between templates and their instances.
-
-TEMPLATE_RELATIONS     = NO
-
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
-# tags are set to YES then doxygen will generate a graph for each documented
-# file showing the direct and indirect include dependencies of the file with
-# other documented files.
-
-INCLUDE_GRAPH          = YES
-
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
-# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
-# documented header file showing the documented files that directly or
-# indirectly include this file.
-
-INCLUDED_BY_GRAPH      = YES
-
-# If the CALL_GRAPH and HAVE_DOT options are set to YES then
-# doxygen will generate a call dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable call graphs
-# for selected functions only using the \callgraph command.
-
-CALL_GRAPH             = NO
-
-# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
-# doxygen will generate a caller dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable caller
-# graphs for selected functions only using the \callergraph command.
-
-CALLER_GRAPH           = NO
-
-# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
-# will graphical hierarchy of all classes instead of a textual one.
-
-GRAPHICAL_HIERARCHY    = YES
-
-# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
-# then doxygen will show the dependencies a directory has on other directories
-# in a graphical way. The dependency relations are determined by the #include
-# relations between the files in the directories.
-
-DIRECTORY_GRAPH        = YES
-
-# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot. Possible values are png, jpg, or gif
-# If left blank png will be used.
-
-DOT_IMAGE_FORMAT       = png
-
-# The tag DOT_PATH can be used to specify the path where the dot tool can be
-# found. If left blank, it is assumed the dot tool can be found in the path.
-
-DOT_PATH               =
-
-# The DOTFILE_DIRS tag can be used to specify one or more directories that
-# contain dot files that are included in the documentation (see the
-# \dotfile command).
-
-DOTFILE_DIRS           =
-
-# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
-# nodes that will be shown in the graph. If the number of nodes in a graph
-# becomes larger than this value, doxygen will truncate the graph, which is
-# visualized by representing a node as a red box. Note that doxygen if the
-# number of direct children of the root node in a graph is already larger than
-# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
-# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
-
-DOT_GRAPH_MAX_NODES    = 50
-
-# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
-# graphs generated by dot. A depth value of 3 means that only nodes reachable
-# from the root by following a path via at most 3 edges will be shown. Nodes
-# that lay further from the root node will be omitted. Note that setting this
-# option to 1 or 2 may greatly reduce the computation time needed for large
-# code bases. Also note that the size of a graph can be further restricted by
-# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
-
-MAX_DOT_GRAPH_DEPTH    = 0
-
-# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
-# background. This is disabled by default, because dot on Windows does not
-# seem to support this out of the box. Warning: Depending on the platform used,
-# enabling this option may lead to badly anti-aliased labels on the edges of
-# a graph (i.e. they become hard to read).
-
-DOT_TRANSPARENT        = NO
-
-# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
-# files in one run (i.e. multiple -o and -T options on the command line). This
-# makes dot run faster, but since only newer versions of dot (>1.8.10)
-# support this, this feature is disabled by default.
-
-DOT_MULTI_TARGETS      = YES
+#
+# Output format
+#
 
-# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
-# generate a legend page explaining the meaning of the various boxes and
-# arrows in the dot generated graphs.
+ALPHABETICAL_INDEX    = YES
+GENERATE_HTML         = YES
+GENERATE_LATEX        = NO
+IGNORE_PREFIX         = guac_ vguac_
+OPTIMIZE_OUTPUT_FOR_C = YES
+OUTPUT_DIRECTORY      = doxygen-output
+RECURSIVE             = YES 
+SHOW_INCLUDE_FILES    = NO
 
-GENERATE_LEGEND        = YES
+#
+# Input format
+#
 
-# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
-# remove the intermediate dot files that are used to generate
-# the various graphs.
+CASE_SENSE_NAMES     = YES
+EXCLUDE_SYMBOLS      = __* guac_palette*
+FILE_PATTERNS        = *.h
+INPUT                = ../src/libguac/guacamole
+JAVADOC_AUTOBRIEF    = YES
+STRIP_FROM_PATH      = ../src/libguac
+TAB_SIZE             = 4
+TYPEDEF_HIDES_STRUCT = YES
 
-DOT_CLEANUP            = YES
diff --git a/install-sh b/install-sh
index a9244eb..377bb86 100755
--- a/install-sh
+++ b/install-sh
@@ -1,7 +1,7 @@
 #!/bin/sh
 # install - install a program, script, or datafile
 
-scriptversion=2011-01-19.21; # UTC
+scriptversion=2011-11-20.07; # UTC
 
 # This originates from X11R5 (mit/util/scripts/install.sh), which was
 # later released in X11R6 (xc/config/util/install.sh) with the
@@ -35,7 +35,7 @@ scriptversion=2011-01-19.21; # UTC
 # FSF changes to this file are in the public domain.
 #
 # Calling this script install-sh is preferred over install.sh, to prevent
-# `make' implicit rules from creating a file called install from it
+# 'make' implicit rules from creating a file called install from it
 # when there is no Makefile.
 #
 # This script is compatible with the BSD install script, but was written
@@ -156,7 +156,7 @@ while test $# -ne 0; do
     -s) stripcmd=$stripprog;;
 
     -t) dst_arg=$2
-	# Protect names problematic for `test' and other utilities.
+	# Protect names problematic for 'test' and other utilities.
 	case $dst_arg in
 	  -* | [=\(\)!]) dst_arg=./$dst_arg;;
 	esac
@@ -190,7 +190,7 @@ if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
     fi
     shift # arg
     dst_arg=$arg
-    # Protect names problematic for `test' and other utilities.
+    # Protect names problematic for 'test' and other utilities.
     case $dst_arg in
       -* | [=\(\)!]) dst_arg=./$dst_arg;;
     esac
@@ -202,7 +202,7 @@ if test $# -eq 0; then
     echo "$0: no input file specified." >&2
     exit 1
   fi
-  # It's OK to call `install-sh -d' without argument.
+  # It's OK to call 'install-sh -d' without argument.
   # This can happen when creating conditional directories.
   exit 0
 fi
@@ -240,7 +240,7 @@ fi
 
 for src
 do
-  # Protect names problematic for `test' and other utilities.
+  # Protect names problematic for 'test' and other utilities.
   case $src in
     -* | [=\(\)!]) src=./$src;;
   esac
@@ -354,7 +354,7 @@ do
 	      if test -z "$dir_arg" || {
 		   # Check for POSIX incompatibilities with -m.
 		   # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
-		   # other-writeable bit of parent directory when it shouldn't.
+		   # other-writable bit of parent directory when it shouldn't.
 		   # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
 		   ls_ld_tmpdir=`ls -ld "$tmpdir"`
 		   case $ls_ld_tmpdir in
diff --git a/ltmain.sh b/ltmain.sh
index 33f642a..63ae69d 100644
--- a/ltmain.sh
+++ b/ltmain.sh
@@ -70,7 +70,7 @@
 #         compiler:		$LTCC
 #         compiler flags:		$LTCFLAGS
 #         linker:		$LD (gnu? $with_gnu_ld)
-#         $progname:	(GNU libtool) 2.4.2 Debian-2.4.2-1.1
+#         $progname:	(GNU libtool) 2.4.2
 #         automake:	$automake_version
 #         autoconf:	$autoconf_version
 #
@@ -80,7 +80,7 @@
 
 PROGRAM=libtool
 PACKAGE=libtool
-VERSION="2.4.2 Debian-2.4.2-1.1"
+VERSION=2.4.2
 TIMESTAMP=""
 package_revision=1.3337
 
@@ -6124,10 +6124,7 @@ func_mode_link ()
 	case $pass in
 	dlopen) libs="$dlfiles" ;;
 	dlpreopen) libs="$dlprefiles" ;;
-	link)
-	  libs="$deplibs %DEPLIBS%"
-	  test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs"
-	  ;;
+	link) libs="$deplibs %DEPLIBS% $dependency_libs" ;;
 	esac
       fi
       if test "$linkmode,$pass" = "lib,dlpreopen"; then
@@ -6447,19 +6444,19 @@ func_mode_link ()
 	    # It is a libtool convenience library, so add in its objects.
 	    func_append convenience " $ladir/$objdir/$old_library"
 	    func_append old_convenience " $ladir/$objdir/$old_library"
-	    tmp_libs=
-	    for deplib in $dependency_libs; do
-	      deplibs="$deplib $deplibs"
-	      if $opt_preserve_dup_deps ; then
-		case "$tmp_libs " in
-		*" $deplib "*) func_append specialdeplibs " $deplib" ;;
-		esac
-	      fi
-	      func_append tmp_libs " $deplib"
-	    done
 	  elif test "$linkmode" != prog && test "$linkmode" != lib; then
 	    func_fatal_error "\`$lib' is not a convenience library"
 	  fi
+	  tmp_libs=
+	  for deplib in $dependency_libs; do
+	    deplibs="$deplib $deplibs"
+	    if $opt_preserve_dup_deps ; then
+	      case "$tmp_libs " in
+	      *" $deplib "*) func_append specialdeplibs " $deplib" ;;
+	      esac
+	    fi
+	    func_append tmp_libs " $deplib"
+	  done
 	  continue
 	fi # $pass = conv
 
@@ -7352,9 +7349,6 @@ func_mode_link ()
 	    revision="$number_minor"
 	    lt_irix_increment=no
 	    ;;
-	  *)
-	    func_fatal_configuration "$modename: unknown library version type \`$version_type'"
-	    ;;
 	  esac
 	  ;;
 	no)
diff --git a/m4/libtool.m4 b/m4/libtool.m4
index 534d1cc..f12cfdf 100644
--- a/m4/libtool.m4
+++ b/m4/libtool.m4
@@ -1312,7 +1312,7 @@ ia64-*-hpux*)
   rm -rf conftest*
   ;;
 
-x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \
 s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
   # Find out which ABI we are using.
   echo 'int i;' > conftest.$ac_ext
@@ -1326,7 +1326,10 @@ s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
 	  x86_64-*linux*)
 	    LD="${LD-ld} -m elf_i386"
 	    ;;
-	  ppc64-*linux*|powerpc64-*linux*)
+	  powerpc64le-*linux*)
+	    LD="${LD-ld} -m elf32lppclinux"
+	    ;;
+	  powerpc64-*linux*)
 	    LD="${LD-ld} -m elf32ppclinux"
 	    ;;
 	  s390x-*linux*)
@@ -1345,7 +1348,10 @@ s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
 	  x86_64-*linux*)
 	    LD="${LD-ld} -m elf_x86_64"
 	    ;;
-	  ppc*-*linux*|powerpc*-*linux*)
+	  powerpcle-*linux*)
+	    LD="${LD-ld} -m elf64lppc"
+	    ;;
+	  powerpc-*linux*)
 	    LD="${LD-ld} -m elf64ppc"
 	    ;;
 	  s390*-*linux*|s390*-*tpf*)
@@ -2512,6 +2518,17 @@ freebsd* | dragonfly*)
   esac
   ;;
 
+gnu*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
 haiku*)
   version_type=linux # correct to gnu/linux during the next big refactor
   need_lib_prefix=no
@@ -2628,7 +2645,7 @@ linux*oldld* | linux*aout* | linux*coff*)
   ;;
 
 # This must be glibc/ELF.
-linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+linux* | k*bsd*-gnu | kopensolaris*-gnu)
   version_type=linux # correct to gnu/linux during the next big refactor
   need_lib_prefix=no
   need_version=no
@@ -2658,10 +2675,14 @@ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
   # before this can be enabled.
   hardcode_into_libs=yes
 
+  # Add ABI-specific directories to the system library path.
+  sys_lib_dlsearch_path_spec="/lib64 /usr/lib64 /lib /usr/lib"
+
   # Append ld.so.conf contents to the search path
   if test -f /etc/ld.so.conf; then
     lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[	 ]*hwcap[	 ]/d;s/[:,	]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '`
-    sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+    sys_lib_dlsearch_path_spec="$sys_lib_dlsearch_path_spec $lt_ld_extra"
+
   fi
 
   # We used to test for /lib/ld.so.1 and disable shared libraries on
@@ -2673,18 +2694,6 @@ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
   dynamic_linker='GNU/Linux ld.so'
   ;;
 
-netbsdelf*-gnu)
-  version_type=linux
-  need_lib_prefix=no
-  need_version=no
-  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
-  soname_spec='${libname}${release}${shared_ext}$major'
-  shlibpath_var=LD_LIBRARY_PATH
-  shlibpath_overrides_runpath=no
-  hardcode_into_libs=yes
-  dynamic_linker='NetBSD ld.elf_so'
-  ;;
-
 netbsd*)
   version_type=sunos
   need_lib_prefix=no
@@ -3244,6 +3253,10 @@ freebsd* | dragonfly*)
   fi
   ;;
 
+gnu*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
 haiku*)
   lt_cv_deplibs_check_method=pass_all
   ;;
@@ -3282,11 +3295,11 @@ irix5* | irix6* | nonstopux*)
   ;;
 
 # This must be glibc/ELF.
-linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+linux* | k*bsd*-gnu | kopensolaris*-gnu)
   lt_cv_deplibs_check_method=pass_all
   ;;
 
-netbsd* | netbsdelf*-gnu)
+netbsd*)
   if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
     lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
   else
@@ -4034,7 +4047,7 @@ m4_if([$1], [CXX], [
 	    ;;
 	esac
 	;;
-      linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+      linux* | k*bsd*-gnu | kopensolaris*-gnu)
 	case $cc_basename in
 	  KCC*)
 	    # KAI C++ Compiler
@@ -4098,7 +4111,7 @@ m4_if([$1], [CXX], [
 	    ;;
 	esac
 	;;
-      netbsd* | netbsdelf*-gnu)
+      netbsd*)
 	;;
       *qnx* | *nto*)
         # QNX uses GNU C++, but need to define -shared option too, otherwise
@@ -4333,7 +4346,7 @@ m4_if([$1], [CXX], [
       _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
       ;;
 
-    linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+    linux* | k*bsd*-gnu | kopensolaris*-gnu)
       case $cc_basename in
       # old Intel for x86_64 which still supported -KPIC.
       ecc*)
@@ -4575,9 +4588,6 @@ m4_if([$1], [CXX], [
       ;;
     esac
     ;;
-  linux* | k*bsd*-gnu | gnu*)
-    _LT_TAGVAR(link_all_deplibs, $1)=no
-    ;;
   *)
     _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
     ;;
@@ -4640,9 +4650,6 @@ dnl Note also adjust exclude_expsyms for C++ above.
   openbsd*)
     with_gnu_ld=no
     ;;
-  linux* | k*bsd*-gnu | gnu*)
-    _LT_TAGVAR(link_all_deplibs, $1)=no
-    ;;
   esac
 
   _LT_TAGVAR(ld_shlibs, $1)=yes
@@ -4864,7 +4871,7 @@ _LT_EOF
       fi
       ;;
 
-    netbsd* | netbsdelf*-gnu)
+    netbsd*)
       if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
 	_LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
 	wlarc=
@@ -5041,7 +5048,6 @@ _LT_EOF
 	if test "$aix_use_runtimelinking" = yes; then
 	  shared_flag="$shared_flag "'${wl}-G'
 	fi
-	_LT_TAGVAR(link_all_deplibs, $1)=no
       else
 	# not using gcc
 	if test "$host_cpu" = ia64; then
@@ -5346,7 +5352,7 @@ _LT_EOF
       _LT_TAGVAR(link_all_deplibs, $1)=yes
       ;;
 
-    netbsd* | netbsdelf*-gnu)
+    netbsd*)
       if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
 	_LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'  # a.out
       else
@@ -6226,6 +6232,9 @@ if test "$_lt_caught_CXX_error" != yes; then
         _LT_TAGVAR(ld_shlibs, $1)=yes
         ;;
 
+      gnu*)
+        ;;
+
       haiku*)
         _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
         _LT_TAGVAR(link_all_deplibs, $1)=yes
@@ -6387,7 +6396,7 @@ if test "$_lt_caught_CXX_error" != yes; then
         _LT_TAGVAR(inherit_rpath, $1)=yes
         ;;
 
-      linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+      linux* | k*bsd*-gnu | kopensolaris*-gnu)
         case $cc_basename in
           KCC*)
 	    # Kuck and Associates, Inc. (KAI) C++ Compiler
diff --git a/missing b/missing
index 86a8fc3..db98974 100755
--- a/missing
+++ b/missing
@@ -1,11 +1,10 @@
 #! /bin/sh
-# Common stub for a few missing GNU programs while installing.
+# Common wrapper for a few potentially missing GNU programs.
 
-scriptversion=2012-01-06.13; # UTC
+scriptversion=2013-10-28.13; # UTC
 
-# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006,
-# 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
-# Originally by Fran,cois Pinard <pinard at iro.umontreal.ca>, 1996.
+# Copyright (C) 1996-2013 Free Software Foundation, Inc.
+# Originally written by Fran,cois Pinard <pinard at iro.umontreal.ca>, 1996.
 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -26,68 +25,40 @@ scriptversion=2012-01-06.13; # UTC
 # the same distribution terms that you use for the rest of that program.
 
 if test $# -eq 0; then
-  echo 1>&2 "Try \`$0 --help' for more information"
+  echo 1>&2 "Try '$0 --help' for more information"
   exit 1
 fi
 
-run=:
-sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p'
-sed_minuso='s/.* -o \([^ ]*\).*/\1/p'
-
-# In the cases where this matters, `missing' is being run in the
-# srcdir already.
-if test -f configure.ac; then
-  configure_ac=configure.ac
-else
-  configure_ac=configure.in
-fi
+case $1 in
 
-msg="missing on your system"
+  --is-lightweight)
+    # Used by our autoconf macros to check whether the available missing
+    # script is modern enough.
+    exit 0
+    ;;
 
-case $1 in
---run)
-  # Try to run requested program, and just exit if it succeeds.
-  run=
-  shift
-  "$@" && exit 0
-  # Exit code 63 means version mismatch.  This often happens
-  # when the user try to use an ancient version of a tool on
-  # a file that requires a minimum version.  In this case we
-  # we should proceed has if the program had been absent, or
-  # if --run hadn't been passed.
-  if test $? = 63; then
-    run=:
-    msg="probably too old"
-  fi
-  ;;
+  --run)
+    # Back-compat with the calling convention used by older automake.
+    shift
+    ;;
 
   -h|--h|--he|--hel|--help)
     echo "\
 $0 [OPTION]... PROGRAM [ARGUMENT]...
 
-Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
-error status if there is no known handling for PROGRAM.
+Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due
+to PROGRAM being missing or too old.
 
 Options:
   -h, --help      display this help and exit
   -v, --version   output version information and exit
-  --run           try to run the given command, and emulate it if it fails
 
 Supported PROGRAM values:
-  aclocal      touch file \`aclocal.m4'
-  autoconf     touch file \`configure'
-  autoheader   touch file \`config.h.in'
-  autom4te     touch the output file, or create a stub one
-  automake     touch all \`Makefile.in' files
-  bison        create \`y.tab.[ch]', if possible, from existing .[ch]
-  flex         create \`lex.yy.c', if possible, from existing .c
-  help2man     touch the output file
-  lex          create \`lex.yy.c', if possible, from existing .c
-  makeinfo     touch the output file
-  yacc         create \`y.tab.[ch]', if possible, from existing .[ch]
+  aclocal   autoconf  autoheader   autom4te  automake  makeinfo
+  bison     yacc      flex         lex       help2man
 
-Version suffixes to PROGRAM as well as the prefixes \`gnu-', \`gnu', and
-\`g' are ignored when checking the name.
+Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and
+'g' are ignored when checking the name.
 
 Send bug reports to <bug-automake at gnu.org>."
     exit $?
@@ -99,228 +70,141 @@ Send bug reports to <bug-automake at gnu.org>."
     ;;
 
   -*)
-    echo 1>&2 "$0: Unknown \`$1' option"
-    echo 1>&2 "Try \`$0 --help' for more information"
+    echo 1>&2 "$0: unknown '$1' option"
+    echo 1>&2 "Try '$0 --help' for more information"
     exit 1
     ;;
 
 esac
 
-# normalize program name to check for.
-program=`echo "$1" | sed '
-  s/^gnu-//; t
-  s/^gnu//; t
-  s/^g//; t'`
-
-# Now exit if we have it, but it failed.  Also exit now if we
-# don't have it and --version was passed (most likely to detect
-# the program).  This is about non-GNU programs, so use $1 not
-# $program.
-case $1 in
-  lex*|yacc*)
-    # Not GNU programs, they don't have --version.
-    ;;
-
-  *)
-    if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
-       # We have it, but it failed.
-       exit 1
-    elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
-       # Could not run --version or --help.  This is probably someone
-       # running `$TOOL --version' or `$TOOL --help' to check whether
-       # $TOOL exists and not knowing $TOOL uses missing.
-       exit 1
-    fi
-    ;;
-esac
-
-# If it does not exist, or fails to run (possibly an outdated version),
-# try to emulate it.
-case $program in
-  aclocal*)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-         you modified \`acinclude.m4' or \`${configure_ac}'.  You might want
-         to install the \`Automake' and \`Perl' packages.  Grab them from
-         any GNU archive site."
-    touch aclocal.m4
-    ;;
-
-  autoconf*)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-         you modified \`${configure_ac}'.  You might want to install the
-         \`Autoconf' and \`GNU m4' packages.  Grab them from any GNU
-         archive site."
-    touch configure
-    ;;
-
-  autoheader*)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-         you modified \`acconfig.h' or \`${configure_ac}'.  You might want
-         to install the \`Autoconf' and \`GNU m4' packages.  Grab them
-         from any GNU archive site."
-    files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
-    test -z "$files" && files="config.h"
-    touch_files=
-    for f in $files; do
-      case $f in
-      *:*) touch_files="$touch_files "`echo "$f" |
-				       sed -e 's/^[^:]*://' -e 's/:.*//'`;;
-      *) touch_files="$touch_files $f.in";;
-      esac
-    done
-    touch $touch_files
-    ;;
-
-  automake*)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-         you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
-         You might want to install the \`Automake' and \`Perl' packages.
-         Grab them from any GNU archive site."
-    find . -type f -name Makefile.am -print |
-	   sed 's/\.am$/.in/' |
-	   while read f; do touch "$f"; done
-    ;;
-
-  autom4te*)
-    echo 1>&2 "\
-WARNING: \`$1' is needed, but is $msg.
-         You might have modified some files without having the
-         proper tools for further handling them.
-         You can get \`$1' as part of \`Autoconf' from any GNU
-         archive site."
-
-    file=`echo "$*" | sed -n "$sed_output"`
-    test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
-    if test -f "$file"; then
-	touch $file
-    else
-	test -z "$file" || exec >$file
-	echo "#! /bin/sh"
-	echo "# Created by GNU Automake missing as a replacement of"
-	echo "#  $ $@"
-	echo "exit 0"
-	chmod +x $file
-	exit 1
-    fi
-    ;;
-
-  bison*|yacc*)
-    echo 1>&2 "\
-WARNING: \`$1' $msg.  You should only need it if
-         you modified a \`.y' file.  You may need the \`Bison' package
-         in order for those modifications to take effect.  You can get
-         \`Bison' from any GNU archive site."
-    rm -f y.tab.c y.tab.h
-    if test $# -ne 1; then
-        eval LASTARG=\${$#}
-	case $LASTARG in
-	*.y)
-	    SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
-	    if test -f "$SRCFILE"; then
-	         cp "$SRCFILE" y.tab.c
-	    fi
-	    SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
-	    if test -f "$SRCFILE"; then
-	         cp "$SRCFILE" y.tab.h
-	    fi
-	  ;;
-	esac
-    fi
-    if test ! -f y.tab.h; then
-	echo >y.tab.h
-    fi
-    if test ! -f y.tab.c; then
-	echo 'main() { return 0; }' >y.tab.c
-    fi
-    ;;
-
-  lex*|flex*)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-         you modified a \`.l' file.  You may need the \`Flex' package
-         in order for those modifications to take effect.  You can get
-         \`Flex' from any GNU archive site."
-    rm -f lex.yy.c
-    if test $# -ne 1; then
-        eval LASTARG=\${$#}
-	case $LASTARG in
-	*.l)
-	    SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
-	    if test -f "$SRCFILE"; then
-	         cp "$SRCFILE" lex.yy.c
-	    fi
-	  ;;
-	esac
-    fi
-    if test ! -f lex.yy.c; then
-	echo 'main() { return 0; }' >lex.yy.c
-    fi
-    ;;
-
-  help2man*)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-	 you modified a dependency of a manual page.  You may need the
-	 \`Help2man' package in order for those modifications to take
-	 effect.  You can get \`Help2man' from any GNU archive site."
-
-    file=`echo "$*" | sed -n "$sed_output"`
-    test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
-    if test -f "$file"; then
-	touch $file
-    else
-	test -z "$file" || exec >$file
-	echo ".ab help2man is required to generate this page"
-	exit $?
-    fi
-    ;;
-
-  makeinfo*)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-         you modified a \`.texi' or \`.texinfo' file, or any other file
-         indirectly affecting the aspect of the manual.  The spurious
-         call might also be the consequence of using a buggy \`make' (AIX,
-         DU, IRIX).  You might want to install the \`Texinfo' package or
-         the \`GNU make' package.  Grab either from any GNU archive site."
-    # The file to touch is that specified with -o ...
-    file=`echo "$*" | sed -n "$sed_output"`
-    test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
-    if test -z "$file"; then
-      # ... or it is the one specified with @setfilename ...
-      infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
-      file=`sed -n '
-	/^@setfilename/{
-	  s/.* \([^ ]*\) *$/\1/
-	  p
-	  q
-	}' $infile`
-      # ... or it is derived from the source name (dir/f.texi becomes f.info)
-      test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info
-    fi
-    # If the file does not exist, the user really needs makeinfo;
-    # let's fail without touching anything.
-    test -f $file || exit 1
-    touch $file
-    ;;
+# Run the given program, remember its exit status.
+"$@"; st=$?
+
+# If it succeeded, we are done.
+test $st -eq 0 && exit 0
+
+# Also exit now if we it failed (or wasn't found), and '--version' was
+# passed; such an option is passed most likely to detect whether the
+# program is present and works.
+case $2 in --version|--help) exit $st;; esac
+
+# Exit code 63 means version mismatch.  This often happens when the user
+# tries to use an ancient version of a tool on a file that requires a
+# minimum version.
+if test $st -eq 63; then
+  msg="probably too old"
+elif test $st -eq 127; then
+  # Program was missing.
+  msg="missing on your system"
+else
+  # Program was found and executed, but failed.  Give up.
+  exit $st
+fi
 
-  *)
-    echo 1>&2 "\
-WARNING: \`$1' is needed, and is $msg.
-         You might have modified some files without having the
-         proper tools for further handling them.  Check the \`README' file,
-         it often tells you about the needed prerequisites for installing
-         this package.  You may also peek at any GNU archive site, in case
-         some other package would contain this missing \`$1' program."
-    exit 1
+perl_URL=http://www.perl.org/
+flex_URL=http://flex.sourceforge.net/
+gnu_software_URL=http://www.gnu.org/software
+
+program_details ()
+{
+  case $1 in
+    aclocal|automake)
+      echo "The '$1' program is part of the GNU Automake package:"
+      echo "<$gnu_software_URL/automake>"
+      echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:"
+      echo "<$gnu_software_URL/autoconf>"
+      echo "<$gnu_software_URL/m4/>"
+      echo "<$perl_URL>"
+      ;;
+    autoconf|autom4te|autoheader)
+      echo "The '$1' program is part of the GNU Autoconf package:"
+      echo "<$gnu_software_URL/autoconf/>"
+      echo "It also requires GNU m4 and Perl in order to run:"
+      echo "<$gnu_software_URL/m4/>"
+      echo "<$perl_URL>"
+      ;;
+  esac
+}
+
+give_advice ()
+{
+  # Normalize program name to check for.
+  normalized_program=`echo "$1" | sed '
+    s/^gnu-//; t
+    s/^gnu//; t
+    s/^g//; t'`
+
+  printf '%s\n' "'$1' is $msg."
+
+  configure_deps="'configure.ac' or m4 files included by 'configure.ac'"
+  case $normalized_program in
+    autoconf*)
+      echo "You should only need it if you modified 'configure.ac',"
+      echo "or m4 files included by it."
+      program_details 'autoconf'
+      ;;
+    autoheader*)
+      echo "You should only need it if you modified 'acconfig.h' or"
+      echo "$configure_deps."
+      program_details 'autoheader'
+      ;;
+    automake*)
+      echo "You should only need it if you modified 'Makefile.am' or"
+      echo "$configure_deps."
+      program_details 'automake'
+      ;;
+    aclocal*)
+      echo "You should only need it if you modified 'acinclude.m4' or"
+      echo "$configure_deps."
+      program_details 'aclocal'
+      ;;
+   autom4te*)
+      echo "You might have modified some maintainer files that require"
+      echo "the 'autom4te' program to be rebuilt."
+      program_details 'autom4te'
+      ;;
+    bison*|yacc*)
+      echo "You should only need it if you modified a '.y' file."
+      echo "You may want to install the GNU Bison package:"
+      echo "<$gnu_software_URL/bison/>"
+      ;;
+    lex*|flex*)
+      echo "You should only need it if you modified a '.l' file."
+      echo "You may want to install the Fast Lexical Analyzer package:"
+      echo "<$flex_URL>"
+      ;;
+    help2man*)
+      echo "You should only need it if you modified a dependency" \
+           "of a man page."
+      echo "You may want to install the GNU Help2man package:"
+      echo "<$gnu_software_URL/help2man/>"
     ;;
-esac
-
-exit 0
+    makeinfo*)
+      echo "You should only need it if you modified a '.texi' file, or"
+      echo "any other file indirectly affecting the aspect of the manual."
+      echo "You might want to install the Texinfo package:"
+      echo "<$gnu_software_URL/texinfo/>"
+      echo "The spurious makeinfo call might also be the consequence of"
+      echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might"
+      echo "want to install GNU make:"
+      echo "<$gnu_software_URL/make/>"
+      ;;
+    *)
+      echo "You might have modified some files without having the proper"
+      echo "tools for further handling them.  Check the 'README' file, it"
+      echo "often tells you about the needed prerequisites for installing"
+      echo "this package.  You may also peek at any GNU archive site, in"
+      echo "case some other package contains this missing '$1' program."
+      ;;
+  esac
+}
+
+give_advice "$1" | sed -e '1s/^/WARNING: /' \
+                       -e '2,$s/^/         /' >&2
+
+# Propagate the correct exit status (expected to be 127 for a program
+# not found, 63 for a program that failed due to version mismatch).
+exit $st
 
 # Local variables:
 # eval: (add-hook 'write-file-hooks 'time-stamp)
diff --git a/src/common-ssh/Makefile.am b/src/common-ssh/Makefile.am
new file mode 100644
index 0000000..9d6b671
--- /dev/null
+++ b/src/common-ssh/Makefile.am
@@ -0,0 +1,54 @@
+#
+# Copyright (C) 2015 Glyptodon LLC
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+AUTOMAKE_OPTIONS = foreign
+ACLOCAL_AMFLAGS = -I m4
+
+noinst_LTLIBRARIES = libguac_common_ssh.la
+
+libguac_common_ssh_la_SOURCES = \
+    guac_sftp.c                 \
+    guac_ssh.c                  \
+    guac_ssh_buffer.c           \
+    guac_ssh_key.c              \
+    guac_ssh_user.c
+
+noinst_HEADERS =      \
+    guac_sftp.h       \
+    guac_ssh.h        \
+    guac_ssh_buffer.h \
+    guac_ssh_key.h    \
+    guac_ssh_user.h
+
+libguac_common_ssh_la_CFLAGS = \
+    -Werror -Wall -pedantic    \
+    @COMMON_INCLUDE@           \
+    @LIBGUAC_INCLUDE@
+
+libguac_common_ssh_la_LIBADD = \
+    @LIBGUAC_LTLIB@
+
+libguac_common_ssh_la_LDFLAGS = \
+    @PTHREAD_LIBS@              \
+    @SSH_LIBS@                  \
+    @SSL_LIBS@
+
diff --git a/src/common-ssh/Makefile.in b/src/common-ssh/Makefile.in
new file mode 100644
index 0000000..de9ebd7
--- /dev/null
+++ b/src/common-ssh/Makefile.in
@@ -0,0 +1,704 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+ at SET_MAKE@
+
+#
+# Copyright (C) 2015 Glyptodon LLC
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/common-ssh
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(top_srcdir)/depcomp $(noinst_HEADERS)
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
+	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+	$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libguac_common_ssh_la_DEPENDENCIES =
+am_libguac_common_ssh_la_OBJECTS = libguac_common_ssh_la-guac_sftp.lo \
+	libguac_common_ssh_la-guac_ssh.lo \
+	libguac_common_ssh_la-guac_ssh_buffer.lo \
+	libguac_common_ssh_la-guac_ssh_key.lo \
+	libguac_common_ssh_la-guac_ssh_user.lo
+libguac_common_ssh_la_OBJECTS = $(am_libguac_common_ssh_la_OBJECTS)
+AM_V_lt = $(am__v_lt_ at AM_V@)
+am__v_lt_ = $(am__v_lt_ at AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+libguac_common_ssh_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(libguac_common_ssh_la_CFLAGS) $(CFLAGS) \
+	$(libguac_common_ssh_la_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_ at AM_V@)
+am__v_P_ = $(am__v_P_ at AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_ at AM_V@)
+am__v_GEN_ = $(am__v_GEN_ at AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_ at AM_V@)
+am__v_at_ = $(am__v_at_ at AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I. at am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_ at AM_V@)
+am__v_CC_ = $(am__v_CC_ at AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_ at AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_ at AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
+SOURCES = $(libguac_common_ssh_la_SOURCES)
+DIST_SOURCES = $(libguac_common_ssh_la_SOURCES)
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CAIRO_LIBS = @CAIRO_LIBS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+COMMON_INCLUDE = @COMMON_INCLUDE@
+COMMON_LTLIB = @COMMON_LTLIB@
+COMMON_SSH_INCLUDE = @COMMON_SSH_INCLUDE@
+COMMON_SSH_LTLIB = @COMMON_SSH_LTLIB@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CUNIT_LIBS = @CUNIT_LIBS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+JPEG_LIBS = @JPEG_LIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBADD_DLOPEN = @LIBADD_DLOPEN@
+LIBGUAC_INCLUDE = @LIBGUAC_INCLUDE@
+LIBGUAC_LTLIB = @LIBGUAC_LTLIB@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MATH_LIBS = @MATH_LIBS@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGO_CFLAGS = @PANGO_CFLAGS@
+PANGO_LIBS = @PANGO_LIBS@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_LIBS = @PNG_LIBS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+PULSE_LIBS = @PULSE_LIBS@
+RANLIB = @RANLIB@
+RDP_LIBS = @RDP_LIBS@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SSH_LIBS = @SSH_LIBS@
+SSL_LIBS = @SSL_LIBS@
+STRIP = @STRIP@
+TELNET_LIBS = @TELNET_LIBS@
+TERMINAL_INCLUDE = @TERMINAL_INCLUDE@
+TERMINAL_LTLIB = @TERMINAL_LTLIB@
+UUID_LIBS = @UUID_LIBS@
+VERSION = @VERSION@
+VNC_LIBS = @VNC_LIBS@
+VORBIS_LIBS = @VORBIS_LIBS@
+WEBP_LIBS = @WEBP_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+init_dir = @init_dir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AUTOMAKE_OPTIONS = foreign
+ACLOCAL_AMFLAGS = -I m4
+noinst_LTLIBRARIES = libguac_common_ssh.la
+libguac_common_ssh_la_SOURCES = \
+    guac_sftp.c                 \
+    guac_ssh.c                  \
+    guac_ssh_buffer.c           \
+    guac_ssh_key.c              \
+    guac_ssh_user.c
+
+noinst_HEADERS = \
+    guac_sftp.h       \
+    guac_ssh.h        \
+    guac_ssh_buffer.h \
+    guac_ssh_key.h    \
+    guac_ssh_user.h
+
+libguac_common_ssh_la_CFLAGS = \
+    -Werror -Wall -pedantic    \
+    @COMMON_INCLUDE@           \
+    @LIBGUAC_INCLUDE@
+
+libguac_common_ssh_la_LIBADD = \
+    @LIBGUAC_LTLIB@
+
+libguac_common_ssh_la_LDFLAGS = \
+    @PTHREAD_LIBS@              \
+    @SSH_LIBS@                  \
+    @SSL_LIBS@
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/common-ssh/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --foreign src/common-ssh/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure:  $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+	-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+	@list='$(noinst_LTLIBRARIES)'; \
+	locs=`for p in $$list; do echo $$p; done | \
+	      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+	      sort -u`; \
+	test -z "$$locs" || { \
+	  echo rm -f $${locs}; \
+	  rm -f $${locs}; \
+	}
+
+libguac_common_ssh.la: $(libguac_common_ssh_la_OBJECTS) $(libguac_common_ssh_la_DEPENDENCIES) $(EXTRA_libguac_common_ssh_la_DEPENDENCIES) 
+	$(AM_V_CCLD)$(libguac_common_ssh_la_LINK)  $(libguac_common_ssh_la_OBJECTS) $(libguac_common_ssh_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_common_ssh_la-guac_sftp.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_common_ssh_la-guac_ssh.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_common_ssh_la-guac_ssh_buffer.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_common_ssh_la-guac_ssh_key.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_common_ssh_la-guac_ssh_user.Plo at am__quote@
+
+.c.o:
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+ at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+ at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+ at am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+libguac_common_ssh_la-guac_sftp.lo: guac_sftp.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_ssh_la_CFLAGS) $(CFLAGS) -MT libguac_common_ssh_la-guac_sftp.lo -MD -MP -MF $(DEPDIR)/libguac_common_ssh_la-guac_sftp.Tpo -c -o libguac_common_ssh_la-guac_sftp.lo `test -f 'guac_sftp.c' || echo '$(srcdir)/'`guac_sftp.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_common_ssh_la-guac_sftp.Tpo $(DEPDIR)/libguac_common_ssh_la-guac_sftp.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_sftp.c' object='libguac_common_ssh_la-guac_sftp.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_ssh_la_CFLAGS) $(CFLAGS) -c -o libguac_common_ssh_la-guac_sftp.lo `test -f 'guac_sftp.c' || echo '$(srcdir)/'`guac_sftp.c
+
+libguac_common_ssh_la-guac_ssh.lo: guac_ssh.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_ssh_la_CFLAGS) $(CFLAGS) -MT libguac_common_ssh_la-guac_ssh.lo -MD -MP -MF $(DEPDIR)/libguac_common_ssh_la-guac_ssh.Tpo -c -o libguac_common_ssh_la-guac_ssh.lo `test -f 'guac_ssh.c' || echo '$(srcdir)/'`guac_ssh.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_common_ssh_la-guac_ssh.Tpo $(DEPDIR)/libguac_common_ssh_la-guac_ssh.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_ssh.c' object='libguac_common_ssh_la-guac_ssh.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_ssh_la_CFLAGS) $(CFLAGS) -c -o libguac_common_ssh_la-guac_ssh.lo `test -f 'guac_ssh.c' || echo '$(srcdir)/'`guac_ssh.c
+
+libguac_common_ssh_la-guac_ssh_buffer.lo: guac_ssh_buffer.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_ssh_la_CFLAGS) $(CFLAGS) -MT libguac_common_ssh_la-guac_ssh_buffer.lo -MD -MP -MF $(DEPDIR)/libguac_common_ssh_la-guac_ssh_buffer.Tpo -c -o libguac_common_ssh_la-guac_ssh_buffer.lo `test -f 'guac_ssh_buffer.c' || echo '$(srcdir)/'`guac_ssh_buffer.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_common_ssh_la-guac_ssh_buffer.Tpo $(DEPDIR)/libguac_common_ssh_la-guac_ssh_buffer.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_ssh_buffer.c' object='libguac_common_ssh_la-guac_ssh_buffer.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_ssh_la_CFLAGS) $(CFLAGS) -c -o libguac_common_ssh_la-guac_ssh_buffer.lo `test -f 'guac_ssh_buffer.c' || echo '$(srcdir)/'`guac_ssh_buffer.c
+
+libguac_common_ssh_la-guac_ssh_key.lo: guac_ssh_key.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_ssh_la_CFLAGS) $(CFLAGS) -MT libguac_common_ssh_la-guac_ssh_key.lo -MD -MP -MF $(DEPDIR)/libguac_common_ssh_la-guac_ssh_key.Tpo -c -o libguac_common_ssh_la-guac_ssh_key.lo `test -f 'guac_ssh_key.c' || echo '$(srcdir)/'`guac_ssh_key.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_common_ssh_la-guac_ssh_key.Tpo $(DEPDIR)/libguac_common_ssh_la-guac_ssh_key.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_ssh_key.c' object='libguac_common_ssh_la-guac_ssh_key.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_ssh_la_CFLAGS) $(CFLAGS) -c -o libguac_common_ssh_la-guac_ssh_key.lo `test -f 'guac_ssh_key.c' || echo '$(srcdir)/'`guac_ssh_key.c
+
+libguac_common_ssh_la-guac_ssh_user.lo: guac_ssh_user.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_ssh_la_CFLAGS) $(CFLAGS) -MT libguac_common_ssh_la-guac_ssh_user.lo -MD -MP -MF $(DEPDIR)/libguac_common_ssh_la-guac_ssh_user.Tpo -c -o libguac_common_ssh_la-guac_ssh_user.lo `test -f 'guac_ssh_user.c' || echo '$(srcdir)/'`guac_ssh_user.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_common_ssh_la-guac_ssh_user.Tpo $(DEPDIR)/libguac_common_ssh_la-guac_ssh_user.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_ssh_user.c' object='libguac_common_ssh_la-guac_ssh_user.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_ssh_la_CFLAGS) $(CFLAGS) -c -o libguac_common_ssh_la-guac_ssh_user.lo `test -f 'guac_ssh_user.c' || echo '$(srcdir)/'`guac_ssh_user.c
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+	mostlyclean-am
+
+distclean: distclean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
+	clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \
+	ctags-am distclean distclean-compile distclean-generic \
+	distclean-libtool distclean-tags distdir dvi dvi-am html \
+	html-am info info-am install install-am install-data \
+	install-data-am install-dvi install-dvi-am install-exec \
+	install-exec-am install-html install-html-am install-info \
+	install-info-am install-man install-pdf install-pdf-am \
+	install-ps install-ps-am install-strip installcheck \
+	installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+	tags tags-am uninstall uninstall-am
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/common-ssh/guac_sftp.c b/src/common-ssh/guac_sftp.c
new file mode 100644
index 0000000..1ccff56
--- /dev/null
+++ b/src/common-ssh/guac_sftp.c
@@ -0,0 +1,783 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "guac_sftp.h"
+#include "guac_ssh.h"
+
+#include <guacamole/client.h>
+#include <guacamole/object.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+#include <libssh2.h>
+
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * Translates the last error message received by the SFTP layer of an SSH
+ * session into a Guacamole protocol status code.
+ *
+ * @param filesystem
+ *     The Guacamole protocol object defining the filesystem associated with
+ *     the SFTP and SSH sessions.
+ *
+ * @return
+ *     The Guacamole protocol status code corresponding to the last reported
+ *     error of the SFTP layer, if nay, or GUAC_PROTOCOL_STATUS_SUCCESS if no
+ *     error has occurred.
+ */
+static guac_protocol_status guac_sftp_get_status(guac_object* filesystem) {
+
+    guac_common_ssh_sftp_data* sftp_data =
+        (guac_common_ssh_sftp_data*) filesystem->data;
+
+    /* Get libssh2 objects */
+    LIBSSH2_SFTP*    sftp    = sftp_data->sftp_session;
+    LIBSSH2_SESSION* session = sftp_data->ssh_session->session;
+
+    /* Return success code if no error occurred */
+    if (libssh2_session_last_errno(session) != LIBSSH2_ERROR_SFTP_PROTOCOL)
+        return GUAC_PROTOCOL_STATUS_SUCCESS;
+
+    /* Translate SFTP error codes defined by
+     * https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02 (the most
+     * commonly-implemented standard) */
+    switch (libssh2_sftp_last_error(sftp)) {
+
+        /* SSH_FX_OK (not an error) */
+        case 0:
+            return GUAC_PROTOCOL_STATUS_SUCCESS;
+
+        /* SSH_FX_EOF (technically not an error) */
+        case 1:
+            return GUAC_PROTOCOL_STATUS_SUCCESS;
+
+        /* SSH_FX_NO_SUCH_FILE */
+        case 2:
+            return GUAC_PROTOCOL_STATUS_RESOURCE_NOT_FOUND;
+
+        /* SSH_FX_PERMISSION_DENIED */
+        case 3:
+            return GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN;
+
+        /* SSH_FX_FAILURE */
+        case 4:
+            return GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR;
+
+        /* SSH_FX_BAD_MESSAGE */
+        case 5:
+            return GUAC_PROTOCOL_STATUS_SERVER_ERROR;
+
+        /* SSH_FX_NO_CONNECTION / SSH_FX_CONNECTION_LOST */
+        case 6:
+        case 7:
+            return GUAC_PROTOCOL_STATUS_UPSTREAM_TIMEOUT;
+
+        /* SSH_FX_OP_UNSUPPORTED */
+        case 8:
+            return GUAC_PROTOCOL_STATUS_UNSUPPORTED;
+
+        /* Return generic error if cause unknown */
+        default:
+            return GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR;
+
+    }
+
+}
+
+/**
+ * Concatenates the given filename with the given path, separating the two
+ * with a single forward slash. The full result must be no more than
+ * GUAC_COMMON_SSH_SFTP_MAX_PATH bytes long, counting null terminator.
+ *
+ * @param fullpath
+ *     The buffer to store the result within. This buffer must be at least
+ *     GUAC_COMMON_SSH_SFTP_MAX_PATH bytes long.
+ *
+ * @param path
+ *     The path to append the filename to.
+ *
+ * @param filename
+ *     The filename to append to the path.
+ *
+ * @return
+ *     Non-zero if the filename is valid and was successfully appended to the
+ *     path, zero otherwise.
+ */
+static int guac_ssh_append_filename(char* fullpath, const char* path,
+        const char* filename) {
+
+    int i;
+
+    /* Disallow "." as a filename */
+    if (strcmp(filename, ".") == 0)
+        return 0;
+
+    /* Disallow ".." as a filename */
+    if (strcmp(filename, "..") == 0)
+        return 0;
+
+    /* Copy path, append trailing slash */
+    for (i=0; i<GUAC_COMMON_SSH_SFTP_MAX_PATH; i++) {
+
+        /*
+         * Append trailing slash only if:
+         *  1) Trailing slash is not already present
+         *  2) Path is non-empty
+         */
+
+        char c = path[i];
+        if (c == '\0') {
+            if (i > 0 && path[i-1] != '/')
+                fullpath[i++] = '/';
+            break;
+        }
+
+        /* Copy character if not end of string */
+        fullpath[i] = c;
+
+    }
+
+    /* Append filename */
+    for (; i<GUAC_COMMON_SSH_SFTP_MAX_PATH; i++) {
+
+        char c = *(filename++);
+        if (c == '\0')
+            break;
+
+        /* Filenames may not contain slashes */
+        if (c == '\\' || c == '/')
+            return 0;
+
+        /* Append each character within filename */
+        fullpath[i] = c;
+
+    }
+
+    /* Verify path length is within maximum */
+    if (i == GUAC_COMMON_SSH_SFTP_MAX_PATH)
+        return 0;
+
+    /* Terminate path string */
+    fullpath[i] = '\0';
+
+    /* Append was successful */
+    return 1;
+
+}
+
+/**
+ * Handler for blob messages which continue an inbound SFTP data transfer
+ * (upload). The data associated with the given stream is expected to be a
+ * pointer to an open LIBSSH2_SFTP_HANDLE for the file to which the data
+ * should be written.
+ *
+ * @param client
+ *     The client receiving the blob message.
+ *
+ * @param stream
+ *     The Guacamole protocol stream associated with the received blob message.
+ *
+ * @param data
+ *     The data received within the blob.
+ *
+ * @param length
+ *     The length of the received data, in bytes.
+ *
+ * @return
+ *     Zero if the blob is handled successfully, or non-zero on error.
+ */
+static int guac_common_ssh_sftp_blob_handler(guac_client* client,
+        guac_stream* stream, void* data, int length) {
+
+    /* Pull file from stream */
+    LIBSSH2_SFTP_HANDLE* file = (LIBSSH2_SFTP_HANDLE*) stream->data;
+
+    /* Attempt write */
+    if (libssh2_sftp_write(file, data, length) == length) {
+        guac_client_log(client, GUAC_LOG_DEBUG, "%i bytes written", length);
+        guac_protocol_send_ack(client->socket, stream, "SFTP: OK",
+                GUAC_PROTOCOL_STATUS_SUCCESS);
+        guac_socket_flush(client->socket);
+    }
+
+    /* Inform of any errors */
+    else {
+        guac_client_log(client, GUAC_LOG_INFO, "Unable to write to file");
+        guac_protocol_send_ack(client->socket, stream, "SFTP: Write failed",
+                GUAC_PROTOCOL_STATUS_SERVER_ERROR);
+        guac_socket_flush(client->socket);
+    }
+
+    return 0;
+
+}
+
+/**
+ * Handler for end messages which terminate an inbound SFTP data transfer
+ * (upload). The data associated with the given stream is expected to be a
+ * pointer to an open LIBSSH2_SFTP_HANDLE for the file to which the data
+ * has been written and which should now be closed.
+ *
+ * @param client
+ *     The client receiving the end message.
+ *
+ * @param stream
+ *     The Guacamole protocol stream associated with the received end message.
+ *
+ * @return
+ *     Zero if the file is closed successfully, or non-zero on error.
+ */
+static int guac_common_ssh_sftp_end_handler(guac_client* client,
+        guac_stream* stream) {
+
+    /* Pull file from stream */
+    LIBSSH2_SFTP_HANDLE* file = (LIBSSH2_SFTP_HANDLE*) stream->data;
+
+    /* Attempt to close file */
+    if (libssh2_sftp_close(file) == 0) {
+        guac_client_log(client, GUAC_LOG_DEBUG, "File closed");
+        guac_protocol_send_ack(client->socket, stream, "SFTP: OK",
+                GUAC_PROTOCOL_STATUS_SUCCESS);
+        guac_socket_flush(client->socket);
+    }
+    else {
+        guac_client_log(client, GUAC_LOG_INFO, "Unable to close file");
+        guac_protocol_send_ack(client->socket, stream, "SFTP: Close failed",
+                GUAC_PROTOCOL_STATUS_SERVER_ERROR);
+        guac_socket_flush(client->socket);
+    }
+
+    return 0;
+
+}
+
+int guac_common_ssh_sftp_handle_file_stream(guac_object* filesystem,
+        guac_stream* stream, char* mimetype, char* filename) {
+
+    guac_common_ssh_sftp_data* sftp_data =
+        (guac_common_ssh_sftp_data*) filesystem->data;
+
+    guac_client* client = sftp_data->ssh_session->client;
+
+    char fullpath[GUAC_COMMON_SSH_SFTP_MAX_PATH];
+    LIBSSH2_SFTP_HANDLE* file;
+
+    /* Concatenate filename with path */
+    if (!guac_ssh_append_filename(fullpath, sftp_data->upload_path,
+                filename)) {
+
+        guac_client_log(client, GUAC_LOG_DEBUG,
+                "Filename \"%s\" is invalid or resulting path is too long",
+                filename);
+
+        /* Abort transfer - invalid filename */
+        guac_protocol_send_ack(client->socket, stream, 
+                "SFTP: Illegal filename",
+                GUAC_PROTOCOL_STATUS_CLIENT_BAD_REQUEST);
+
+        guac_socket_flush(client->socket);
+        return 0;
+    }
+
+    /* Open file via SFTP */
+    file = libssh2_sftp_open(sftp_data->sftp_session, fullpath,
+            LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_TRUNC,
+            S_IRUSR | S_IWUSR);
+
+    /* Inform of status */
+    if (file != NULL) {
+
+        guac_client_log(client, GUAC_LOG_DEBUG,
+                "File \"%s\" opened",
+                fullpath);
+
+        guac_protocol_send_ack(client->socket, stream, "SFTP: File opened",
+                GUAC_PROTOCOL_STATUS_SUCCESS);
+        guac_socket_flush(client->socket);
+    }
+    else {
+        guac_client_log(client, GUAC_LOG_INFO,
+                "Unable to open file \"%s\"", fullpath);
+        guac_protocol_send_ack(client->socket, stream, "SFTP: Open failed",
+                guac_sftp_get_status(filesystem));
+        guac_socket_flush(client->socket);
+    }
+
+    /* Set handlers for file stream */
+    stream->blob_handler = guac_common_ssh_sftp_blob_handler;
+    stream->end_handler = guac_common_ssh_sftp_end_handler;
+
+    /* Store file within stream */
+    stream->data = file;
+    return 0;
+
+}
+
+/**
+ * Handler for ack messages which continue an outbound SFTP data transfer
+ * (download), signalling the current status and requesting additional data.
+ * The data associated with the given stream is expected to be a pointer to an
+ * open LIBSSH2_SFTP_HANDLE for the file from which the data is to be read.
+ *
+ * @param client
+ *     The client receiving the ack message.
+ *
+ * @param stream
+ *     The Guacamole protocol stream associated with the received ack message.
+ *
+ * @param message
+ *     An arbitrary human-readable message describing the nature of the
+ *     success or failure denoted by the ack message.
+ *
+ * @param status
+ *     The status code associated with the ack message, which may indicate
+ *     success or an error.
+ *
+ * @return
+ *     Zero if the file is read from successfully, or non-zero on error.
+ */
+static int guac_common_ssh_sftp_ack_handler(guac_client* client,
+        guac_stream* stream, char* message, guac_protocol_status status) {
+
+    /* Pull file from stream */
+    LIBSSH2_SFTP_HANDLE* file = (LIBSSH2_SFTP_HANDLE*) stream->data;
+
+    /* If successful, read data */
+    if (status == GUAC_PROTOCOL_STATUS_SUCCESS) {
+
+        /* Attempt read into buffer */
+        char buffer[4096];
+        int bytes_read = libssh2_sftp_read(file, buffer, sizeof(buffer)); 
+
+        /* If bytes read, send as blob */
+        if (bytes_read > 0) {
+            guac_protocol_send_blob(client->socket, stream,
+                    buffer, bytes_read);
+
+            guac_client_log(client, GUAC_LOG_DEBUG, "%i bytes sent to client",
+                    bytes_read);
+
+        }
+
+        /* If EOF, send end */
+        else if (bytes_read == 0) {
+            guac_client_log(client, GUAC_LOG_DEBUG, "File sent");
+            guac_protocol_send_end(client->socket, stream);
+            guac_client_free_stream(client, stream);
+        }
+
+        /* Otherwise, fail stream */
+        else {
+            guac_client_log(client, GUAC_LOG_INFO, "Error reading file");
+            guac_protocol_send_end(client->socket, stream);
+            guac_client_free_stream(client, stream);
+        }
+
+        guac_socket_flush(client->socket);
+
+    }
+
+    /* Otherwise, return stream to client */
+    else
+        guac_client_free_stream(client, stream);
+
+    return 0;
+}
+
+guac_stream* guac_common_ssh_sftp_download_file(guac_object* filesystem,
+        char* filename) {
+
+    guac_common_ssh_sftp_data* sftp_data =
+        (guac_common_ssh_sftp_data*) filesystem->data;
+
+    guac_client* client = sftp_data->ssh_session->client;
+
+    guac_stream* stream;
+    LIBSSH2_SFTP_HANDLE* file;
+
+    /* Attempt to open file for reading */
+    file = libssh2_sftp_open(sftp_data->sftp_session, filename,
+            LIBSSH2_FXF_READ, 0);
+    if (file == NULL) {
+        guac_client_log(client, GUAC_LOG_INFO, 
+                "Unable to read file \"%s\"", filename);
+        return NULL;
+    }
+
+    /* Allocate stream */
+    stream = guac_client_alloc_stream(client);
+    stream->ack_handler = guac_common_ssh_sftp_ack_handler;
+    stream->data = file;
+
+    /* Send stream start, strip name */
+    filename = basename(filename);
+    guac_protocol_send_file(client->socket, stream,
+            "application/octet-stream", filename);
+    guac_socket_flush(client->socket);
+
+    guac_client_log(client, GUAC_LOG_DEBUG, "Sending file \"%s\"", filename);
+    return stream;
+
+}
+
+void guac_common_ssh_sftp_set_upload_path(guac_object* filesystem,
+        const char* path) {
+
+    guac_common_ssh_sftp_data* sftp_data =
+        (guac_common_ssh_sftp_data*) filesystem->data;
+
+    guac_client* client = sftp_data->ssh_session->client;
+
+    /* Ignore requests which exceed maximum-allowed path */
+    int length = strnlen(path, GUAC_COMMON_SSH_SFTP_MAX_PATH)+1;
+    if (length > GUAC_COMMON_SSH_SFTP_MAX_PATH) {
+        guac_client_log(client, GUAC_LOG_ERROR,
+                "Submitted path exceeds limit of %i bytes",
+                GUAC_COMMON_SSH_SFTP_MAX_PATH);
+        return;
+    }
+
+    /* Copy path */
+    memcpy(sftp_data->upload_path, path, length);
+    guac_client_log(client, GUAC_LOG_DEBUG, "Upload path set to \"%s\"", path);
+
+}
+
+/**
+ * Handler for ack messages received due to receipt of a "body" or "blob"
+ * instruction associated with a SFTP directory list operation.
+ *
+ * @param client
+ *     The client receiving the ack message.
+ *
+ * @param stream
+ *     The Guacamole protocol stream associated with the received ack message.
+ *
+ * @param message
+ *     An arbitrary human-readable message describing the nature of the
+ *     success or failure denoted by this ack message.
+ *
+ * @param status
+ *     The status code associated with this ack message, which may indicate
+ *     success or an error.
+ *
+ * @return
+ *     Zero on success, non-zero on error.
+ */
+static int guac_common_ssh_sftp_ls_ack_handler(guac_client* client,
+        guac_stream* stream, char* message, guac_protocol_status status) {
+
+    int bytes_read;
+    int blob_written = 0;
+
+    char filename[GUAC_COMMON_SSH_SFTP_MAX_PATH];
+    LIBSSH2_SFTP_ATTRIBUTES attributes;
+
+    guac_common_ssh_sftp_ls_state* list_state =
+        (guac_common_ssh_sftp_ls_state*) stream->data;
+
+    guac_common_ssh_sftp_data* sftp_data = list_state->sftp_data;
+
+    LIBSSH2_SFTP* sftp = sftp_data->sftp_session;
+
+    /* If unsuccessful, free stream and abort */
+    if (status != GUAC_PROTOCOL_STATUS_SUCCESS) {
+        libssh2_sftp_closedir(list_state->directory);
+        guac_client_free_stream(client, stream);
+        free(list_state);
+        return 0;
+    }
+
+    /* While directory entries remain */
+    while ((bytes_read = libssh2_sftp_readdir(list_state->directory,
+                filename, sizeof(filename), &attributes)) > 0
+            && !blob_written) {
+
+        char absolute_path[GUAC_COMMON_SSH_SFTP_MAX_PATH];
+
+        /* Skip current and parent directory entries */
+        if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0)
+            continue;
+
+        /* Concatenate into absolute path - skip if invalid */
+        if (!guac_ssh_append_filename(absolute_path, 
+                    list_state->directory_name, filename)) {
+
+            guac_client_log(client, GUAC_LOG_DEBUG,
+                    "Skipping filename \"%s\" - filename is invalid or "
+                    "resulting path is too long", filename);
+
+            continue;
+        }
+
+        /* Stat explicitly if symbolic link (might point to directory) */
+        if (LIBSSH2_SFTP_S_ISLNK(attributes.permissions))
+            libssh2_sftp_stat(sftp, absolute_path, &attributes);
+
+        /* Determine mimetype */
+        const char* mimetype;
+        if (LIBSSH2_SFTP_S_ISDIR(attributes.permissions))
+            mimetype = GUAC_CLIENT_STREAM_INDEX_MIMETYPE;
+        else
+            mimetype = "application/octet-stream";
+
+        /* Write entry */
+        blob_written |= guac_common_json_write_property(client, stream,
+                &list_state->json_state, absolute_path, mimetype);
+
+    }
+
+    /* Complete JSON and cleanup at end of directory */
+    if (bytes_read <= 0) {
+
+        /* Complete JSON object */
+        guac_common_json_end_object(client, stream, &list_state->json_state);
+        guac_common_json_flush(client, stream, &list_state->json_state);
+
+        /* Clean up resources */
+        libssh2_sftp_closedir(list_state->directory);
+        free(list_state);
+
+        /* Signal of stream */
+        guac_protocol_send_end(client->socket, stream);
+        guac_client_free_stream(client, stream);
+
+    }
+
+    guac_socket_flush(client->socket);
+    return 0;
+
+}
+
+/**
+ * Handler for get messages. In context of SFTP and the filesystem exposed via
+ * the Guacamole protocol, get messages request the body of a file within the
+ * filesystem.
+ *
+ * @param client
+ *     The client receiving the get message.
+ *
+ * @param object
+ *     The Guacamole protocol object associated with the get request itself.
+ *
+ * @param name
+ *     The name of the input stream (file) being requested.
+ *
+ * @return
+ *     Zero on success, non-zero on error.
+ */
+static int guac_common_ssh_sftp_get_handler(guac_client* client,
+        guac_object* object, char* name) {
+
+    guac_common_ssh_sftp_data* sftp_data =
+        (guac_common_ssh_sftp_data*) object->data;
+
+    LIBSSH2_SFTP* sftp = sftp_data->sftp_session;
+    LIBSSH2_SFTP_ATTRIBUTES attributes;
+
+    /* Attempt to read file information */
+    if (libssh2_sftp_stat(sftp, name, &attributes)) {
+        guac_client_log(client, GUAC_LOG_INFO, "Unable to read file \"%s\"",
+                name);
+        return 0;
+    }
+
+    /* If directory, send contents of directory */
+    if (LIBSSH2_SFTP_S_ISDIR(attributes.permissions)) {
+
+        /* Open as directory */
+        LIBSSH2_SFTP_HANDLE* dir = libssh2_sftp_opendir(sftp, name);
+        if (dir == NULL) {
+            guac_client_log(client, GUAC_LOG_INFO,
+                    "Unable to read directory \"%s\"", name);
+            return 0;
+        }
+
+        /* Init directory listing state */
+        guac_common_ssh_sftp_ls_state* list_state =
+            malloc(sizeof(guac_common_ssh_sftp_ls_state));
+
+        list_state->directory = dir;
+        list_state->sftp_data = sftp_data;
+        strncpy(list_state->directory_name, name,
+                sizeof(list_state->directory_name) - 1);
+
+        /* Allocate stream for body */
+        guac_stream* stream = guac_client_alloc_stream(client);
+        stream->ack_handler = guac_common_ssh_sftp_ls_ack_handler;
+        stream->data = list_state;
+
+        /* Init JSON object state */
+        guac_common_json_begin_object(client, stream, &list_state->json_state);
+
+        /* Associate new stream with get request */
+        guac_protocol_send_body(client->socket, object, stream,
+                GUAC_CLIENT_STREAM_INDEX_MIMETYPE, name);
+
+    }
+
+    /* Otherwise, send file contents */
+    else {
+
+        /* Open as normal file */
+        LIBSSH2_SFTP_HANDLE* file = libssh2_sftp_open(sftp, name,
+            LIBSSH2_FXF_READ, 0);
+        if (file == NULL) {
+            guac_client_log(client, GUAC_LOG_INFO,
+                    "Unable to read file \"%s\"", name);
+            return 0;
+        }
+
+        /* Allocate stream for body */
+        guac_stream* stream = guac_client_alloc_stream(client);
+        stream->ack_handler = guac_common_ssh_sftp_ack_handler;
+        stream->data = file;
+
+        /* Associate new stream with get request */
+        guac_protocol_send_body(client->socket, object, stream,
+                "application/octet-stream", name);
+
+    }
+
+    guac_socket_flush(client->socket);
+    return 0;
+}
+
+/**
+ * Handler for put messages. In context of SFTP and the filesystem exposed via
+ * the Guacamole protocol, put messages request write access to a file within
+ * the filesystem.
+ *
+ * @param client
+ *     The client receiving the put message.
+ *
+ * @param object
+ *     The Guacamole protocol object associated with the put request itself.
+ *
+ * @param stream
+ *     The Guacamole protocol stream along which the client will be sending
+ *     file data.
+ *
+ * @param mimetype
+ *     The mimetype of the data being send along the stream.
+ *
+ * @param name
+ *     The name of the input stream (file) being requested.
+ *
+ * @return
+ *     Zero on success, non-zero on error.
+ */
+static int guac_common_ssh_sftp_put_handler(guac_client* client,
+        guac_object* object, guac_stream* stream, char* mimetype, char* name) {
+
+    guac_common_ssh_sftp_data* sftp_data =
+        (guac_common_ssh_sftp_data*) object->data;
+
+    LIBSSH2_SFTP* sftp = sftp_data->sftp_session;
+
+    /* Open file via SFTP */
+    LIBSSH2_SFTP_HANDLE* file = libssh2_sftp_open(sftp, name,
+            LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_TRUNC,
+            S_IRUSR | S_IWUSR);
+
+    /* Acknowledge stream if successful */
+    if (file != NULL) {
+        guac_client_log(client, GUAC_LOG_DEBUG, "File \"%s\" opened", name);
+        guac_protocol_send_ack(client->socket, stream, "SFTP: File opened",
+                GUAC_PROTOCOL_STATUS_SUCCESS);
+    }
+
+    /* Abort on failure */
+    else {
+        guac_client_log(client, GUAC_LOG_INFO,
+                "Unable to open file \"%s\"", name);
+        guac_protocol_send_ack(client->socket, stream, "SFTP: Open failed",
+                guac_sftp_get_status(object));
+    }
+
+    /* Set handlers for file stream */
+    stream->blob_handler = guac_common_ssh_sftp_blob_handler;
+    stream->end_handler = guac_common_ssh_sftp_end_handler;
+
+    /* Store file within stream */
+    stream->data = file;
+
+    guac_socket_flush(client->socket);
+    return 0;
+}
+
+guac_object* guac_common_ssh_create_sftp_filesystem(
+        guac_common_ssh_session* session,
+        const char* name) {
+
+    guac_client* client = session->client;
+
+    /* Request SFTP */
+    LIBSSH2_SFTP* sftp_session = libssh2_sftp_init(session->session);
+    if (sftp_session == NULL) {
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
+                "Unable to start SFTP session.");
+        return NULL;
+    }
+
+    /* Allocate data for SFTP session */
+    guac_common_ssh_sftp_data* sftp_data =
+        malloc(sizeof(guac_common_ssh_sftp_data));
+
+    /* Associate SSH session with SFTP data */
+    sftp_data->ssh_session = session;
+    sftp_data->sftp_session = sftp_session;
+
+    /* Initially upload files to current directory */
+    strcpy(sftp_data->upload_path, ".");
+
+    /* Init filesystem */
+    guac_object* filesystem = guac_client_alloc_object(client);
+    filesystem->get_handler = guac_common_ssh_sftp_get_handler;
+    filesystem->put_handler = guac_common_ssh_sftp_put_handler;
+    filesystem->data = sftp_data;
+
+    /* Send filesystem to client */
+    guac_protocol_send_filesystem(client->socket, filesystem, "/");
+    guac_socket_flush(client->socket);
+
+    /* Return allocated filesystem */
+    return filesystem;
+
+}
+
+void guac_common_ssh_destroy_sftp_filesystem(guac_object* filesystem) {
+
+    guac_common_ssh_sftp_data* sftp_data =
+        (guac_common_ssh_sftp_data*) filesystem->data;
+
+    /* Shutdown SFTP session */
+    libssh2_sftp_shutdown(sftp_data->sftp_session);
+
+    /* Clean up the SFTP filesystem object */
+    guac_client_free_object(sftp_data->ssh_session->client, filesystem);
+
+}
+
diff --git a/src/common-ssh/guac_sftp.h b/src/common-ssh/guac_sftp.h
new file mode 100644
index 0000000..03c4de1
--- /dev/null
+++ b/src/common-ssh/guac_sftp.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GUAC_COMMON_SSH_SFTP_H
+#define GUAC_COMMON_SSH_SFTP_H
+
+#include "guac_json.h"
+#include "guac_ssh.h"
+
+#include <guacamole/client.h>
+#include <guacamole/object.h>
+#include <libssh2.h>
+#include <libssh2_sftp.h>
+
+/**
+ * Maximum number of bytes per path.
+ */
+#define GUAC_COMMON_SSH_SFTP_MAX_PATH 2048
+
+/**
+ * Data associated with an SFTP-driven filesystem object.
+ */
+typedef struct guac_common_ssh_sftp_data {
+
+    /**
+     * The distinct SSH session used for SFTP.
+     */
+    guac_common_ssh_session* ssh_session;
+
+    /**
+     * SFTP session, used for file transfers.
+     */
+    LIBSSH2_SFTP* sftp_session;
+
+    /**
+     * The path files will be sent to, if uploaded directly via a "file"
+     * instruction.
+     */
+    char upload_path[GUAC_COMMON_SSH_SFTP_MAX_PATH];
+
+} guac_common_ssh_sftp_data;
+
+/**
+ * The current state of a directory listing operation.
+ */
+typedef struct guac_common_ssh_sftp_ls_state {
+
+    /**
+     * Data associated with the current SFTP session.
+     */
+    guac_common_ssh_sftp_data* sftp_data;
+
+    /**
+     * Reference to the directory currently being listed over SFTP. This
+     * directory must already be open from a call to libssh2_sftp_opendir().
+     */
+    LIBSSH2_SFTP_HANDLE* directory;
+
+    /**
+     * The absolute path of the directory being listed.
+     */
+    char directory_name[GUAC_COMMON_SSH_SFTP_MAX_PATH];
+
+    /**
+     * The current state of the JSON directory object being written.
+     */
+    guac_common_json_state json_state;
+
+} guac_common_ssh_sftp_ls_state;
+
+/**
+ * Creates a new Guacamole filesystem object which provides access to files
+ * and directories via SFTP using the given SSH session. When the filesystem
+ * will no longer be used, it must be explicitly destroyed with
+ * guac_common_ssh_destroy_sftp_filesystem().
+ *
+ * @param session
+ *     The session to use to provide SFTP. This session will automatically be
+ *     destroyed when this filesystem is destroyed.
+ *
+ * @param name
+ *     The name to send as the name of the filesystem.
+ *
+ * @return
+ *     A new Guacamole filesystem object, already configured to use SFTP for
+ *     uploading and downloading files.
+ */
+guac_object* guac_common_ssh_create_sftp_filesystem(
+        guac_common_ssh_session* session,
+        const char* name);
+
+/**
+ * Destroys the given filesystem object, disconnecting from SFTP and freeing
+ * and associated resources. Any associated session or user objects must be
+ * explicitly destroyed.
+ *
+ * @param object
+ *     The filesystem object to destroy.
+ */
+void guac_common_ssh_destroy_sftp_filesystem(guac_object* filesystem);
+
+/**
+ * Initiates an SFTP file download to the user via the Guacamole "file"
+ * instruction. The download will be automatically monitored and continued
+ * after this function terminates in response to "ack" instructions received by
+ * the client.
+ *
+ * @param filesystem
+ *     The filesystem containing the file to be downloaded.
+ *
+ * @param filename
+ *     The filename of the file to download, relative to the given filesystem.
+ *
+ * @return
+ *     The file stream created for the file download, already configured to
+ *     properly handle "ack" responses, etc. from the client.
+ */
+guac_stream* guac_common_ssh_sftp_download_file(guac_object* filesystem,
+        char* filename);
+
+/**
+ * Handles an incoming stream from a Guacamole "file" instruction, saving the
+ * contents of that stream to the file having the given name within the
+ * upload directory set by guac_common_ssh_sftp_set_upload_path().
+ *
+ * @param filesystem
+ *     The filesystem that should receive the uploaded file.
+ *
+ * @param stream
+ *     The stream through which the uploaded file data will be received.
+ *
+ * @param mimetype
+ *     The mimetype of the data being received.
+ *
+ * @param filename
+ *     The filename of the file to write to. This filename will always be taken
+ *     relative to the upload path set by
+ *     guac_common_ssh_sftp_set_upload_path().
+ *
+ * @return
+ *     Zero if the incoming stream has been handled successfully, non-zero on
+ *     failure.
+ */
+int guac_common_ssh_sftp_handle_file_stream(guac_object* filesystem,
+        guac_stream* stream, char* mimetype, char* filename);
+
+/**
+ * Set the destination directory for future uploads submitted via
+ * guac_common_ssh_sftp_handle_file_stream(). This function has no bearing
+ * on the destination directories of files uploaded with "put" instructions.
+ *
+ * @param filesystem
+ *     The filesystem to set the upload path of.
+ *
+ * @param path
+ *     The path to use for future uploads submitted via the
+ *     guac_common_ssh_sftp_handle_file_stream() function.
+ */
+void guac_common_ssh_sftp_set_upload_path(guac_object* filesystem,
+        const char* path);
+
+#endif
+
diff --git a/src/common-ssh/guac_ssh.c b/src/common-ssh/guac_ssh.c
new file mode 100644
index 0000000..68dcf39
--- /dev/null
+++ b/src/common-ssh/guac_ssh.c
@@ -0,0 +1,511 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "guac_ssh.h"
+#include "guac_ssh_key.h"
+#include "guac_ssh_user.h"
+
+#include <guacamole/client.h>
+#include <libssh2.h>
+
+#ifdef LIBSSH2_USES_GCRYPT
+#include <gcrypt.h>
+#endif
+
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+
+#include <errno.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <pthread.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#ifdef LIBSSH2_USES_GCRYPT
+GCRY_THREAD_OPTION_PTHREAD_IMPL;
+#endif
+
+/**
+ * Array of mutexes, used by OpenSSL.
+ */
+static pthread_mutex_t* guac_common_ssh_openssl_locks;
+
+/**
+ * Called by OpenSSL when locking or unlocking the Nth mutex.
+ *
+ * @param mode
+ *     A bitmask denoting the action to be taken on the Nth lock, such as
+ *     CRYPTO_LOCK or CRYPTO_UNLOCK.
+ *
+ * @param n
+ *     The index of the lock to lock or unlock.
+ *
+ * @param file
+ *     The filename of the function setting the lock, for debugging purposes.
+ *
+ * @param line
+ *     The line number of the function setting the lock, for debugging
+ *     purposes.
+ */
+static void guac_common_ssh_openssl_locking_callback(int mode, int n,
+        const char* file, int line){
+
+    /* Lock given mutex upon request */
+    if (mode & CRYPTO_LOCK)
+        pthread_mutex_lock(&(guac_common_ssh_openssl_locks[n]));
+
+    /* Unlock given mutex upon request */
+    else if (mode & CRYPTO_UNLOCK)
+        pthread_mutex_unlock(&(guac_common_ssh_openssl_locks[n]));
+
+}
+
+/**
+ * Called by OpenSSL when determining the current thread ID.
+ *
+ * @return
+ *     An ID which uniquely identifies the current thread.
+ */
+static unsigned long guac_common_ssh_openssl_id_callback() {
+    return (unsigned long) pthread_self();
+}
+
+/**
+ * Creates the given number of mutexes, such that OpenSSL will have at least
+ * this number of mutexes at its disposal.
+ *
+ * @param count
+ *     The number of mutexes (locks) to create.
+ */
+static void guac_common_ssh_openssl_init_locks(int count) {
+
+    int i;
+
+    /* Allocate required number of locks */
+    guac_common_ssh_openssl_locks =
+        malloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks());
+
+    /* Initialize each lock */
+    for (i=0; i < count; i++)
+        pthread_mutex_init(&(guac_common_ssh_openssl_locks[i]), NULL);
+
+}
+
+/**
+ * Frees the given number of mutexes.
+ *
+ * @param count
+ *     The number of mutexes (locks) to free.
+ */
+static void guac_common_ssh_openssl_free_locks(int count) {
+
+    int i;
+
+    /* Free all locks */
+    for (i=0; i < count; i++)
+        pthread_mutex_destroy(&(guac_common_ssh_openssl_locks[i]));
+
+}
+
+int guac_common_ssh_init(guac_client* client) {
+
+#ifdef LIBSSH2_USES_GCRYPT
+    /* Init threadsafety in libgcrypt */
+    gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+    if (!gcry_check_version(GCRYPT_VERSION)) {
+        guac_client_log(client, GUAC_LOG_ERROR, "libgcrypt version mismatch.");
+        return 1;
+    }
+#endif
+
+    /* Init threadsafety in OpenSSL */
+    guac_common_ssh_openssl_init_locks(CRYPTO_num_locks());
+    CRYPTO_set_id_callback(guac_common_ssh_openssl_id_callback);
+    CRYPTO_set_locking_callback(guac_common_ssh_openssl_locking_callback);
+
+    /* Init OpenSSL */
+    SSL_library_init();
+    ERR_load_crypto_strings();
+
+    /* Init libssh2 */
+    libssh2_init(0);
+
+    /* Success */
+    return 0;
+
+}
+
+void guac_common_ssh_uninit() {
+    guac_common_ssh_openssl_free_locks(CRYPTO_num_locks());
+}
+
+/**
+ * Callback invoked by libssh2 when libssh2_userauth_publickkey() is invoked.
+ * This callback must sign the given data, returning the signature as newly-
+ * allocated buffer space.
+ *
+ * @param session
+ *     The SSH session for which the signature is being generated.
+ *
+ * @param sig
+ *     A pointer to the buffer space containing the signature. This callback
+ *     MUST allocate and assign this space.
+ *
+ * @param sig_len
+ *     The length of the signature within the allocated buffer space, in bytes.
+ *     This value must be set to the size of the signature after the signing
+ *     operation completes.
+ *
+ * @param data
+ *     The arbitrary data that must be signed.
+ *
+ * @param data_len
+ *     The length of the arbitrary data to be signed, in bytes.
+ *
+ * @param abstract
+ *     The value of the abstract parameter provided with the corresponding call
+ *     to libssh2_userauth_publickey().
+ *
+ * @return
+ *     Zero on success, non-zero if the signing operation failed.
+ */
+static int guac_common_ssh_sign_callback(LIBSSH2_SESSION* session,
+        unsigned char** sig, size_t* sig_len,
+        const unsigned char* data, size_t data_len, void **abstract) {
+
+    guac_common_ssh_key* key = (guac_common_ssh_key*) abstract;
+    int length;
+
+    /* Allocate space for signature */
+    *sig = malloc(4096);
+
+    /* Sign with key */
+    length = guac_common_ssh_key_sign(key, (const char*) data, data_len, *sig);
+    if (length < 0)
+        return 1;
+
+    *sig_len = length;
+    return 0;
+}
+
+/**
+ * Callback for the keyboard-interactive authentication method. Currently
+ * supports just one prompt for the password. This callback is invoked as
+ * needed to fullfill a call to libssh2_userauth_keyboard_interactive().
+ *
+ * @param name
+ *     An arbitrary name which should be printed to the terminal for the
+ *     benefit of the user. This is currently ignored.
+ *
+ * @param name_len
+ *     The length of the name string, in bytes.
+ *
+ * @param instruction
+ *     Arbitrary instructions which should be printed to the terminal for the
+ *     benefit of the user. This is currently ignored.
+ *
+ * @param instruction_len
+ *     The length of the instruction string, in bytes.
+ *
+ * @param num_prompts
+ *     The number of keyboard-interactive prompts for which responses are
+ *     requested. This callback currently only supports one prompt, and assumes
+ *     that this prompt is requesting the password.
+ *
+ * @param prompts
+ *     An array of all keyboard-interactive prompts for which responses are
+ *     requested.
+ *
+ * @param responses
+ *     A parallel array into which all prompt responses should be stored. Each
+ *     entry within this array corresponds to the entry in the prompts array
+ *     with the same index.
+ *
+ * @param abstract
+ *     The value of the abstract parameter provided when the SSH session was
+ *     created with libssh2_session_init_ex().
+ */
+static void guac_common_ssh_kbd_callback(const char *name, int name_len,
+        const char *instruction, int instruction_len, int num_prompts,
+        const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts,
+        LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses,
+        void **abstract) {
+
+    guac_common_ssh_session* common_session =
+        (guac_common_ssh_session*) *abstract;
+
+    guac_client* client = common_session->client;
+
+    /* Send password if only one prompt */
+    if (num_prompts == 1) {
+        char* password = common_session->user->password;
+        responses[0].text = strdup(password);
+        responses[0].length = strlen(password);
+    }
+
+    /* If more than one prompt, a single password is not enough */
+    else
+        guac_client_log(client, GUAC_LOG_WARNING,
+                "Unsupported number of keyboard-interactive prompts: %i",
+                num_prompts);
+
+}
+
+/**
+ * Authenticates the user associated with the given session over SSH. All
+ * required credentials must already be present within the user object
+ * associated with the given session.
+ *
+ * @param session
+ *     The session associated with the user to be authenticated.
+ *
+ * @return
+ *     Zero if authentication succeeds, or non-zero if authentication has
+ *     failed.
+ */
+static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session) {
+
+    guac_client* client = common_session->client;
+    guac_common_ssh_user* user = common_session->user;
+    LIBSSH2_SESSION* session = common_session->session;
+
+    /* Get user credentials */
+    char* username = user->username;
+    char* password = user->password;
+    guac_common_ssh_key* key = user->private_key;
+
+    /* Validate username provided */
+    if (username == NULL) {
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
+                "SSH authentication requires a username.");
+        return 1;
+    }
+
+    /* Get list of supported authentication methods */
+    char* user_authlist = libssh2_userauth_list(session, username,
+            strlen(username));
+    guac_client_log(client, GUAC_LOG_DEBUG,
+            "Supported authentication methods: %s", user_authlist);
+
+    /* Authenticate with private key, if provided */
+    if (key != NULL) {
+
+        /* Check if public key auth is supported on the server */
+        if (strstr(user_authlist, "publickey") == NULL) {
+            guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
+                    "Public key authentication is not supported by "
+                    "the SSH server");
+            return 1;
+        }
+
+        /* Attempt public key auth */
+        if (libssh2_userauth_publickey(session, username,
+                    (unsigned char*) key->public_key, key->public_key_length,
+                    guac_common_ssh_sign_callback, (void**) key)) {
+
+            /* Abort on failure */
+            char* error_message;
+            libssh2_session_last_error(session, &error_message, NULL, 0);
+            guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
+                    "Public key authentication failed: %s", error_message);
+
+            return 1;
+
+        }
+
+        /* Private key authentication succeeded */
+        return 0;
+
+    }
+
+    /* Authenticate with password, if provided */
+    else if (password != NULL) {
+
+        /* Authenticate with password */
+        if (strstr(user_authlist, "password") != NULL) {
+            guac_client_log(client, GUAC_LOG_DEBUG,
+                    "Using password authentication method");
+            return libssh2_userauth_password(session, username, password);
+        }
+
+        /* Authenticate with password via keyboard-interactive auth */
+        if (strstr(user_authlist, "keyboard-interactive") != NULL) {
+            guac_client_log(client, GUAC_LOG_DEBUG,
+                    "Using keyboard-interactive authentication method");
+            return libssh2_userauth_keyboard_interactive(session, username,
+                    &guac_common_ssh_kbd_callback);
+        }
+
+        /* No known authentication types available */
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
+                "Password and keyboard-interactive authentication are not "
+                "supported by the SSH server");
+        return 1;
+
+    }
+
+    /* No credentials provided */
+    guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
+            "SSH authentication requires either a private key or a password.");
+    return 1;
+
+}
+
+guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
+        const char* hostname, const char* port, guac_common_ssh_user* user) {
+
+    int retval;
+
+    int fd;
+    struct addrinfo* addresses;
+    struct addrinfo* current_address;
+
+    char connected_address[1024];
+    char connected_port[64];
+
+    struct addrinfo hints = {
+        .ai_family   = AF_UNSPEC,
+        .ai_socktype = SOCK_STREAM,
+        .ai_protocol = IPPROTO_TCP
+    };
+
+    /* Get socket */
+    fd = socket(AF_INET, SOCK_STREAM, 0);
+    if (fd < 0) {
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
+                "Unable to create socket: %s", strerror(errno));
+        return NULL;
+    }
+
+    /* Get addresses connection */
+    if ((retval = getaddrinfo(hostname, port, &hints, &addresses))) {
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
+                "Error parsing given address or port: %s",
+                gai_strerror(retval));
+        close(fd);
+        return NULL;
+    }
+
+    /* Attempt connection to each address until success */
+    current_address = addresses;
+    while (current_address != NULL) {
+
+        /* Resolve hostname */
+        if ((retval = getnameinfo(current_address->ai_addr,
+                current_address->ai_addrlen,
+                connected_address, sizeof(connected_address),
+                connected_port, sizeof(connected_port),
+                NI_NUMERICHOST | NI_NUMERICSERV)))
+            guac_client_log(client, GUAC_LOG_DEBUG,
+                    "Unable to resolve host: %s", gai_strerror(retval));
+
+        /* Connect */
+        if (connect(fd, current_address->ai_addr,
+                        current_address->ai_addrlen) == 0) {
+
+            guac_client_log(client, GUAC_LOG_DEBUG,
+                    "Successfully connected to host %s, port %s",
+                    connected_address, connected_port);
+
+            /* Done if successful connect */
+            break;
+
+        }
+
+        /* Otherwise log information regarding bind failure */
+        else
+            guac_client_log(client, GUAC_LOG_DEBUG, "Unable to connect to "
+                    "host %s, port %s: %s",
+                    connected_address, connected_port, strerror(errno));
+
+        current_address = current_address->ai_next;
+
+    }
+
+    /* If unable to connect to anything, fail */
+    if (current_address == NULL) {
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
+                "Unable to connect to any addresses.");
+        close(fd);
+        return NULL;
+    }
+
+    /* Free addrinfo */
+    freeaddrinfo(addresses);
+
+    /* Allocate new session */
+    guac_common_ssh_session* common_session =
+        malloc(sizeof(guac_common_ssh_session));
+
+    /* Open SSH session */
+    LIBSSH2_SESSION* session = libssh2_session_init_ex(NULL, NULL,
+            NULL, common_session);
+    if (session == NULL) {
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
+                "Session allocation failed.");
+        free(common_session);
+        close(fd);
+        return NULL;
+    }
+
+    /* Perform handshake */
+    if (libssh2_session_handshake(session, fd)) {
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
+                "SSH handshake failed.");
+        free(common_session);
+        close(fd);
+        return NULL;
+    }
+
+    /* Store basic session data */
+    common_session->client = client;
+    common_session->user = user;
+    common_session->session = session;
+    common_session->fd = fd;
+
+    /* Attempt authentication */
+    if (guac_common_ssh_authenticate(common_session)) {
+        free(common_session);
+        close(fd);
+        return NULL;
+    }
+
+    /* Return created session */
+    return common_session;
+
+}
+
+void guac_common_ssh_destroy_session(guac_common_ssh_session* session) {
+
+    /* Disconnect and clean up libssh2 */
+    libssh2_session_disconnect(session->session, "Bye");
+    libssh2_session_free(session->session);
+
+    /* Free all other data */
+    free(session);
+
+}
+
diff --git a/src/common-ssh/guac_ssh.h b/src/common-ssh/guac_ssh.h
new file mode 100644
index 0000000..e7a35fa
--- /dev/null
+++ b/src/common-ssh/guac_ssh.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GUAC_COMMON_SSH_H
+#define GUAC_COMMON_SSH_H
+
+#include "guac_ssh_user.h"
+
+#include <guacamole/client.h>
+#include <libssh2.h>
+
+/**
+ * An SSH session, backed by libssh2 and associated with a particular
+ * Guacamole client.
+ */
+typedef struct guac_common_ssh_session {
+
+    /**
+     * The Guacamole client using this SSH session.
+     */
+    guac_client* client;
+
+    /**
+     * The user that will be authenticating via SSH.
+     */
+    guac_common_ssh_user* user;
+
+    /**
+     * The underlying SSH session from libssh2.
+     */
+    LIBSSH2_SESSION* session;
+
+    /**
+     * The file descriptor of the socket being used for the SSH connection.
+     */
+    int fd;
+
+} guac_common_ssh_session;
+
+/**
+ * Initializes the underlying SSH and encryption libraries used by Guacamole.
+ * This function must be called before any other guac_common_ssh_*() functions
+ * are called.
+ *
+ * @param client
+ *     The Guacamole client that will be using SSH.
+ *
+ * @return
+ *     Zero if initialization, or non-zero if an error occurs.
+ */
+int guac_common_ssh_init(guac_client* client);
+
+/**
+ * Cleans up the underlying SSH and encryption libraries used by Guacamole.
+ * This function must be called once no other guac_common_ssh_*() functions
+ * will be used.
+ */
+void guac_common_ssh_uninit();
+
+/**
+ * Connects to the SSH server running at the given hostname and port, and
+ * authenticates as the given user. If an error occurs while connecting or
+ * authenticating, the Guacamole client will automatically and fatally abort.
+ * The user object provided must eventually be explicitly destroyed, but should
+ * not be destroyed until this session is destroyed, assuming the session is
+ * successfully created.
+ *
+ * @param client
+ *     The Guacamole client that will be using SSH.
+ *
+ * @param hostname
+ *     The hostname of the SSH server to connect to.
+ *
+ * @param port
+ *     The port to connect to on the given hostname.
+ *
+ * @param user
+ *     The user to authenticate as, once connected.
+ *
+ * @return
+ *     A new SSH session if the connection and authentication succeed, or NULL
+ *     if the connection or authentication were not successful.
+ */
+guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
+        const char* hostname, const char* port, guac_common_ssh_user* user);
+
+/**
+ * Disconnects and destroys the given SSH session, freeing all associated
+ * resources. Any associated user must be explicitly destroyed, and will not
+ * be destroyed automatically.
+ *
+ * @param session
+ *     The SSH session to destroy.
+ */
+void guac_common_ssh_destroy_session(guac_common_ssh_session* session);
+
+#endif
+
diff --git a/src/common-ssh/guac_ssh_buffer.c b/src/common-ssh/guac_ssh_buffer.c
new file mode 100644
index 0000000..0390c40
--- /dev/null
+++ b/src/common-ssh/guac_ssh_buffer.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <openssl/bn.h>
+#include <openssl/ossl_typ.h>
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+
+void guac_common_ssh_buffer_write_byte(char** buffer, uint8_t value) {
+
+    uint8_t* data = (uint8_t*) *buffer;
+    *data = value;
+
+    (*buffer)++;
+
+}
+
+void guac_common_ssh_buffer_write_uint32(char** buffer, uint32_t value) {
+
+    uint8_t* data = (uint8_t*) *buffer;
+
+    data[0] = (value & 0xFF000000) >> 24;
+    data[1] = (value & 0x00FF0000) >> 16;
+    data[2] = (value & 0x0000FF00) >> 8;
+    data[3] =  value & 0x000000FF;
+
+    *buffer += 4;
+
+}
+
+void guac_common_ssh_buffer_write_data(char** buffer, const char* data,
+        int length) {
+    memcpy(*buffer, data, length);
+    *buffer += length;
+}
+
+void guac_common_ssh_buffer_write_bignum(char** buffer, BIGNUM* value) {
+
+    unsigned char* bn_buffer;
+    int length;
+
+    /* If zero, just write zero length */
+    if (BN_is_zero(value)) {
+        guac_common_ssh_buffer_write_uint32(buffer, 0);
+        return;
+    }
+
+    /* Allocate output buffer, add padding byte */
+    length = BN_num_bytes(value);
+    bn_buffer = malloc(length);
+
+    /* Convert BIGNUM */
+    BN_bn2bin(value, bn_buffer);
+
+    /* If first byte has high bit set, write padding byte */
+    if (bn_buffer[0] & 0x80) {
+        guac_common_ssh_buffer_write_uint32(buffer, length+1);
+        guac_common_ssh_buffer_write_byte(buffer, 0);
+    }
+    else
+        guac_common_ssh_buffer_write_uint32(buffer, length);
+
+    /* Write data */
+    memcpy(*buffer, bn_buffer, length);
+    *buffer += length;
+
+    free(bn_buffer);
+
+}
+
+void guac_common_ssh_buffer_write_string(char** buffer, const char* string,
+        int length) {
+    guac_common_ssh_buffer_write_uint32(buffer, length);
+    guac_common_ssh_buffer_write_data(buffer, string, length);
+}
+
+uint8_t guac_common_ssh_buffer_read_byte(char** buffer) {
+
+    uint8_t* data = (uint8_t*) *buffer;
+    uint8_t value = *data;
+
+    (*buffer)++;
+
+    return value;
+
+}
+
+uint32_t guac_common_ssh_buffer_read_uint32(char** buffer) {
+
+    uint8_t* data = (uint8_t*) *buffer;
+    uint32_t value =
+          (data[0] << 24)
+        | (data[1] << 16)
+        | (data[2] <<  8)
+        |  data[3];
+
+    *buffer += 4;
+
+    return value;
+
+}
+
+char* guac_common_ssh_buffer_read_string(char** buffer, int* length) {
+
+    char* value;
+
+    *length = guac_common_ssh_buffer_read_uint32(buffer);
+    value = *buffer;
+
+    *buffer += *length;
+
+    return value;
+
+}
+
diff --git a/src/common-ssh/guac_ssh_buffer.h b/src/common-ssh/guac_ssh_buffer.h
new file mode 100644
index 0000000..0c77bba
--- /dev/null
+++ b/src/common-ssh/guac_ssh_buffer.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GUAC_COMMON_SSH_BUFFER_H
+#define GUAC_COMMON_SSH_BUFFER_H
+
+#include "config.h"
+
+#include <openssl/bn.h>
+#include <stdint.h>
+
+/**
+ * Writes the given byte to the given buffer, advancing the buffer pointer by
+ * one byte.
+ *
+ * @param buffer
+ *     The buffer to write to.
+ *
+ * @param value
+ *     The value to write.
+ */
+void guac_common_ssh_buffer_write_byte(char** buffer, uint8_t value);
+
+/**
+ * Writes the given integer to the given buffer, advancing the buffer pointer
+ * four bytes.
+ *
+ * @param buffer
+ *     The buffer to write to.
+ *
+ * @param value
+ *     The value to write.
+ */
+void guac_common_ssh_buffer_write_uint32(char** buffer, uint32_t value);
+
+/**
+ * Writes the given string and its length to the given buffer, advancing the
+ * buffer pointer by the size of the length (four bytes) and the size of the
+ * string.
+ *
+ * @param buffer
+ *     The buffer to write to.
+ *
+ * @param string
+ *     The string value to write.
+ *
+ * @param length
+ *     The length of the string to write, in bytes.
+ */
+void guac_common_ssh_buffer_write_string(char** buffer, const char* string,
+        int length);
+
+/**
+ * Writes the given BIGNUM the given buffer, advancing the buffer pointer by
+ * the size of the length (four bytes) and the size of the BIGNUM.
+ *
+ * @param buffer
+ *     The buffer to write to.
+ *
+ * @param value
+ *     The value to write.
+ */
+void guac_common_ssh_buffer_write_bignum(char** buffer, BIGNUM* value);
+
+/**
+ * Writes the given data the given buffer, advancing the buffer pointer by the
+ * given length.
+ *
+ * @param data
+ *     The arbitrary data to write.
+ *
+ * @param length
+ *     The length of data to write, in bytes.
+ */
+void guac_common_ssh_buffer_write_data(char** buffer, const char* data, int length);
+
+/**
+ * Reads a single byte from the given buffer, advancing the buffer by one byte.
+ *
+ * @param buffer
+ *     The buffer to read from.
+ *
+ * @return
+ *     The value read from the buffer.
+ */
+uint8_t guac_common_ssh_buffer_read_byte(char** buffer);
+
+/**
+ * Reads an integer from the given buffer, advancing the buffer by four bytes.
+ *
+ * @param buffer
+ *     The buffer to read from.
+ *
+ * @return
+ *     The value read from the buffer.
+ */
+uint32_t guac_common_ssh_buffer_read_uint32(char** buffer);
+
+/**
+ * Reads a string and its length from the given buffer, advancing the buffer
+ * by the size of the length (four bytes) and the size of the string, and
+ * returning a pointer to the buffer. The length of the string is stored in
+ * the given int.
+ *
+ * @param buffer
+ *     The buffer to read from.
+ *
+ * @param length
+ *     A pointer to an integer into which the length of the read string will
+ *     be stored.
+ *
+ * @return
+ *     A pointer to the value within the buffer.
+ */
+char* guac_common_ssh_buffer_read_string(char** buffer, int* length);
+
+#endif
+
diff --git a/src/common-ssh/guac_ssh_key.c b/src/common-ssh/guac_ssh_key.c
new file mode 100644
index 0000000..adbe041
--- /dev/null
+++ b/src/common-ssh/guac_ssh_key.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "guac_ssh_buffer.h"
+#include "guac_ssh_key.h"
+
+#include <openssl/bio.h>
+#include <openssl/bn.h>
+#include <openssl/dsa.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/obj_mac.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+guac_common_ssh_key* guac_common_ssh_key_alloc(char* data, int length,
+        char* passphrase) {
+
+    guac_common_ssh_key* key;
+    BIO* key_bio;
+
+    char* public_key;
+    char* pos;
+
+    /* Create BIO for reading key from memory */
+    key_bio = BIO_new_mem_buf(data, length);
+
+    /* If RSA key, load RSA */
+    if (length > sizeof(SSH_RSA_KEY_HEADER)-1
+            && memcmp(SSH_RSA_KEY_HEADER, data,
+                      sizeof(SSH_RSA_KEY_HEADER)-1) == 0) {
+
+        RSA* rsa_key;
+
+        /* Read key */
+        rsa_key = PEM_read_bio_RSAPrivateKey(key_bio, NULL, NULL, passphrase);
+        if (rsa_key == NULL)
+            return NULL;
+
+        /* Allocate key */
+        key = malloc(sizeof(guac_common_ssh_key));
+        key->rsa = rsa_key;
+
+        /* Set type */
+        key->type = SSH_KEY_RSA;
+
+        /* Allocate space for public key */
+        public_key = malloc(4096);
+        pos = public_key;
+
+        /* Derive public key */
+        guac_common_ssh_buffer_write_string(&pos, "ssh-rsa", sizeof("ssh-rsa")-1);
+        guac_common_ssh_buffer_write_bignum(&pos, rsa_key->e);
+        guac_common_ssh_buffer_write_bignum(&pos, rsa_key->n);
+
+        /* Save public key to structure */
+        key->public_key = public_key;
+        key->public_key_length = pos - public_key;
+
+    }
+
+    /* If DSA key, load DSA */
+    else if (length > sizeof(SSH_DSA_KEY_HEADER)-1
+            && memcmp(SSH_DSA_KEY_HEADER, data,
+                      sizeof(SSH_DSA_KEY_HEADER)-1) == 0) {
+
+        DSA* dsa_key;
+
+        /* Read key */
+        dsa_key = PEM_read_bio_DSAPrivateKey(key_bio, NULL, NULL, passphrase);
+        if (dsa_key == NULL)
+            return NULL;
+
+        /* Allocate key */
+        key = malloc(sizeof(guac_common_ssh_key));
+        key->dsa = dsa_key;
+
+        /* Set type */
+        key->type = SSH_KEY_DSA;
+
+        /* Allocate space for public key */
+        public_key = malloc(4096);
+        pos = public_key;
+
+        /* Derive public key */
+        guac_common_ssh_buffer_write_string(&pos, "ssh-dss", sizeof("ssh-dss")-1);
+        guac_common_ssh_buffer_write_bignum(&pos, dsa_key->p);
+        guac_common_ssh_buffer_write_bignum(&pos, dsa_key->q);
+        guac_common_ssh_buffer_write_bignum(&pos, dsa_key->g);
+        guac_common_ssh_buffer_write_bignum(&pos, dsa_key->pub_key);
+
+        /* Save public key to structure */
+        key->public_key = public_key;
+        key->public_key_length = pos - public_key;
+
+    }
+
+    /* Otherwise, unsupported type */
+    else {
+        BIO_free(key_bio);
+        return NULL;
+    }
+
+    /* Copy private key to structure */
+    key->private_key_length = length;
+    key->private_key = malloc(length);
+    memcpy(key->private_key, data, length);
+
+    BIO_free(key_bio);
+    return key;
+
+}
+
+const char* guac_common_ssh_key_error() {
+
+    /* Return static error string */
+    return ERR_reason_error_string(ERR_get_error());
+
+}
+
+void guac_common_ssh_key_free(guac_common_ssh_key* key) {
+
+    /* Free key-specific data */
+    if (key->type == SSH_KEY_RSA)
+        RSA_free(key->rsa);
+    else if (key->type == SSH_KEY_DSA)
+        DSA_free(key->dsa);
+
+    free(key->public_key);
+    free(key);
+}
+
+int guac_common_ssh_key_sign(guac_common_ssh_key* key, const char* data,
+        int length, unsigned char* sig) {
+
+    const EVP_MD* md;
+    EVP_MD_CTX md_ctx;
+
+    unsigned char digest[EVP_MAX_MD_SIZE];
+    unsigned int dlen, len;
+
+    /* Get SHA1 digest */
+    if ((md = EVP_get_digestbynid(NID_sha1)) == NULL)
+        return -1;
+
+    /* Digest data */
+    EVP_DigestInit(&md_ctx, md);
+    EVP_DigestUpdate(&md_ctx, data, length);
+    EVP_DigestFinal(&md_ctx, digest, &dlen);
+
+    /* Sign with key */
+    switch (key->type) {
+
+        case SSH_KEY_RSA:
+            if (RSA_sign(NID_sha1, digest, dlen, sig, &len, key->rsa) == 1)
+                return len;
+            break;
+
+        case SSH_KEY_DSA: {
+
+            DSA_SIG* dsa_sig = DSA_do_sign(digest, dlen, key->dsa);
+            if (dsa_sig != NULL) {
+
+                /* Compute size of each half of signature */
+                int rlen = BN_num_bytes(dsa_sig->r);
+                int slen = BN_num_bytes(dsa_sig->s);
+
+                /* Ensure each number is within the required size */
+                if (rlen > DSA_SIG_NUMBER_SIZE || slen > DSA_SIG_NUMBER_SIZE)
+                    return -1;
+
+                /* Init to all zeroes */
+                memset(sig, 0, DSA_SIG_SIZE);
+
+                /* Add R at the end of the first block of the signature */
+                BN_bn2bin(dsa_sig->r, sig + DSA_SIG_SIZE
+                                          - DSA_SIG_NUMBER_SIZE - rlen);
+
+                /* Add S at the end of the second block of the signature */
+                BN_bn2bin(dsa_sig->s, sig + DSA_SIG_SIZE - slen);
+
+                /* Done */
+                DSA_SIG_free(dsa_sig);
+                return DSA_SIG_SIZE;
+
+            }
+
+        }
+
+    }
+
+    return -1;
+
+}
+
diff --git a/src/common-ssh/guac_ssh_key.h b/src/common-ssh/guac_ssh_key.h
new file mode 100644
index 0000000..e76ac06
--- /dev/null
+++ b/src/common-ssh/guac_ssh_key.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GUAC_COMMON_SSH_KEY_H
+#define GUAC_COMMON_SSH_KEY_H
+
+#include "config.h"
+
+#include <openssl/ossl_typ.h>
+
+/**
+ * The expected header of RSA private keys.
+ */
+#define SSH_RSA_KEY_HEADER "-----BEGIN RSA PRIVATE KEY-----"
+
+/**
+ * The expected header of DSA private keys.
+ */
+#define SSH_DSA_KEY_HEADER "-----BEGIN DSA PRIVATE KEY-----"
+
+/**
+ * The size of single number within a DSA signature, in bytes.
+ */
+#define DSA_SIG_NUMBER_SIZE 20
+
+/**
+ * The size of a DSA signature, in bytes.
+ */
+#define DSA_SIG_SIZE DSA_SIG_NUMBER_SIZE*2 
+
+/**
+ * The type of an SSH key.
+ */
+typedef enum guac_common_ssh_key_type {
+
+    /**
+     * RSA key.
+     */
+    SSH_KEY_RSA,
+
+    /**
+     * DSA key.
+     */
+    SSH_KEY_DSA
+
+} guac_common_ssh_key_type;
+
+/**
+ * Abstraction of a key used for SSH authentication.
+ */
+typedef struct guac_common_ssh_key {
+
+    /**
+     * The type of this key.
+     */
+    guac_common_ssh_key_type type;
+
+    /**
+     * Underlying RSA private key, if any.
+     */
+    RSA* rsa;
+
+    /**
+     * Underlying DSA private key, if any.
+     */
+    DSA* dsa;
+
+    /**
+     * The associated public key, encoded as necessary for SSH.
+     */
+    char* public_key;
+
+    /**
+     * The length of the public key, in bytes.
+     */
+    int public_key_length;
+
+    /**
+     * The private key, encoded as necessary for SSH.
+     */
+    char* private_key;
+
+    /**
+     * The length of the private key, in bytes.
+     */
+    int private_key_length;
+
+} guac_common_ssh_key;
+
+/**
+ * Allocates a new key containing the given private key data and specified
+ * passphrase. If unable to read the key, NULL is returned.
+ *
+ * @param data
+ *     The base64-encoded data to decode when reading the key.
+ *
+ * @param length
+ *     The length of the provided data, in bytes.
+ *
+ * @param passphrase
+ *     The passphrase to use when decrypting the key, if any, or an empty
+ *     string or NULL if no passphrase is needed.
+ *
+ * @return
+ *     The decoded, decrypted private key, or NULL if the key could not be
+ *     decoded.
+ */
+guac_common_ssh_key* guac_common_ssh_key_alloc(char* data, int length,
+        char* passphrase);
+
+/**
+ * Returns a statically-allocated string describing the most recent SSH key
+ * error.
+ *
+ * @return
+ *     A statically-allocated string describing the most recent SSH key error.
+ */
+const char* guac_common_ssh_key_error();
+
+/**
+ * Frees all memory associated with the given key.
+ *
+ * @param key
+ *     The key to free.
+ */
+void guac_common_ssh_key_free(guac_common_ssh_key* key);
+
+/**
+ * Signs the given data using the given key, returning the length of the
+ * signature in bytes, or a value less than zero on error.
+ *
+ * @param key
+ *     The key to use when signing the given data.
+ *
+ * @param data
+ *     The arbitrary data to sign.
+ *
+ * @param length
+ *     The length of the arbitrary data being signed, in bytes.
+ *
+ * @param sig
+ *     The buffer into which the signature should be written. The buffer must
+ *     be at least DSA_SIG_SIZE for DSA keys. For RSA keys, the signature size
+ *     is dependent only on key size, and is equal to the length of the
+ *     modulus, in bytes.
+ *
+ * @return
+ *     The number of bytes in the resulting signature.
+ */
+int guac_common_ssh_key_sign(guac_common_ssh_key* key, const char* data,
+        int length, unsigned char* sig);
+
+#endif
+
diff --git a/src/common-ssh/guac_ssh_user.c b/src/common-ssh/guac_ssh_user.c
new file mode 100644
index 0000000..c9691b1
--- /dev/null
+++ b/src/common-ssh/guac_ssh_user.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "guac_ssh_key.h"
+#include "guac_ssh_user.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+guac_common_ssh_user* guac_common_ssh_create_user(const char* username) {
+
+    guac_common_ssh_user* user = malloc(sizeof(guac_common_ssh_user));
+
+    /* Init user */
+    user->username = strdup(username);
+    user->password = NULL;
+    user->private_key = NULL;
+
+    return user;
+
+}
+
+void guac_common_ssh_destroy_user(guac_common_ssh_user* user) {
+
+    /* Free private key, if present */
+    if (user->private_key != NULL)
+        guac_common_ssh_key_free(user->private_key);
+
+    /* Free all other data */
+    free(user->password);
+    free(user->username);
+    free(user);
+
+}
+
+void guac_common_ssh_user_set_password(guac_common_ssh_user* user,
+        const char* password) {
+
+    /* Replace current password with given value */
+    free(user->password);
+    user->password = strdup(password);
+
+}
+
+int guac_common_ssh_user_import_key(guac_common_ssh_user* user,
+        char* private_key, char* passphrase) {
+
+    /* Free existing private key, if present */
+    if (user->private_key != NULL)
+        guac_common_ssh_key_free(user->private_key);
+
+    /* Attempt to read key without passphrase if none given */
+    if (passphrase == NULL)
+        user->private_key = guac_common_ssh_key_alloc(private_key,
+                strlen(private_key), "");
+
+    /* Otherwise, use provided passphrase */
+    else
+        user->private_key = guac_common_ssh_key_alloc(private_key,
+                strlen(private_key), passphrase);
+
+    /* Fail if key could not be read */
+    return user->private_key == NULL;
+
+}
+
diff --git a/src/common-ssh/guac_ssh_user.h b/src/common-ssh/guac_ssh_user.h
new file mode 100644
index 0000000..760523a
--- /dev/null
+++ b/src/common-ssh/guac_ssh_user.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GUAC_COMMON_SSH_USER_H
+#define GUAC_COMMON_SSH_USER_H
+
+#include "guac_ssh_key.h"
+
+/**
+ * Data describing an SSH user, including their credentials.
+ */
+typedef struct guac_common_ssh_user {
+
+    /**
+     * The username of this user.
+     */
+    char* username;
+
+    /**
+     * The password which should be used to authenticate this user, if any, or
+     * NULL if a private key will be used instead.
+     */
+    char* password;
+
+    /**
+     * The private key which should be used to authenticate this user, if any,
+     * or NULL if a password will be used instead.
+     */
+    guac_common_ssh_key* private_key;
+
+} guac_common_ssh_user;
+
+/**
+ * Creates a new SSH user with the given username. When additionally populated
+ * with a password or private key, this user can then be used for
+ * authentication.
+ *
+ * @param username
+ *     The username of the user being created.
+ *
+ * @return
+ *     A new SSH user having the given username, but no associated password
+ *     or private key.
+ */
+guac_common_ssh_user* guac_common_ssh_create_user(const char* username);
+
+/**
+ * Destroys the given user object, releasing all associated resources.
+ *
+ * @param user
+ *     The user to destroy.
+ */
+void guac_common_ssh_destroy_user(guac_common_ssh_user* user);
+
+/**
+ * Associates the given user with the given password, such that that password
+ * is used for future authentication attempts.
+ *
+ * @param user
+ *     The user to associate with the given password.
+ *
+ * @param password
+ *     The password to associate with the given user.
+ */
+void guac_common_ssh_user_set_password(guac_common_ssh_user* user,
+        const char* password);
+
+/**
+ * Imports the given private key, associating that key with the given user. If
+ * necessary to decrypt the key, a passphrase may be specified. The private key
+ * must be provided in base64 form. If the private key is imported
+ * successfully, it will be used for future authentication attempts.
+ *
+ * @param user
+ *     The user to associate with the given private key.
+ *
+ * @param private_key
+ *     The base64-encoded private key to import.
+ *
+ * @param passphrase
+ *     The passphrase to use to decrypt the given private key, or NULL if no
+ *     passphrase should be used.
+ *
+ * @return
+ *     Zero if the private key is successfully imported, or non-zero if the
+ *     private key could not be imported due to an error.
+ */
+int guac_common_ssh_user_import_key(guac_common_ssh_user* user,
+        char* private_key, char* passphrase);
+
+#endif
+
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
new file mode 100644
index 0000000..4f4b02c
--- /dev/null
+++ b/src/common/Makefile.am
@@ -0,0 +1,58 @@
+#
+# Copyright (C) 2015 Glyptodon LLC
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+AUTOMAKE_OPTIONS = foreign 
+ACLOCAL_AMFLAGS = -I m4
+
+noinst_LTLIBRARIES = libguac_common.la
+
+noinst_HEADERS =          \
+    guac_io.h             \
+    guac_clipboard.h      \
+    guac_dot_cursor.h     \
+    guac_iconv.h          \
+    guac_json.h           \
+    guac_list.h           \
+    guac_pointer_cursor.h \
+    guac_rect.h           \
+    guac_string.h         \
+    guac_surface.h
+
+libguac_common_la_SOURCES = \
+    guac_io.c               \
+    guac_clipboard.c        \
+    guac_dot_cursor.c       \
+    guac_iconv.c            \
+    guac_json.c             \
+    guac_list.c             \
+    guac_pointer_cursor.c   \
+    guac_rect.c             \
+    guac_string.c           \
+    guac_surface.c
+
+libguac_common_la_CFLAGS =  \
+    -Werror -Wall -pedantic \
+    @LIBGUAC_INCLUDE@
+
+libguac_common_la_LIBADD = \
+    @LIBGUAC_LTLIB@
+
diff --git a/src/common/Makefile.in b/src/common/Makefile.in
new file mode 100644
index 0000000..c026cdc
--- /dev/null
+++ b/src/common/Makefile.in
@@ -0,0 +1,752 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+ at SET_MAKE@
+
+#
+# Copyright (C) 2015 Glyptodon LLC
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/common
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(top_srcdir)/depcomp $(noinst_HEADERS)
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
+	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+	$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libguac_common_la_DEPENDENCIES =
+am_libguac_common_la_OBJECTS = libguac_common_la-guac_io.lo \
+	libguac_common_la-guac_clipboard.lo \
+	libguac_common_la-guac_dot_cursor.lo \
+	libguac_common_la-guac_iconv.lo libguac_common_la-guac_json.lo \
+	libguac_common_la-guac_list.lo \
+	libguac_common_la-guac_pointer_cursor.lo \
+	libguac_common_la-guac_rect.lo \
+	libguac_common_la-guac_string.lo \
+	libguac_common_la-guac_surface.lo
+libguac_common_la_OBJECTS = $(am_libguac_common_la_OBJECTS)
+AM_V_lt = $(am__v_lt_ at AM_V@)
+am__v_lt_ = $(am__v_lt_ at AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+libguac_common_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(libguac_common_la_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \
+	-o $@
+AM_V_P = $(am__v_P_ at AM_V@)
+am__v_P_ = $(am__v_P_ at AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_ at AM_V@)
+am__v_GEN_ = $(am__v_GEN_ at AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_ at AM_V@)
+am__v_at_ = $(am__v_at_ at AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I. at am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_ at AM_V@)
+am__v_CC_ = $(am__v_CC_ at AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_ at AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_ at AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
+SOURCES = $(libguac_common_la_SOURCES)
+DIST_SOURCES = $(libguac_common_la_SOURCES)
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CAIRO_LIBS = @CAIRO_LIBS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+COMMON_INCLUDE = @COMMON_INCLUDE@
+COMMON_LTLIB = @COMMON_LTLIB@
+COMMON_SSH_INCLUDE = @COMMON_SSH_INCLUDE@
+COMMON_SSH_LTLIB = @COMMON_SSH_LTLIB@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CUNIT_LIBS = @CUNIT_LIBS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+JPEG_LIBS = @JPEG_LIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBADD_DLOPEN = @LIBADD_DLOPEN@
+LIBGUAC_INCLUDE = @LIBGUAC_INCLUDE@
+LIBGUAC_LTLIB = @LIBGUAC_LTLIB@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MATH_LIBS = @MATH_LIBS@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGO_CFLAGS = @PANGO_CFLAGS@
+PANGO_LIBS = @PANGO_LIBS@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_LIBS = @PNG_LIBS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+PULSE_LIBS = @PULSE_LIBS@
+RANLIB = @RANLIB@
+RDP_LIBS = @RDP_LIBS@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SSH_LIBS = @SSH_LIBS@
+SSL_LIBS = @SSL_LIBS@
+STRIP = @STRIP@
+TELNET_LIBS = @TELNET_LIBS@
+TERMINAL_INCLUDE = @TERMINAL_INCLUDE@
+TERMINAL_LTLIB = @TERMINAL_LTLIB@
+UUID_LIBS = @UUID_LIBS@
+VERSION = @VERSION@
+VNC_LIBS = @VNC_LIBS@
+VORBIS_LIBS = @VORBIS_LIBS@
+WEBP_LIBS = @WEBP_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+init_dir = @init_dir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AUTOMAKE_OPTIONS = foreign 
+ACLOCAL_AMFLAGS = -I m4
+noinst_LTLIBRARIES = libguac_common.la
+noinst_HEADERS = \
+    guac_io.h             \
+    guac_clipboard.h      \
+    guac_dot_cursor.h     \
+    guac_iconv.h          \
+    guac_json.h           \
+    guac_list.h           \
+    guac_pointer_cursor.h \
+    guac_rect.h           \
+    guac_string.h         \
+    guac_surface.h
+
+libguac_common_la_SOURCES = \
+    guac_io.c               \
+    guac_clipboard.c        \
+    guac_dot_cursor.c       \
+    guac_iconv.c            \
+    guac_json.c             \
+    guac_list.c             \
+    guac_pointer_cursor.c   \
+    guac_rect.c             \
+    guac_string.c           \
+    guac_surface.c
+
+libguac_common_la_CFLAGS = \
+    -Werror -Wall -pedantic \
+    @LIBGUAC_INCLUDE@
+
+libguac_common_la_LIBADD = \
+    @LIBGUAC_LTLIB@
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/common/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --foreign src/common/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure:  $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+	-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+	@list='$(noinst_LTLIBRARIES)'; \
+	locs=`for p in $$list; do echo $$p; done | \
+	      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+	      sort -u`; \
+	test -z "$$locs" || { \
+	  echo rm -f $${locs}; \
+	  rm -f $${locs}; \
+	}
+
+libguac_common.la: $(libguac_common_la_OBJECTS) $(libguac_common_la_DEPENDENCIES) $(EXTRA_libguac_common_la_DEPENDENCIES) 
+	$(AM_V_CCLD)$(libguac_common_la_LINK)  $(libguac_common_la_OBJECTS) $(libguac_common_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_common_la-guac_clipboard.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_common_la-guac_dot_cursor.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_common_la-guac_iconv.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_common_la-guac_io.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_common_la-guac_json.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_common_la-guac_list.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_common_la-guac_pointer_cursor.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_common_la-guac_rect.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_common_la-guac_string.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_common_la-guac_surface.Plo at am__quote@
+
+.c.o:
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+ at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+ at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+ at am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+libguac_common_la-guac_io.lo: guac_io.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_la_CFLAGS) $(CFLAGS) -MT libguac_common_la-guac_io.lo -MD -MP -MF $(DEPDIR)/libguac_common_la-guac_io.Tpo -c -o libguac_common_la-guac_io.lo `test -f 'guac_io.c' || echo '$(srcdir)/'`guac_io.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_common_la-guac_io.Tpo $(DEPDIR)/libguac_common_la-guac_io.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_io.c' object='libguac_common_la-guac_io.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_la_CFLAGS) $(CFLAGS) -c -o libguac_common_la-guac_io.lo `test -f 'guac_io.c' || echo '$(srcdir)/'`guac_io.c
+
+libguac_common_la-guac_clipboard.lo: guac_clipboard.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_la_CFLAGS) $(CFLAGS) -MT libguac_common_la-guac_clipboard.lo -MD -MP -MF $(DEPDIR)/libguac_common_la-guac_clipboard.Tpo -c -o libguac_common_la-guac_clipboard.lo `test -f 'guac_clipboard.c' || echo '$(srcdir)/'`guac_clipboard.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_common_la-guac_clipboard.Tpo $(DEPDIR)/libguac_common_la-guac_clipboard.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_clipboard.c' object='libguac_common_la-guac_clipboard.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_la_CFLAGS) $(CFLAGS) -c -o libguac_common_la-guac_clipboard.lo `test -f 'guac_clipboard.c' || echo '$(srcdir)/'`guac_clipboard.c
+
+libguac_common_la-guac_dot_cursor.lo: guac_dot_cursor.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_la_CFLAGS) $(CFLAGS) -MT libguac_common_la-guac_dot_cursor.lo -MD -MP -MF $(DEPDIR)/libguac_common_la-guac_dot_cursor.Tpo -c -o libguac_common_la-guac_dot_cursor.lo `test -f 'guac_dot_cursor.c' || echo '$(srcdir)/'`guac_dot_cursor.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_common_la-guac_dot_cursor.Tpo $(DEPDIR)/libguac_common_la-guac_dot_cursor.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_dot_cursor.c' object='libguac_common_la-guac_dot_cursor.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_la_CFLAGS) $(CFLAGS) -c -o libguac_common_la-guac_dot_cursor.lo `test -f 'guac_dot_cursor.c' || echo '$(srcdir)/'`guac_dot_cursor.c
+
+libguac_common_la-guac_iconv.lo: guac_iconv.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_la_CFLAGS) $(CFLAGS) -MT libguac_common_la-guac_iconv.lo -MD -MP -MF $(DEPDIR)/libguac_common_la-guac_iconv.Tpo -c -o libguac_common_la-guac_iconv.lo `test -f 'guac_iconv.c' || echo '$(srcdir)/'`guac_iconv.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_common_la-guac_iconv.Tpo $(DEPDIR)/libguac_common_la-guac_iconv.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_iconv.c' object='libguac_common_la-guac_iconv.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_la_CFLAGS) $(CFLAGS) -c -o libguac_common_la-guac_iconv.lo `test -f 'guac_iconv.c' || echo '$(srcdir)/'`guac_iconv.c
+
+libguac_common_la-guac_json.lo: guac_json.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_la_CFLAGS) $(CFLAGS) -MT libguac_common_la-guac_json.lo -MD -MP -MF $(DEPDIR)/libguac_common_la-guac_json.Tpo -c -o libguac_common_la-guac_json.lo `test -f 'guac_json.c' || echo '$(srcdir)/'`guac_json.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_common_la-guac_json.Tpo $(DEPDIR)/libguac_common_la-guac_json.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_json.c' object='libguac_common_la-guac_json.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_la_CFLAGS) $(CFLAGS) -c -o libguac_common_la-guac_json.lo `test -f 'guac_json.c' || echo '$(srcdir)/'`guac_json.c
+
+libguac_common_la-guac_list.lo: guac_list.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_la_CFLAGS) $(CFLAGS) -MT libguac_common_la-guac_list.lo -MD -MP -MF $(DEPDIR)/libguac_common_la-guac_list.Tpo -c -o libguac_common_la-guac_list.lo `test -f 'guac_list.c' || echo '$(srcdir)/'`guac_list.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_common_la-guac_list.Tpo $(DEPDIR)/libguac_common_la-guac_list.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_list.c' object='libguac_common_la-guac_list.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_la_CFLAGS) $(CFLAGS) -c -o libguac_common_la-guac_list.lo `test -f 'guac_list.c' || echo '$(srcdir)/'`guac_list.c
+
+libguac_common_la-guac_pointer_cursor.lo: guac_pointer_cursor.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_la_CFLAGS) $(CFLAGS) -MT libguac_common_la-guac_pointer_cursor.lo -MD -MP -MF $(DEPDIR)/libguac_common_la-guac_pointer_cursor.Tpo -c -o libguac_common_la-guac_pointer_cursor.lo `test -f 'guac_pointer_cursor.c' || echo '$(srcdir)/'`guac_pointer_cursor.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_common_la-guac_pointer_cursor.Tpo $(DEPDIR)/libguac_common_la-guac_pointer_cursor.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_pointer_cursor.c' object='libguac_common_la-guac_pointer_cursor.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_la_CFLAGS) $(CFLAGS) -c -o libguac_common_la-guac_pointer_cursor.lo `test -f 'guac_pointer_cursor.c' || echo '$(srcdir)/'`guac_pointer_cursor.c
+
+libguac_common_la-guac_rect.lo: guac_rect.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_la_CFLAGS) $(CFLAGS) -MT libguac_common_la-guac_rect.lo -MD -MP -MF $(DEPDIR)/libguac_common_la-guac_rect.Tpo -c -o libguac_common_la-guac_rect.lo `test -f 'guac_rect.c' || echo '$(srcdir)/'`guac_rect.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_common_la-guac_rect.Tpo $(DEPDIR)/libguac_common_la-guac_rect.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_rect.c' object='libguac_common_la-guac_rect.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_la_CFLAGS) $(CFLAGS) -c -o libguac_common_la-guac_rect.lo `test -f 'guac_rect.c' || echo '$(srcdir)/'`guac_rect.c
+
+libguac_common_la-guac_string.lo: guac_string.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_la_CFLAGS) $(CFLAGS) -MT libguac_common_la-guac_string.lo -MD -MP -MF $(DEPDIR)/libguac_common_la-guac_string.Tpo -c -o libguac_common_la-guac_string.lo `test -f 'guac_string.c' || echo '$(srcdir)/'`guac_string.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_common_la-guac_string.Tpo $(DEPDIR)/libguac_common_la-guac_string.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_string.c' object='libguac_common_la-guac_string.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_la_CFLAGS) $(CFLAGS) -c -o libguac_common_la-guac_string.lo `test -f 'guac_string.c' || echo '$(srcdir)/'`guac_string.c
+
+libguac_common_la-guac_surface.lo: guac_surface.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_la_CFLAGS) $(CFLAGS) -MT libguac_common_la-guac_surface.lo -MD -MP -MF $(DEPDIR)/libguac_common_la-guac_surface.Tpo -c -o libguac_common_la-guac_surface.lo `test -f 'guac_surface.c' || echo '$(srcdir)/'`guac_surface.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_common_la-guac_surface.Tpo $(DEPDIR)/libguac_common_la-guac_surface.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_surface.c' object='libguac_common_la-guac_surface.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_common_la_CFLAGS) $(CFLAGS) -c -o libguac_common_la-guac_surface.lo `test -f 'guac_surface.c' || echo '$(srcdir)/'`guac_surface.c
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+	mostlyclean-am
+
+distclean: distclean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
+	clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \
+	ctags-am distclean distclean-compile distclean-generic \
+	distclean-libtool distclean-tags distdir dvi dvi-am html \
+	html-am info info-am install install-am install-data \
+	install-data-am install-dvi install-dvi-am install-exec \
+	install-exec-am install-html install-html-am install-info \
+	install-info-am install-man install-pdf install-pdf-am \
+	install-ps install-ps-am install-strip installcheck \
+	installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+	tags tags-am uninstall uninstall-am
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/common/guac_clipboard.c b/src/common/guac_clipboard.c
new file mode 100644
index 0000000..0f880bc
--- /dev/null
+++ b/src/common/guac_clipboard.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+#include "guac_clipboard.h"
+
+#include <guacamole/client.h>
+#include <guacamole/protocol.h>
+#include <guacamole/stream.h>
+#include <string.h>
+#include <stdlib.h>
+
+guac_common_clipboard* guac_common_clipboard_alloc(int size) {
+
+    guac_common_clipboard* clipboard = malloc(sizeof(guac_common_clipboard));
+
+    /* Init clipboard */
+    clipboard->mimetype[0] = '\0';
+    clipboard->buffer = malloc(size);
+    clipboard->length = 0;
+    clipboard->available = size;
+
+    return clipboard;
+
+}
+
+void guac_common_clipboard_free(guac_common_clipboard* clipboard) {
+    free(clipboard->buffer);
+    free(clipboard);
+}
+
+void guac_common_clipboard_send(guac_common_clipboard* clipboard, guac_client* client) {
+
+    char* current = clipboard->buffer;
+    int remaining = clipboard->length;
+
+    /* Begin stream */
+    guac_stream* stream = guac_client_alloc_stream(client);
+    guac_protocol_send_clipboard(client->socket, stream, clipboard->mimetype);
+
+    guac_client_log(client, GUAC_LOG_DEBUG,
+            "Created stream %i for %s clipboard data.",
+            stream->index, clipboard->mimetype);
+
+    /* Split clipboard into chunks */
+    while (remaining > 0) {
+
+        /* Calculate size of next block */
+        int block_size = GUAC_COMMON_CLIPBOARD_BLOCK_SIZE;
+        if (remaining < block_size)
+            block_size = remaining; 
+
+        /* Send block */
+        guac_protocol_send_blob(client->socket, stream, current, block_size);
+        guac_client_log(client, GUAC_LOG_DEBUG,
+                "Sent %i bytes of clipboard data on stream %i.",
+                block_size, stream->index);
+
+        /* Next block */
+        remaining -= block_size;
+        current += block_size;
+
+    }
+
+    guac_client_log(client, GUAC_LOG_DEBUG,
+            "Clipboard stream %i complete.",
+            stream->index);
+
+    /* End stream */
+    guac_protocol_send_end(client->socket, stream);
+    guac_client_free_stream(client, stream);
+
+}
+
+void guac_common_clipboard_reset(guac_common_clipboard* clipboard, const char* mimetype) {
+    clipboard->length = 0;
+    strncpy(clipboard->mimetype, mimetype, sizeof(clipboard->mimetype)-1);
+}
+
+void guac_common_clipboard_append(guac_common_clipboard* clipboard, const char* data, int length) {
+
+    /* Truncate data to available length */
+    int remaining = clipboard->available - clipboard->length;
+    if (remaining < length)
+        length = remaining;
+
+    /* Append to buffer */
+    memcpy(clipboard->buffer + clipboard->length, data, length);
+
+    /* Update length */
+    clipboard->length += length;
+
+}
+
diff --git a/src/common/guac_clipboard.h b/src/common/guac_clipboard.h
new file mode 100644
index 0000000..cdcded6
--- /dev/null
+++ b/src/common/guac_clipboard.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef __GUAC_CLIPBOARD_H
+#define __GUAC_CLIPBOARD_H
+
+#include "config.h"
+
+#include <guacamole/client.h>
+
+/**
+ * The maximum number of bytes to send in an individual blob when
+ * transmitting the clipboard contents to a connected client.
+ */
+#define GUAC_COMMON_CLIPBOARD_BLOCK_SIZE 4096
+
+/**
+ * Generic clipboard structure.
+ */
+typedef struct guac_common_clipboard {
+
+    /**
+     * The mimetype of the contained clipboard data.
+     */
+    char mimetype[256];
+
+    /**
+     * Arbitrary clipboard data.
+     */
+    char* buffer;
+
+    /**
+     * The number of bytes currently stored in the clipboard buffer.
+     */
+    int length;
+
+    /**
+     * The total number of bytes available in the clipboard buffer.
+     */
+    int available;
+
+} guac_common_clipboard;
+
+/**
+ * Creates a new clipboard having the given initial size.
+ *
+ * @param size The maximum number of bytes to allow within the clipboard.
+ * @return A newly-allocated clipboard.
+ */
+guac_common_clipboard* guac_common_clipboard_alloc(int size);
+
+/**
+ * Frees the given clipboard.
+ *
+ * @param clipboard The clipboard to free.
+ */
+void guac_common_clipboard_free(guac_common_clipboard* clipboard);
+
+/**
+ * Sends the contents of the clipboard along the given client, splitting
+ * the contents as necessary.
+ *
+ * @param clipboard The clipboard whose contents should be sent.
+ * @param client The client to send the clipboard contents on.
+ */
+void guac_common_clipboard_send(guac_common_clipboard* clipboard, guac_client* client);
+
+/**
+ * Clears the clipboard contents and assigns a new mimetype for future data.
+ *
+ * @param clipboard The clipboard to reset.
+ * @param mimetype The mimetype of future data.
+ */
+void guac_common_clipboard_reset(guac_common_clipboard* clipboard, const char* mimetype);
+
+/**
+ * Appends the given data to the current clipboard contents. The data must
+ * match the mimetype chosen for the clipboard data by
+ * guac_common_clipboard_reset().
+ *
+ * @param clipboard The clipboard to append data to.
+ * @param data The data to append.
+ * @param length The number of bytes to append from the data given.
+ */
+void guac_common_clipboard_append(guac_common_clipboard* clipboard, const char* data, int length);
+
+#endif
+
diff --git a/src/common/guac_dot_cursor.c b/src/common/guac_dot_cursor.c
new file mode 100644
index 0000000..cf672a4
--- /dev/null
+++ b/src/common/guac_dot_cursor.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <cairo/cairo.h>
+#include <guacamole/client.h>
+#include <guacamole/layer.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+
+/* Macros for prettying up the embedded image. */
+#define X 0x00,0x00,0x00,0xFF
+#define O 0xFF,0xFF,0xFF,0xFF
+#define _ 0x00,0x00,0x00,0x00
+
+/* Dimensions */
+const int guac_common_dot_cursor_width  = 5;
+const int guac_common_dot_cursor_height = 5;
+
+/* Format */
+const cairo_format_t guac_common_dot_cursor_format = CAIRO_FORMAT_ARGB32;
+const int guac_common_dot_cursor_stride = 20;
+
+/* Embedded pointer graphic */
+unsigned char guac_common_dot_cursor[] = {
+
+        _,O,O,O,_,
+        O,X,X,X,O,
+        O,X,X,X,O,
+        O,X,X,X,O,
+        _,O,O,O,_
+
+};
+
+void guac_common_set_dot_cursor(guac_client* client) {
+
+    guac_socket* socket = client->socket;
+
+    /* Draw to buffer */
+    guac_layer* cursor = guac_client_alloc_buffer(client);
+
+    cairo_surface_t* graphic = cairo_image_surface_create_for_data(
+            guac_common_dot_cursor,
+            guac_common_dot_cursor_format,
+            guac_common_dot_cursor_width,
+            guac_common_dot_cursor_height,
+            guac_common_dot_cursor_stride);
+
+    guac_client_stream_png(client, socket, GUAC_COMP_SRC, cursor,
+            0, 0, graphic);
+    cairo_surface_destroy(graphic);
+
+    /* Set cursor */
+    guac_protocol_send_cursor(socket, 2, 2, cursor,
+            0, 0,
+            guac_common_dot_cursor_width,
+            guac_common_dot_cursor_height);
+
+    /* Free buffer */
+    guac_client_free_buffer(client, cursor);
+
+    guac_client_log(client, GUAC_LOG_DEBUG,
+            "Client cursor image set to generic built-in dot.");
+
+}
+
diff --git a/src/common/guac_dot_cursor.h b/src/common/guac_dot_cursor.h
new file mode 100644
index 0000000..d3ab2b2
--- /dev/null
+++ b/src/common/guac_dot_cursor.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef _GUAC_COMMON_DOT_CURSOR_H
+#define _GUAC_COMMON_DOT_CURSOR_H
+
+#include "config.h"
+
+#include <cairo/cairo.h>
+#include <guacamole/client.h>
+
+/**
+ * Width of the embedded mouse cursor graphic.
+ */
+extern const int guac_common_dot_cursor_width;
+
+/**
+ * Height of the embedded mouse cursor graphic.
+ */
+extern const int guac_common_dot_cursor_height;
+
+/**
+ * Number of bytes in each row of the embedded mouse cursor graphic.
+ */
+extern const int guac_common_dot_cursor_stride;
+
+/**
+ * The Cairo grapic format of the mouse cursor graphic.
+ */
+extern const cairo_format_t guac_common_dot_cursor_format;
+
+/**
+ * Embedded mouse cursor graphic.
+ */
+extern unsigned char guac_common_dot_cursor[];
+
+/**
+ * Set the cursor of the remote display to the embedded cursor graphic.
+ *
+ * @param client The guac_client to send the cursor to.
+ */
+void guac_common_set_dot_cursor(guac_client* client);
+
+#endif
diff --git a/src/common/guac_iconv.c b/src/common/guac_iconv.c
new file mode 100644
index 0000000..3ef377d
--- /dev/null
+++ b/src/common/guac_iconv.c
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+#include "guac_iconv.h"
+
+#include <guacamole/unicode.h>
+#include <stdint.h>
+
+/**
+ * Lookup table for Unicode code points, indexed by CP-1252 codepoint.
+ */
+const static int __GUAC_RDP_CP1252_CODEPOINT[32] = {
+    0x20AC, /* 0x80 */
+    0xFFFD, /* 0x81 */
+    0x201A, /* 0x82 */
+    0x0192, /* 0x83 */
+    0x201E, /* 0x84 */
+    0x2026, /* 0x85 */
+    0x2020, /* 0x86 */
+    0x2021, /* 0x87 */
+    0x02C6, /* 0x88 */
+    0x2030, /* 0x89 */
+    0x0160, /* 0x8A */
+    0x2039, /* 0x8B */
+    0x0152, /* 0x8C */
+    0xFFFD, /* 0x8D */
+    0x017D, /* 0x8E */
+    0xFFFD, /* 0x8F */
+    0xFFFD, /* 0x90 */
+    0x2018, /* 0x91 */
+    0x2019, /* 0x92 */
+    0x201C, /* 0x93 */
+    0x201D, /* 0x94 */
+    0x2022, /* 0x95 */
+    0x2013, /* 0x96 */
+    0x2014, /* 0x97 */
+    0x02DC, /* 0x98 */
+    0x2122, /* 0x99 */
+    0x0161, /* 0x9A */
+    0x203A, /* 0x9B */
+    0x0153, /* 0x9C */
+    0xFFFD, /* 0x9D */
+    0x017E, /* 0x9E */
+    0x0178, /* 0x9F */
+};
+
+int guac_iconv(guac_iconv_read* reader, const char** input, int in_remaining,
+               guac_iconv_write* writer, char** output, int out_remaining) {
+
+    while (in_remaining > 0 && out_remaining > 0) {
+
+        int value;
+        const char* read_start;
+        char* write_start;
+
+        /* Read character */
+        read_start = *input;
+        value = reader(input, in_remaining);
+        in_remaining -= *input - read_start;
+
+        /* Write character */
+        write_start = *output;
+        writer(output, out_remaining, value);
+        out_remaining -= *output - write_start;
+
+        /* Stop if null terminator reached */
+        if (value == 0)
+            return 1;
+
+    }
+
+    /* Null terminator not reached */
+    return 0;
+
+}
+
+int GUAC_READ_UTF8(const char** input, int remaining) {
+
+    int value;
+
+    *input += guac_utf8_read(*input, remaining, &value);
+    return value;
+
+}
+
+int GUAC_READ_UTF16(const char** input, int remaining) {
+
+    int value;
+
+    /* Bail if not enough data */
+    if (remaining < 2)
+        return 0;
+
+    /* Read two bytes as integer */
+    value = *((uint16_t*) *input);
+    *input += 2;
+
+    return value;
+
+}
+
+int GUAC_READ_CP1252(const char** input, int remaining) {
+
+    int value = *((unsigned char*) *input);
+
+    /* Replace value with exception if not identical to ISO-8859-1 */
+    if (value >= 0x80 && value <= 0x9F)
+        value = __GUAC_RDP_CP1252_CODEPOINT[value - 0x80];
+
+    (*input)++;
+    return value;
+
+}
+
+int GUAC_READ_ISO8859_1(const char** input, int remaining) {
+
+    int value = *((unsigned char*) *input);
+
+    (*input)++;
+    return value;
+
+}
+
+void GUAC_WRITE_UTF8(char** output, int remaining, int value) {
+    *output += guac_utf8_write(value, *output, remaining);
+}
+
+void GUAC_WRITE_UTF16(char** output, int remaining, int value) {
+
+    /* Bail if not enough data */
+    if (remaining < 2)
+        return;
+
+    /* Write two bytes as integer */
+    *((uint16_t*) *output) = value;
+    *output += 2;
+
+}
+
+void GUAC_WRITE_CP1252(char** output, int remaining, int value) {
+
+    /* If not in ISO-8859-1 part of CP1252, check lookup table */
+    if ((value >= 0x80 && value <= 0x9F) || value > 0xFF) {
+
+        int i;
+        int replacement_value = '?';
+        const int* codepoint = __GUAC_RDP_CP1252_CODEPOINT;
+
+        /* Search lookup table for value */
+        for (i=0x80; i<=0x9F; i++, codepoint++) {
+            if (*codepoint == value) {
+                replacement_value = i;
+                break;
+            }
+        }
+
+        /* Replace value with discovered value (or question mark) */
+        value = replacement_value;
+
+    }
+
+    *((unsigned char*) *output) = (unsigned char) value;
+    (*output)++;
+}
+
+void GUAC_WRITE_ISO8859_1(char** output, int remaining, int value) {
+
+    /* Translate to question mark if out of range */
+    if (value > 0xFF)
+        value = '?';
+
+    *((unsigned char*) *output) = (unsigned char) value;
+    (*output)++;
+}
+
diff --git a/src/common/guac_iconv.h b/src/common/guac_iconv.h
new file mode 100644
index 0000000..d6f758b
--- /dev/null
+++ b/src/common/guac_iconv.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef __GUAC_COMMON_ICONV_H
+#define __GUAC_COMMON_ICONV_H
+
+#include "config.h"
+
+/**
+ * Function which reads a character from the given string data, returning
+ * the Unicode codepoint read, updating the string pointer to point to the
+ * byte immediately after the character read.
+ */
+typedef int guac_iconv_read(const char** input, int remaining);
+
+/**
+ * Function writes the character having the given Unicode codepoint value to
+ * the given string data, updating the string pointer to point to the byte
+ * immediately after the character written.
+ */
+typedef void guac_iconv_write(char** output, int remaining, int value);
+
+/**
+ * Converts characters within a given string from one encoding to another,
+ * as defined by the reader/writer functions specified. The input and output
+ * string pointers will be updated based on the number of bytes read or
+ * written.
+ *
+ * @param reader The reader function to use when reading the input string.
+ * @param input Pointer to the beginning of the input string.
+ * @param in_remaining The number of bytes remaining after the pointer to the
+ *                     input string.
+ * @param writer The writer function to use when writing the output string.
+ * @param output Pointer to the beginning of the output string.
+ * @param out_remaining The number of bytes remaining after the pointer to the
+ *                      output string.
+ * @return Non-zero if the NULL terminator of the input string was read and
+ *         copied into the destination string, zero otherwise.
+ */
+int guac_iconv(guac_iconv_read* reader, const char** input, int in_remaining,
+               guac_iconv_write* writer, char** output, int out_remaining);
+
+/**
+ * Read function for UTF8.
+ */
+guac_iconv_read GUAC_READ_UTF8;
+
+/**
+ * Read function for UTF16.
+ */
+guac_iconv_read GUAC_READ_UTF16;
+
+/**
+ * Read function for CP-1252.
+ */
+guac_iconv_read GUAC_READ_CP1252;
+
+/**
+ * Read function for ISO-8859-1
+ */
+guac_iconv_read GUAC_READ_ISO8859_1;
+
+/**
+ * Write function for UTF8.
+ */
+guac_iconv_write GUAC_WRITE_UTF8;
+
+/**
+ * Write function for UTF16.
+ */
+guac_iconv_write GUAC_WRITE_UTF16;
+
+/**
+ * Write function for CP-1252.
+ */
+guac_iconv_write GUAC_WRITE_CP1252;
+
+/**
+ * Write function for ISO-8859-1
+ */
+guac_iconv_write GUAC_WRITE_ISO8859_1;
+
+#endif
+
diff --git a/src/common/guac_io.c b/src/common/guac_io.c
new file mode 100644
index 0000000..b4c87d6
--- /dev/null
+++ b/src/common/guac_io.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+#include "guac_io.h"
+
+#include <unistd.h>
+
+int guac_common_write(int fd, void* buffer, int length) {
+    
+    unsigned char* bytes = (unsigned char*) buffer;
+
+    while (length > 0) {
+
+        /* Attempt write */
+        int bytes_written = write(fd, bytes, length);
+        if (bytes_written < 0)
+            return bytes_written;
+
+        /* Update buffer */
+        length -= bytes_written;
+        bytes += bytes_written;
+
+    }
+
+    /* Success */
+    return length;
+
+}
+
+int guac_common_read(int fd, void* buffer, int length) {
+
+    unsigned char* bytes = (unsigned char*) buffer;
+
+    while (length > 0) {
+
+        /* Attempt read */
+        int bytes_read = read(fd, bytes, length);
+        if (bytes_read < 0)
+            return bytes_read;
+
+        /* Update buffer */
+        length -= bytes_read;
+        bytes += bytes_read;
+
+    }
+
+    /* Success */
+    return length;
+
+}
+
diff --git a/src/common/guac_io.h b/src/common/guac_io.h
new file mode 100644
index 0000000..fc8269e
--- /dev/null
+++ b/src/common/guac_io.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef __GUAC_COMMON_IO_H
+#define __GUAC_COMMON_IO_H
+
+#include "config.h"
+
+/**
+ * Writes absolutely all bytes from within the given buffer, returning an error
+ * only if the required writes fail.
+ *
+ * @param fd The file descriptor to write to.
+ * @param buffer The buffer containing the data to write.
+ * @param length The number of bytes to write.
+ * @return The number of bytes written, or a value less than zero if an error
+ *         occurs.
+ */
+int guac_common_write(int fd, void* buffer, int length);
+
+/**
+ * Reads enough bytes to fill the given buffer, returning an error only if the
+ * required reads fail.
+ *
+ * @param fd The file descriptor to read from.
+ * @param buffer The buffer to read data into.
+ * @param length The number of bytes to read.
+ * @return The number of bytes read, or a value less than zero if an error
+ *         occurs.
+ */
+int guac_common_read(int fd, void* buffer, int length);
+
+#endif
+
diff --git a/src/common/guac_json.c b/src/common/guac_json.c
new file mode 100644
index 0000000..4359d89
--- /dev/null
+++ b/src/common/guac_json.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "guac_json.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <guacamole/client.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+#include <guacamole/stream.h>
+
+void guac_common_json_flush(guac_client* client, guac_stream* stream,
+        guac_common_json_state* json_state) {
+
+    /* If JSON buffer is non-empty, write contents to blob and reset */
+    if (json_state->size > 0) {
+        guac_protocol_send_blob(client->socket, stream,
+                json_state->buffer, json_state->size);
+
+        /* Reset JSON buffer size */
+        json_state->size = 0;
+
+    }
+
+}
+
+int guac_common_json_write(guac_client* client, guac_stream* stream,
+        guac_common_json_state* json_state, const char* buffer, int length) {
+
+    int blob_written = 0;
+
+    /*
+     * Append to and flush the JSON buffer as necessary to write the given
+     * data
+     */
+    while (length > 0) {
+
+        /* Ensure provided data does not exceed size of buffer */
+        int blob_length = length;
+        if (blob_length > sizeof(json_state->buffer))
+            blob_length = sizeof(json_state->buffer);
+
+        /* Flush if more room is needed */
+        if (json_state->size + blob_length > sizeof(json_state->buffer)) {
+            guac_common_json_flush(client, stream, json_state);
+            blob_written = 1;
+        }
+
+        /* Append data to JSON buffer */
+        memcpy(json_state->buffer + json_state->size,
+                buffer, blob_length);
+
+        json_state->size += blob_length;
+
+        /* Advance to next blob of data */
+        buffer += blob_length;
+        length -= blob_length;
+
+    }
+
+    return blob_written;
+
+}
+
+int guac_common_json_write_string(guac_client* client,
+        guac_stream* stream, guac_common_json_state* json_state,
+        const char* str) {
+
+    int blob_written = 0;
+
+    /* Write starting quote */
+    blob_written |= guac_common_json_write(client, stream,
+            json_state, "\"", 1);
+
+    /* Write given string, escaping as necessary */
+    const char* current = str;
+    for (; *current != '\0'; current++) {
+
+        /* Escape all quotes */
+        if (*current == '"') {
+
+            /* Write any string content up to current character */
+            if (current != str)
+                blob_written |= guac_common_json_write(client, stream,
+                        json_state, str, current - str);
+
+            /* Escape the quote that was just read */
+            blob_written |= guac_common_json_write(client, stream,
+                    json_state, "\\", 1);
+
+            /* Reset string */
+            str = current;
+
+        }
+
+    }
+
+    /* Write any remaining string content */
+    if (current != str)
+        blob_written |= guac_common_json_write(client, stream,
+                json_state, str, current - str);
+
+    /* Write ending quote */
+    blob_written |= guac_common_json_write(client, stream,
+            json_state, "\"", 1);
+
+    return blob_written;
+
+}
+
+int guac_common_json_write_property(guac_client* client, guac_stream* stream,
+        guac_common_json_state* json_state, const char* name,
+        const char* value) {
+
+    int blob_written = 0;
+
+    /* Write leading comma if not first property */
+    if (json_state->properties_written != 0)
+        blob_written |= guac_common_json_write(client, stream,
+                json_state, ",", 1);
+
+    /* Write property name */
+    blob_written |= guac_common_json_write_string(client, stream,
+            json_state, name);
+
+    /* Separate name from value with colon */
+    blob_written |= guac_common_json_write(client, stream,
+            json_state, ":", 1);
+
+    /* Write property value */
+    blob_written |= guac_common_json_write_string(client, stream,
+            json_state, value);
+
+    json_state->properties_written++;
+
+    return blob_written;
+
+}
+
+void guac_common_json_begin_object(guac_client* client, guac_stream* stream,
+        guac_common_json_state* json_state) {
+
+    /* Init JSON state */
+    json_state->size = 0;
+    json_state->properties_written = 0;
+
+    /* Write leading brace - no blob can possibly be written by this */
+    assert(!guac_common_json_write(client, stream, json_state, "{", 1));
+
+}
+
+int guac_common_json_end_object(guac_client* client, guac_stream* stream,
+        guac_common_json_state* json_state) {
+
+    /* Write final brace of JSON object */
+    return guac_common_json_write(client, stream, json_state, "}", 1);
+
+}
+
diff --git a/src/common/guac_json.h b/src/common/guac_json.h
new file mode 100644
index 0000000..6e0d82d
--- /dev/null
+++ b/src/common/guac_json.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GUAC_COMMON_JSON_H
+#define GUAC_COMMON_JSON_H
+
+#include "config.h"
+
+#include <guacamole/client.h>
+#include <guacamole/stream.h>
+
+/**
+ * The current streaming state of an arbitrary JSON object, consisting of
+ * any number of property name/value pairs.
+ */
+typedef struct guac_common_json_state {
+
+    /**
+     * Buffer of partial JSON data. The individual blobs which make up the JSON
+     * body of the object being sent over the Guacamole protocol will be
+     * built here.
+     */
+    char buffer[4096];
+
+    /**
+     * The number of bytes currently used within the JSON buffer.
+     */
+    int size;
+
+    /**
+     * The number of property name/value pairs written to the JSON object thus
+     * far.
+     */
+    int properties_written;
+
+} guac_common_json_state;
+
+/**
+ * Given a stream, the client to which it belongs, and the current stream state
+ * of a JSON object, flushes the contents of the JSON buffer to a blob
+ * instruction. Note that this will flush the JSON buffer only, and will not
+ * necessarily flush the underlying guac_socket of the client.
+ *
+ * @param client
+ *     The client to which the data will be flushed.
+ *
+ * @param stream
+ *     The stream through which the flushed data should be sent as a blob.
+ *
+ * @param json_state
+ *     The state object whose buffer should be flushed.
+ */
+void guac_common_json_flush(guac_client* client, guac_stream* stream,
+        guac_common_json_state* json_state);
+
+/**
+ * Given a stream, the client to which it belongs, and the current stream state
+ * of a JSON object, writes the contents of the given buffer to the JSON buffer
+ * of the stream state, flushing as necessary.
+ *
+ * @param client
+ *     The client to which the data will be flushed as necessary.
+ *
+ * @param stream
+ *     The stream through which the flushed data should be sent as a blob, if
+ *     data must be flushed at all.
+ *
+ * @param json_state
+ *     The state object containing the JSON buffer to which the given buffer
+ *     should be written.
+ *
+ * @param buffer
+ *     The buffer to write.
+ *
+ * @param length
+ *     The number of bytes in the buffer.
+ *
+ * @return
+ *     Non-zero if at least one blob was written, zero otherwise.
+ */
+int guac_common_json_write(guac_client* client, guac_stream* stream,
+        guac_common_json_state* json_state, const char* buffer, int length);
+
+/**
+ * Given a stream, the client to which it belongs, and the current stream state
+ * of a JSON object state, writes the given string as a proper JSON string,
+ * including starting and ending quotes. The contents of the string will be
+ * escaped as necessary.
+ *
+ * @param client
+ *     The client to which the data will be flushed as necessary.
+ *
+ * @param stream
+ *     The stream through which the flushed data should be sent as a blob, if
+ *     data must be flushed at all.
+ *
+ * @param json_state
+ *     The state object containing the JSON buffer to which the given string
+ *     should be written as a JSON name/value pair.
+ *
+ * @param str
+ *     The string to write.
+ *
+ * @return
+ *     Non-zero if at least one blob was written, zero otherwise.
+ */
+int guac_common_json_write_string(guac_client* client,
+        guac_stream* stream, guac_common_json_state* json_state,
+        const char* str);
+
+/**
+ * Given a stream, the client to which it belongs, and the current stream state
+ * of a JSON object, writes the given JSON property name/value pair. The
+ * name and value will be written as proper JSON strings separated by a colon.
+ *
+ * @param client
+ *     The client to which the data will be flushed as necessary.
+ *
+ * @param stream
+ *     The stream through which the flushed data should be sent as a blob, if
+ *     data must be flushed at all.
+ *
+ * @param json_state
+ *     The state object containing the JSON buffer to which the given strings
+ *     should be written as a JSON name/value pair.
+ *
+ * @param name
+ *     The name of the property to write.
+ *
+ * @param value
+ *     The value of the property to write.
+ *
+ * @return
+ *     Non-zero if at least one blob was written, zero otherwise.
+ */
+int guac_common_json_write_property(guac_client* client, guac_stream* stream,
+        guac_common_json_state* json_state, const char* name,
+        const char* value);
+
+/**
+ * Given a stream, the client to which it belongs, and the current stream state
+ * of a JSON object, initializes the state for writing a new JSON object. Note
+ * that although the client and stream must be provided, no instruction or
+ * blobs will be written due to any call to this function.
+ *
+ * @param client
+ *     The client associated with the given stream.
+ *
+ * @param stream
+ *     The stream associated with the JSON object being written.
+ *
+ * @param json_state
+ *     The state object to initialize.
+ */
+void guac_common_json_begin_object(guac_client* client, guac_stream* stream,
+        guac_common_json_state* json_state);
+
+/**
+ * Given a stream, the client to which it belongs, and the current stream state
+ * of a JSON object, completes writing that JSON object by writing the final
+ * terminating brace. This function must only be called following a
+ * corresponding call to guac_common_json_begin_object().
+ *
+ * @param client
+ *     The client associated with the given stream.
+ *
+ * @param stream
+ *     The stream associated with the JSON object being written.
+ *
+ * @param json_state
+ *     The state object whose in-progress JSON object should be terminated.
+ *
+ * @return
+ *     Non-zero if at least one blob was written, zero otherwise.
+ */
+int guac_common_json_end_object(guac_client* client, guac_stream* stream,
+        guac_common_json_state* json_state);
+
+#endif
+
diff --git a/src/common/guac_list.c b/src/common/guac_list.c
new file mode 100644
index 0000000..911b991
--- /dev/null
+++ b/src/common/guac_list.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+#include "guac_list.h"
+
+#include <stdlib.h>
+#include <pthread.h>
+
+guac_common_list* guac_common_list_alloc() {
+
+    guac_common_list* list = malloc(sizeof(guac_common_list));
+
+    pthread_mutex_init(&list->_lock, NULL);
+    list->head = NULL;
+
+    return list;
+
+}
+
+void guac_common_list_free(guac_common_list* list) {
+    free(list);
+}
+
+guac_common_list_element* guac_common_list_add(guac_common_list* list,
+        void* data) {
+
+    /* Allocate element, initialize as new head */
+    guac_common_list_element* element =
+        malloc(sizeof(guac_common_list_element));
+    element->data = data;
+    element->next = list->head;
+    element->_ptr = &(list->head);
+
+    /* If head already existed, point it at this element */
+    if (list->head != NULL)
+        list->head->_ptr = &(element->next);
+
+    /* Set as new head */
+    list->head = element;
+    return element;
+
+}
+
+void guac_common_list_remove(guac_common_list* list,
+        guac_common_list_element* element) {
+
+    /* Point previous (or head) to next */
+    *(element->_ptr) = element->next;
+
+    if (element->next != NULL)
+        element->next->_ptr = element->_ptr;
+
+    free(element);
+
+}
+
+void guac_common_list_lock(guac_common_list* list) {
+    pthread_mutex_lock(&list->_lock);
+}
+
+void guac_common_list_unlock(guac_common_list* list) {
+    pthread_mutex_unlock(&list->_lock);
+}
+
diff --git a/src/common/guac_list.h b/src/common/guac_list.h
new file mode 100644
index 0000000..5075a8b
--- /dev/null
+++ b/src/common/guac_list.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef __GUAC_LIST_H
+#define __GUAC_LIST_H
+
+#include "config.h"
+
+#include <pthread.h>
+
+/**
+ * Generic linked list element.
+ */
+typedef struct guac_common_list_element guac_common_list_element;
+
+struct guac_common_list_element {
+
+    /**
+     * The next element in the list, or NULL if none.
+     */
+    guac_common_list_element* next;
+
+    /**
+     * Generic data.
+     */
+    void* data;
+
+    /**
+     * The pointer which points to this element, whether another element's
+     * next pointer, or the entire list's head pointer.
+     */
+    guac_common_list_element** _ptr;
+
+};
+
+/**
+ * Generic linked list.
+ */
+typedef struct guac_common_list {
+
+    /**
+     * The first element in the list.
+     */
+    guac_common_list_element* head;
+
+    /**
+     * Mutex which is locked when exclusive access to the list is required.
+     * Possession of the lock is not enforced outside the
+     * guac_common_list_lock() function.
+     */
+    pthread_mutex_t _lock;
+
+} guac_common_list;
+
+/**
+ * Creates a new list.
+ *
+ * @return A newly-allocated list.
+ */
+guac_common_list* guac_common_list_alloc();
+
+/**
+ * Frees the given list.
+ *
+ * @param list The list to free.
+ */
+void guac_common_list_free(guac_common_list* list);
+
+/**
+ * Adds the given data to the list as a new element, returning the created
+ * element.
+ *
+ * @param list The list to add an element to.
+ * @param data The data to associate with the newly-created element.
+ * @param The newly-created element.
+ */
+guac_common_list_element* guac_common_list_add(guac_common_list* list,
+        void* data);
+
+/**
+ * Removes the given element from the list.
+ *
+ * @param list The list to remove the element from.
+ * @param element The element to remove.
+ */
+void guac_common_list_remove(guac_common_list* list,
+        guac_common_list_element* element);
+
+/**
+ * Acquires exclusive access to the list. No list functions implicitly lock or
+ * unlock the list, so any list access which must be threadsafe must use
+ * guac_common_list_lock() and guac_common_list_unlock() manually.
+ *
+ * @param list The list to acquire exclusive access to.
+ */
+void guac_common_list_lock(guac_common_list* list);
+
+/**
+ * Releases exclusive access to the list.
+ *
+ * @param list The list to release from exclusive access.
+ */
+void guac_common_list_unlock(guac_common_list* list);
+
+#endif
+
diff --git a/src/common/guac_pointer_cursor.c b/src/common/guac_pointer_cursor.c
new file mode 100644
index 0000000..65fd43f
--- /dev/null
+++ b/src/common/guac_pointer_cursor.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <cairo/cairo.h>
+#include <guacamole/client.h>
+#include <guacamole/layer.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+
+/* Macros for prettying up the embedded image. */
+#define X 0x00,0x00,0x00,0xFF
+#define O 0xFF,0xFF,0xFF,0xFF
+#define _ 0x00,0x00,0x00,0x00
+
+/* Dimensions */
+const int guac_common_pointer_cursor_width  = 11;
+const int guac_common_pointer_cursor_height = 16;
+
+/* Format */
+const cairo_format_t guac_common_pointer_cursor_format = CAIRO_FORMAT_ARGB32;
+const int guac_common_pointer_cursor_stride = 44;
+
+/* Embedded pointer graphic */
+unsigned char guac_common_pointer_cursor[] = {
+
+        O,_,_,_,_,_,_,_,_,_,_,
+        O,O,_,_,_,_,_,_,_,_,_,
+        O,X,O,_,_,_,_,_,_,_,_,
+        O,X,X,O,_,_,_,_,_,_,_,
+        O,X,X,X,O,_,_,_,_,_,_,
+        O,X,X,X,X,O,_,_,_,_,_,
+        O,X,X,X,X,X,O,_,_,_,_,
+        O,X,X,X,X,X,X,O,_,_,_,
+        O,X,X,X,X,X,X,X,O,_,_,
+        O,X,X,X,X,X,X,X,X,O,_,
+        O,X,X,X,X,X,O,O,O,O,O,
+        O,X,X,O,X,X,O,_,_,_,_,
+        O,X,O,_,O,X,X,O,_,_,_,
+        O,O,_,_,O,X,X,O,_,_,_,
+        O,_,_,_,_,O,X,X,O,_,_,
+        _,_,_,_,_,O,O,O,O,_,_
+
+};
+
+void guac_common_set_pointer_cursor(guac_client* client) {
+
+    guac_socket* socket = client->socket;
+
+    /* Draw to buffer */
+    guac_layer* cursor = guac_client_alloc_buffer(client);
+
+    cairo_surface_t* graphic = cairo_image_surface_create_for_data(
+            guac_common_pointer_cursor,
+            guac_common_pointer_cursor_format,
+            guac_common_pointer_cursor_width,
+            guac_common_pointer_cursor_height,
+            guac_common_pointer_cursor_stride);
+
+    guac_client_stream_png(client, socket, GUAC_COMP_SRC, cursor,
+            0, 0, graphic);
+    cairo_surface_destroy(graphic);
+
+    /* Set cursor */
+    guac_protocol_send_cursor(socket, 0, 0, cursor,
+            0, 0,
+            guac_common_pointer_cursor_width,
+            guac_common_pointer_cursor_height);
+
+    /* Free buffer */
+    guac_client_free_buffer(client, cursor);
+
+    guac_client_log(client, GUAC_LOG_DEBUG,
+            "Client cursor image set to generic built-in pointer.");
+
+}
+
diff --git a/src/common/guac_pointer_cursor.h b/src/common/guac_pointer_cursor.h
new file mode 100644
index 0000000..5c3dcd0
--- /dev/null
+++ b/src/common/guac_pointer_cursor.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef _GUAC_COMMON_POINTER_CURSOR_H
+#define _GUAC_COMMON_POINTER_CURSOR_H
+
+#include "config.h"
+
+#include <cairo/cairo.h>
+#include <guacamole/client.h>
+
+/**
+ * Width of the embedded mouse cursor graphic.
+ */
+extern const int guac_common_pointer_cursor_width;
+
+/**
+ * Height of the embedded mouse cursor graphic.
+ */
+extern const int guac_common_pointer_cursor_height;
+
+/**
+ * Number of bytes in each row of the embedded mouse cursor graphic.
+ */
+extern const int guac_common_pointer_cursor_stride;
+
+/**
+ * The Cairo grapic format of the mouse cursor graphic.
+ */
+extern const cairo_format_t guac_common_pointer_cursor_format;
+
+/**
+ * Embedded mouse cursor graphic.
+ */
+extern unsigned char guac_common_pointer_cursor[];
+
+/**
+ * Set the cursor of the remote display to the embedded cursor graphic.
+ *
+ * @param client The guac_client to send the cursor to.
+ */
+void guac_common_set_pointer_cursor(guac_client* client);
+
+#endif
diff --git a/src/common/guac_rect.c b/src/common/guac_rect.c
new file mode 100644
index 0000000..22c3af2
--- /dev/null
+++ b/src/common/guac_rect.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+#include "guac_rect.h"
+
+void guac_common_rect_init(guac_common_rect* rect, int x, int y, int width, int height) {
+    rect->x      = x;
+    rect->y      = y;
+    rect->width  = width;
+    rect->height = height;
+}
+
+void guac_common_rect_extend(guac_common_rect* rect, const guac_common_rect* min) {
+
+    /* Calculate extents of existing dirty rect */
+    int left   = rect->x;
+    int top    = rect->y;
+    int right  = left + rect->width;
+    int bottom = top  + rect->height;
+
+    /* Calculate missing extents of given new rect */
+    int min_left   = min->x;
+    int min_top    = min->y;
+    int min_right  = min_left + min->width;
+    int min_bottom = min_top  + min->height;
+
+    /* Update minimums */
+    if (min_left   < left)   left   = min_left;
+    if (min_top    < top)    top    = min_top;
+    if (min_right  > right)  right  = min_right;
+    if (min_bottom > bottom) bottom = min_bottom;
+
+    /* Commit rect */
+    guac_common_rect_init(rect, left, top, right - left, bottom - top);
+
+}
+
+void guac_common_rect_constrain(guac_common_rect* rect, const guac_common_rect* max) {
+
+    /* Calculate extents of existing dirty rect */
+    int left   = rect->x;
+    int top    = rect->y;
+    int right  = left + rect->width;
+    int bottom = top  + rect->height;
+
+    /* Calculate missing extents of given new rect */
+    int max_left   = max->x;
+    int max_top    = max->y;
+    int max_right  = max_left + max->width;
+    int max_bottom = max_top  + max->height;
+
+    /* Update maximums */
+    if (max_left   > left)   left   = max_left;
+    if (max_top    > top)    top    = max_top;
+    if (max_right  < right)  right  = max_right;
+    if (max_bottom < bottom) bottom = max_bottom;
+
+    /* Commit rect */
+    guac_common_rect_init(rect, left, top, right - left, bottom - top);
+
+}
+
+int guac_common_rect_expand_to_grid(int cell_size, guac_common_rect* rect,
+                                    const guac_common_rect* max_rect) {
+
+    /* Invalid cell_size received */
+    if (cell_size <= 0)
+        return -1;
+
+    /* Nothing to do */
+    if (cell_size == 1)
+        return 0;
+
+    /* Calculate how much the rectangle must be adjusted to fit within the
+     * given cell size. */
+    int dw = cell_size - rect->width % cell_size;
+    int dh = cell_size - rect->height % cell_size;
+
+    int dx = dw / 2;
+    int dy = dh / 2;
+
+    /* Set initial extents of adjusted rectangle. */
+    int top = rect->y - dy;
+    int left = rect->x - dx;
+    int bottom = top + rect->height + dh;
+    int right = left + rect->width + dw;
+
+    /* The max rectangle */
+    int max_left   = max_rect->x;
+    int max_top    = max_rect->y;
+    int max_right  = max_left + max_rect->width;
+    int max_bottom = max_top  + max_rect->height;
+
+    /* If the adjusted rectangle has sides beyond the max rectangle, or is larger
+     * in any direction; shift or adjust the rectangle while trying to fit in
+     * the grid */
+
+    /* Adjust left/right */
+    if (right > max_right) {
+
+        /* shift to left */
+        dw = right - max_right;
+        right -= dw;
+        left -= dw;
+
+        /* clamp left if too far */
+        if (left < max_left) {
+            left = max_left;
+        }
+    }
+    else if (left < max_left) {
+
+        /* shift to right */
+        dw = max_left - left;
+        left += dw;
+        right += dw;
+
+        /* clamp right if too far */
+        if (right > max_right) {
+            right = max_right;
+        }
+    }
+
+    /* Adjust top/bottom */
+    if (bottom > max_bottom) {
+
+        /* shift up */
+        dh = bottom - max_bottom;
+        bottom -= dh;
+        top -= dh;
+
+        /* clamp top if too far */
+        if (top < max_top) {
+            top = max_top;
+        }
+    }
+    else if (top < max_top) {
+
+        /* shift down */
+        dh = max_top - top;
+        top += dh;
+        bottom += dh;
+
+        /* clamp bottom if too far */
+        if (bottom > max_bottom) {
+            bottom = max_bottom;
+        }
+    }
+
+    /* Commit rect */
+    guac_common_rect_init(rect, left, top, right - left, bottom - top);
+
+    return 0;
+
+}
+
+int guac_common_rect_intersects(const guac_common_rect* rect,
+                                const guac_common_rect* other) {
+
+    /* Empty (no intersection) */
+    if (other->x + other->width < rect->x || rect->x + rect->width < other->x ||
+        other->y + other->height < rect->y || rect->y + rect->height < other->y) {
+        return 0;
+    }
+    /* Complete */
+    else if (other->x <= rect->x && (other->x + other->width) >= (rect->x + rect->width) &&
+        other->y <= rect->y && (other->y + other->height) >= (rect->y + rect->height)) {
+        return 2;
+    }
+    /* Partial intersection */
+    return 1;
+
+}
+
+int guac_common_rect_clip_and_split(guac_common_rect* rect,
+        const guac_common_rect* hole, guac_common_rect* split_rect) {
+
+    /* Only continue if the rectangles intersects */
+    if (!guac_common_rect_intersects(rect, hole))
+        return 0;
+
+    int top, left, bottom, right;
+
+    /* Clip and split top */
+    if (rect->y < hole->y) {
+        top = rect->y;
+        left = rect->x;
+        bottom = hole->y;
+        right = rect->x + rect->width;
+        guac_common_rect_init(split_rect, left, top, right - left, bottom - top);
+
+        /* Re-initialize original rect */
+        top = hole->y;
+        bottom = rect->y + rect->height;
+        guac_common_rect_init(rect, left, top, right - left, bottom - top);
+
+        return 1;
+    }
+
+    /* Clip and split left */
+    else if (rect->x < hole->x) {
+        top = rect->y;
+        left = rect->x;
+        bottom = rect->y + rect->height;
+        right = hole->x;
+        guac_common_rect_init(split_rect, left, top, right - left, bottom - top);
+
+        /* Re-initialize original rect */
+        left = hole->x;
+        right = rect->x + rect->width;
+        guac_common_rect_init(rect, left, top, right - left, bottom - top);
+
+        return 1;
+    }
+
+    /* Clip and split bottom */
+    else if (rect->y + rect->height > hole->y + hole->height) {
+        top = hole->y + hole->height;
+        left = rect->x;
+        bottom = rect->y + rect->height;
+        right = rect->x + rect->width;
+        guac_common_rect_init(split_rect, left, top, right - left, bottom - top);
+
+        /* Re-initialize original rect */
+        top = rect->y;
+        bottom = hole->y + hole->height;
+        guac_common_rect_init(rect, left, top, right - left, bottom - top);
+
+        return 1;
+    }
+
+    /* Clip and split right */
+    else if (rect->x + rect->width > hole->x + hole->width) {
+        top = rect->y;
+        left = hole->x + hole->width;
+        bottom = rect->y + rect->height;
+        right = rect->x + rect->width;
+        guac_common_rect_init(split_rect, left, top, right - left, bottom - top);
+
+        /* Re-initialize original rect */
+        left = rect->x;
+        right = hole->x + hole->width;
+        guac_common_rect_init(rect, left, top, right - left, bottom - top);
+
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/src/common/guac_rect.h b/src/common/guac_rect.h
new file mode 100644
index 0000000..2bb7084
--- /dev/null
+++ b/src/common/guac_rect.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef __GUAC_COMMON_RECT_H
+#define __GUAC_COMMON_RECT_H
+
+#include "config.h"
+
+/**
+ * Simple representation of a rectangle, having a defined corner and dimensions.
+ */
+typedef struct guac_common_rect {
+
+    /**
+     * The X coordinate of the upper-left corner of this rectangle.
+     */
+    int x;
+
+    /**
+     * The Y coordinate of the upper-left corner of this rectangle.
+     */
+    int y;
+
+    /**
+     * The width of this rectangle.
+     */
+    int width;
+
+    /**
+     * The height of this rectangle.
+     */
+    int height;
+
+} guac_common_rect;
+
+/**
+ * Initialize the given rect with the given coordinates and dimensions.
+ *
+ * @param rect The rect to initialize.
+ * @param x The X coordinate of the upper-left corner of the rect.
+ * @param y The Y coordinate of the upper-left corner of the rect.
+ * @param width The width of the rect.
+ * @param height The height of the rect.
+ */
+void guac_common_rect_init(guac_common_rect* rect, int x, int y, int width, int height);
+
+/**
+ * Expand the rectangle to fit an NxN grid.
+ *
+ * The rectangle will be shifted to the left and up, expanded and adjusted to 
+ * fit within the max bounding rect.
+ *
+ * @param cell_size
+ *     The (NxN) grid cell size.
+ *
+ * @param rect
+ *     The rectangle to adjust.
+ *
+ * @param max_rect
+ *     The bounding area in which the given rect can exist.
+ *
+ * @return
+ *     Zero on success, non-zero on error.
+ */
+int guac_common_rect_expand_to_grid(int cell_size, guac_common_rect* rect,
+                                    const guac_common_rect* max_rect);
+
+/**
+ * Extend the given rect such that it contains at least the specified minimum
+ * rect.
+ *
+ * @param rect The rect to extend.
+ * @param min The minimum area which must be contained within the given rect.
+ */
+void guac_common_rect_extend(guac_common_rect* rect, const guac_common_rect* min);
+
+/**
+ * Collapse the given rect such that it exists only within the given maximum
+ * rect.
+ *
+ * @param rect The rect to extend.
+ * @param max The maximum area in which the given rect can exist.
+ */
+void guac_common_rect_constrain(guac_common_rect* rect, const guac_common_rect* max);
+
+/**
+ * Check whether a rectangle intersects another.
+ *
+ * @param rect
+ *     Rectangle to check for intersection.
+ *
+ * @param other
+ *     The other rectangle.
+ *
+ * @return
+ *     Zero if no intersection, 1 if partial intersection,
+ *     2 if first rect is completely inside the other.
+ */
+int guac_common_rect_intersects(const guac_common_rect* rect,
+                                const guac_common_rect* other);
+
+/**
+ * Clip and split a rectangle into rectangles which are not covered by the
+ * hole rectangle.
+ *
+ * This function will clip and split single edges when executed and must be
+ * invoked until it returns zero. The edges are handled counter-clockwise
+ * starting at the top edge.
+ *
+ * @param rect
+ *     The rectangle to be split. This rectangle will be clipped by the
+ *     split_rect.
+ *
+ * @param hole
+ *     The rectangle which represents the hole.
+ *
+ * @param split_rect
+ *     Resulting split rectangle.
+ *
+ * @return
+ *     Zero when no splits were done, non-zero when the rectangle was split.
+ */
+int guac_common_rect_clip_and_split(guac_common_rect* rect,
+        const guac_common_rect* hole, guac_common_rect* split_rect);
+
+#endif
+
diff --git a/src/common/guac_string.c b/src/common/guac_string.c
new file mode 100644
index 0000000..fa92b24
--- /dev/null
+++ b/src/common/guac_string.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "guac_string.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+int guac_count_occurrences(const char* string, char c) {
+
+    int count = 0;
+
+    while (*string != 0) {
+
+        /* Count each occurrence */
+        if (*string == c)
+            count++;
+
+        /* Next character */
+        string++;
+
+    }
+
+    return count;
+
+}
+
+char** guac_split(const char* string, char delim) {
+
+    int i = 0;
+
+    int token_count = guac_count_occurrences(string, delim) + 1;
+    const char* token_start = string;
+
+    /* Allocate space for tokens */
+    char** tokens = malloc(sizeof(char*) * (token_count+1));
+
+    do {
+
+        int length;
+        char* token;
+
+        /* Find end of token */
+        while (*string != 0 && *string != delim)
+            string++;
+
+        /* Calculate token length */
+        length = string - token_start;
+
+        /* Allocate space for token and NULL terminator */
+        tokens[i++] = token = malloc(length + 1);
+
+        /* Copy token, store null */
+        memcpy(token, token_start, length);
+        token[length] = 0;
+
+        /* Stop at end of string */
+        if (*string == 0)
+            break;
+
+        /* Next token */
+        token_start = ++string;
+
+    } while (i < token_count);
+
+    /* NULL terminator */
+    tokens[i] = NULL;
+
+    return tokens;
+
+}
+
diff --git a/src/common/guac_string.h b/src/common/guac_string.h
new file mode 100644
index 0000000..9a58f15
--- /dev/null
+++ b/src/common/guac_string.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef __GUAC_COMMON_STRING_H
+#define __GUAC_COMMON_STRING_H
+
+#include "config.h"
+
+/**
+ * Counts the number of occurrences of a given character in a string.
+ *
+ * @param string The string to count occurrences within.
+ * @param c The character to count occurrences of.
+ * @return The number of occurrences.
+ */
+int guac_count_occurrences(const char* string, char c);
+
+/**
+ * Splits a string into a newly-allocated array of strings. The array itself
+ * and each string within the array will eventually need to be freed. The array
+ * is NULL-terminated.
+ *
+ * @param string The string to split.
+ * @param delim The character which separates individual substrings within the
+ *              given string.
+ * @return A newly-allocated, NULL-terminated array of strings.
+ */
+char** guac_split(const char* string, char delim);
+
+#endif
+
diff --git a/src/common/guac_surface.c b/src/common/guac_surface.c
new file mode 100644
index 0000000..40965d9
--- /dev/null
+++ b/src/common/guac_surface.c
@@ -0,0 +1,1526 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+#include "guac_rect.h"
+#include "guac_surface.h"
+
+#include <cairo/cairo.h>
+#include <guacamole/client.h>
+#include <guacamole/layer.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+#include <guacamole/timestamp.h>
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+/**
+ * The width of an update which should be considered negible and thus
+ * trivial overhead compared ot the cost of two updates.
+ */
+#define GUAC_SURFACE_NEGLIGIBLE_WIDTH 64
+
+/**
+ * The height of an update which should be considered negible and thus
+ * trivial overhead compared ot the cost of two updates.
+ */
+#define GUAC_SURFACE_NEGLIGIBLE_HEIGHT 64
+
+/**
+ * The proportional increase in cost contributed by transfer and processing of
+ * image data, compared to processing an equivalent amount of client-side
+ * data.
+ */
+#define GUAC_SURFACE_DATA_FACTOR 16
+
+/**
+ * The base cost of every update. Each update should be considered to have
+ * this starting cost, plus any additional cost estimated from its
+ * content.
+ */
+#define GUAC_SURFACE_BASE_COST 4096
+
+/**
+ * An increase in cost is negligible if it is less than
+ * 1/GUAC_SURFACE_NEGLIGIBLE_INCREASE of the old cost.
+ */
+#define GUAC_SURFACE_NEGLIGIBLE_INCREASE 4
+
+/**
+ * If combining an update because it appears to be follow a fill pattern,
+ * the combined cost must not exceed
+ * GUAC_SURFACE_FILL_PATTERN_FACTOR * (total uncombined cost).
+ */
+#define GUAC_SURFACE_FILL_PATTERN_FACTOR 3
+
+/* Define cairo_format_stride_for_width() if missing */
+#ifndef HAVE_CAIRO_FORMAT_STRIDE_FOR_WIDTH
+#define cairo_format_stride_for_width(format, width) (width*4)
+#endif
+
+/**
+ * The JPEG image quality ('quantization') setting to use. Range 0-100 where
+ * 100 is the highest quality/largest file size, and 0 is the lowest
+ * quality/smallest file size.
+ */
+#define GUAC_SURFACE_JPEG_IMAGE_QUALITY 90
+
+/**
+ * The framerate which, if exceeded, indicates that JPEG is preferred.
+ */
+#define GUAC_COMMON_SURFACE_JPEG_FRAMERATE 3
+
+/**
+ * Minimum JPEG bitmap size (area). If the bitmap is smaller than this threshold,
+ * it should be compressed as a PNG image to avoid the JPEG compression tax.
+ */
+#define GUAC_SURFACE_JPEG_MIN_BITMAP_SIZE 4096
+
+/**
+ * The WebP image quality ('quantization') setting to use. Range 0-100 where
+ * 100 is the highest quality/largest file size, and 0 is the lowest
+ * quality/smallest file size.
+ */
+#define GUAC_SURFACE_WEBP_IMAGE_QUALITY 90
+
+/**
+ * The JPEG compression min block size. This defines the optimal rectangle block
+ * size factor for JPEG compression. Usually 8x8 would suffice, but use 16 to
+ * reduce the occurrence of ringing artifacts further.
+ */
+#define GUAC_SURFACE_JPEG_BLOCK_SIZE 16
+
+/**
+ * The WebP compression min block size. This defines the optimal rectangle block
+ * size factor for WebP compression. WebP does utilize variable block size, but
+ * ensuring a block size factor reduces any noise on the image edges.
+ */
+#define GUAC_SURFACE_WEBP_BLOCK_SIZE 8
+
+/**
+ * Updates the coordinates of the given rectangle to be within the bounds of
+ * the given surface.
+ *
+ * @param surface The surface to use for clipping.
+ * @param rect The rectangle to clip.
+ * @param sx The X coordinate of the source rectangle, if any.
+ * @param sy The Y coordinate of the source rectangle, if any.
+ */
+static void __guac_common_bound_rect(guac_common_surface* surface,
+        guac_common_rect* rect, int* sx, int* sy) {
+
+    guac_common_rect bounds_rect = {
+        .x = 0,
+        .y = 0,
+        .width  = surface->width,
+        .height = surface->height
+    };
+
+    int orig_x = rect->x;
+    int orig_y = rect->y;
+
+    guac_common_rect_constrain(rect, &bounds_rect);
+
+    /* Update source X/Y if given */
+    if (sx != NULL) *sx += rect->x - orig_x;
+    if (sy != NULL) *sy += rect->y - orig_y;
+
+}
+
+/**
+ * Updates the coordinates of the given rectangle to be within the clipping
+ * rectangle of the given surface, which must always be within the bounding
+ * rectangle of the given surface.
+ *
+ * @param surface The surface to use for clipping.
+ * @param rect The rectangle to clip.
+ * @param sx The X coordinate of the source rectangle, if any.
+ * @param sy The Y coordinate of the source rectangle, if any.
+ */
+static void __guac_common_clip_rect(guac_common_surface* surface,
+        guac_common_rect* rect, int* sx, int* sy) {
+
+    int orig_x = rect->x;
+    int orig_y = rect->y;
+
+    /* Just bound within surface if no clipping rectangle applied */
+    if (!surface->clipped) {
+        __guac_common_bound_rect(surface, rect, sx, sy);
+        return;
+    }
+
+    guac_common_rect_constrain(rect, &surface->clip_rect);
+
+    /* Update source X/Y if given */
+    if (sx != NULL) *sx += rect->x - orig_x;
+    if (sy != NULL) *sy += rect->y - orig_y;
+
+}
+
+/**
+ * Returns whether the given rectangle should be combined into the existing
+ * dirty rectangle, to be eventually flushed as a "png" instruction.
+ *
+ * @param surface The surface to be queried.
+ * @param rect The update rectangle.
+ * @param rect_only Non-zero if this update, by its nature, contains only
+ *                  metainformation about the update's rectangle, zero if
+ *                  the update also contains image data.
+ * @return Non-zero if the update should be combined with any existing update,
+ *         zero otherwise.
+ */
+static int __guac_common_should_combine(guac_common_surface* surface, const guac_common_rect* rect, int rect_only) {
+
+    if (surface->dirty) {
+
+        int combined_cost, dirty_cost, update_cost;
+
+        /* Simulate combination */
+        guac_common_rect combined = surface->dirty_rect;
+        guac_common_rect_extend(&combined, rect);
+
+        /* Combine if result is still small */
+        if (combined.width <= GUAC_SURFACE_NEGLIGIBLE_WIDTH && combined.height <= GUAC_SURFACE_NEGLIGIBLE_HEIGHT)
+            return 1;
+
+        /* Estimate costs of the existing update, new update, and both combined */
+        combined_cost = GUAC_SURFACE_BASE_COST + combined.width * combined.height;
+        dirty_cost    = GUAC_SURFACE_BASE_COST + surface->dirty_rect.width * surface->dirty_rect.height;
+        update_cost   = GUAC_SURFACE_BASE_COST + rect->width * rect->height;
+
+        /* Reduce cost if no image data */
+        if (rect_only)
+            update_cost /= GUAC_SURFACE_DATA_FACTOR;
+
+        /* Combine if cost estimate shows benefit */
+        if (combined_cost <= update_cost + dirty_cost)
+            return 1;
+
+        /* Combine if increase in cost is negligible */
+        if (combined_cost - dirty_cost <= dirty_cost / GUAC_SURFACE_NEGLIGIBLE_INCREASE)
+            return 1;
+
+        if (combined_cost - update_cost <= update_cost / GUAC_SURFACE_NEGLIGIBLE_INCREASE)
+            return 1;
+
+        /* Combine if we anticipate further updates, as this update follows a common fill pattern */
+        if (rect->x == surface->dirty_rect.x && rect->y == surface->dirty_rect.y + surface->dirty_rect.height) {
+            if (combined_cost <= (dirty_cost + update_cost) * GUAC_SURFACE_FILL_PATTERN_FACTOR)
+                return 1;
+        }
+
+    }
+    
+    /* Otherwise, do not combine */
+    return 0;
+
+}
+
+/**
+ * Expands the dirty rect of the given surface to contain the rect described by the given
+ * coordinates.
+ *
+ * @param surface The surface to mark as dirty.
+ * @param rect The rectangle of the update which is dirtying the surface.
+ */
+static void __guac_common_mark_dirty(guac_common_surface* surface, const guac_common_rect* rect) {
+
+    /* Ignore empty rects */
+    if (rect->width <= 0 || rect->height <= 0)
+        return;
+
+    /* If already dirty, update existing rect */
+    if (surface->dirty)
+        guac_common_rect_extend(&surface->dirty_rect, rect);
+
+    /* Otherwise init dirty rect */
+    else {
+        surface->dirty_rect = *rect;
+        surface->dirty = 1;
+    }
+
+}
+
+/**
+ * Calculate the current average framerate for a given area on the surface.
+ *
+ * @param surface
+ *     The surface on which the framerate will be calculated.
+ *
+ * @param rect
+ *     The rect containing the area for which the average framerate will be 
+ *     calculated.
+ *
+ * @return
+ *     The average framerate of the given area, in frames per second.
+ */
+static unsigned int __guac_common_surface_calculate_framerate(
+        guac_common_surface* surface, const guac_common_rect* rect) {
+
+    int x, y;
+
+    /* Calculate heat map dimensions */
+    int heat_width = GUAC_COMMON_SURFACE_HEAT_DIMENSION(surface->width);
+
+    /* Calculate minimum X/Y coordinates intersecting given rect */
+    int min_x = rect->x / GUAC_COMMON_SURFACE_HEAT_CELL_SIZE;
+    int min_y = rect->y / GUAC_COMMON_SURFACE_HEAT_CELL_SIZE;
+
+    /* Calculate maximum X/Y coordinates intersecting given rect */
+    int max_x = min_x + (rect->width  - 1) / GUAC_COMMON_SURFACE_HEAT_CELL_SIZE;
+    int max_y = min_y + (rect->height - 1) / GUAC_COMMON_SURFACE_HEAT_CELL_SIZE;
+
+    unsigned int sum_framerate = 0;
+    unsigned int count = 0;
+
+    /* Get start of buffer at given coordinates */
+    const guac_common_surface_heat_cell* heat_row =
+        surface->heat_map + min_y * heat_width + min_x;
+
+    /* Iterate over all the heat map cells for the area
+     * and calculate the average framerate */
+    for (y = min_y; y < max_y; y++) {
+
+        /* Get current row of heat map */
+        const guac_common_surface_heat_cell* heat_cell = heat_row;
+
+        /* For each cell in subset of row */
+        for (x = min_x; x < max_x; x++) {
+
+            /* Calculate indicies for latest and oldest history entries */
+            int oldest_entry = heat_cell->oldest_entry;
+            int latest_entry = oldest_entry - 1;
+            if (latest_entry < 0)
+                latest_entry = GUAC_COMMON_SURFACE_HEAT_CELL_HISTORY_SIZE - 1;
+
+            /* Calculate elapsed time covering entire history for this cell */
+            int elapsed_time = heat_cell->history[latest_entry]
+                             - heat_cell->history[oldest_entry];
+
+            /* Calculate and add framerate */
+            if (elapsed_time)
+                sum_framerate += GUAC_COMMON_SURFACE_HEAT_CELL_HISTORY_SIZE
+                    * 1000 / elapsed_time;
+
+            /* Next heat map cell */
+            heat_cell++;
+            count++;
+
+        }
+
+        /* Next heat map row */
+        heat_row += heat_width;
+
+    }
+
+    /* Calculate the average framerate over entire rect */
+    if (count)
+        return sum_framerate / count;
+
+    return 0;
+
+}
+
+ /**
+ * Guesses whether a rectangle within a particular surface would be better
+ * compressed as PNG or using a lossy format like JPEG. Positive values
+ * indicate PNG is likely to be superior, while negative values indicate the
+ * opposite.
+ *
+ * @param surface
+ *     The surface containing the image data to check.
+ *
+ * @param rect
+ *     The rect to check within the given surface.
+ *
+ * @return
+ *     Positive values if PNG compression is likely to perform better than
+ *     lossy alternatives, or negative values if PNG is likely to perform
+ *     worse.
+ */
+static int __guac_common_surface_png_optimality(guac_common_surface* surface,
+        const guac_common_rect* rect) {
+
+    int x, y;
+
+    int num_same = 0;
+    int num_different = 1;
+
+    /* Get image/buffer metrics */
+    int width = rect->width;
+    int height = rect->height;
+    int stride = surface->stride;
+
+    /* Get buffer from surface */
+    unsigned char* buffer = surface->buffer + rect->y * stride + rect->x * 4;
+
+    /* Image must be at least 1x1 */
+    if (width < 1 || height < 1)
+        return 0;
+
+    /* For each row */
+    for (y = 0; y < height; y++) {
+
+        uint32_t* row = (uint32_t*) buffer;
+        uint32_t last_pixel = *(row++) | 0xFF000000;
+
+        /* For each pixel in current row */
+        for (x = 1; x < width; x++) {
+
+            /* Get next pixel */
+            uint32_t current_pixel = *(row++) | 0xFF000000;
+
+            /* Update same/different counts according to pixel value */
+            if (current_pixel == last_pixel)
+                num_same++;
+            else
+                num_different++;
+
+            last_pixel = current_pixel;
+
+        }
+
+        /* Advance to next row */
+        buffer += stride;
+
+    }
+
+    /* Return rough approximation of optimality for PNG compression */
+    return 0x100 * num_same / num_different - 0x400;
+
+}
+
+/**
+ * Returns whether the given rectangle would be optimally encoded as JPEG
+ * rather than PNG.
+ *
+ * @param surface
+ *     The surface to be queried.
+ *
+ * @param rect
+ *     The rectangle to check.
+ *
+ * @return
+ *     Non-zero if the rectangle would be optimally encoded as JPEG, zero
+ *     otherwise.
+ */
+static int __guac_common_surface_should_use_jpeg(guac_common_surface* surface,
+        const guac_common_rect* rect) {
+
+    /* Calculate the average framerate for the given rect */
+    int framerate = __guac_common_surface_calculate_framerate(surface, rect);
+
+    int rect_size = rect->width * rect->height;
+
+    /* JPEG is preferred if:
+     * - frame rate is high enough
+     * - image size is large enough
+     * - PNG is not more optimal based on image contents */
+    return framerate >= GUAC_COMMON_SURFACE_JPEG_FRAMERATE
+        && rect_size > GUAC_SURFACE_JPEG_MIN_BITMAP_SIZE
+        && __guac_common_surface_png_optimality(surface, rect) < 0;
+
+}
+
+/**
+ * Returns whether the given rectangle would be optimally encoded as WebP
+ * rather than PNG.
+ *
+ * @param surface
+ *     The surface to be queried.
+ *
+ * @param rect
+ *     The rectangle to check.
+ *
+ * @return
+ *     Non-zero if the rectangle would be optimally encoded as WebP, zero
+ *     otherwise.
+ */
+static int __guac_common_surface_should_use_webp(guac_common_surface* surface,
+        const guac_common_rect* rect) {
+
+    /* Do not use WebP if not supported */
+    if (!guac_client_supports_webp(surface->client))
+        return 0;
+
+    /* Calculate the average framerate for the given rect */
+    int framerate = __guac_common_surface_calculate_framerate(surface, rect);
+
+    /* WebP is preferred if:
+     * - frame rate is high enough
+     * - PNG is not more optimal based on image contents */
+    return framerate >= GUAC_COMMON_SURFACE_JPEG_FRAMERATE
+        && __guac_common_surface_png_optimality(surface, rect) < 0;
+
+}
+
+/**
+ * Updates the heat map cells which intersect the given rectangle using the
+ * given timestamp. This timestamp, along with timestamps from past updates,
+ * is used to calculate the framerate of each heat cell.
+ *
+ * @param surface
+ *     The surface containing the heat map cells to be updated.
+ *
+ * @param rect
+ *     The rectangle containing the heat map cells to be updated.
+ *
+ * @param time
+ *     The timestamp to use when updating the heat map cells which intersect
+ *     the given rectangle.
+ */
+static void __guac_common_surface_touch_rect(guac_common_surface* surface,
+        guac_common_rect* rect, guac_timestamp time) {
+
+    int x, y;
+
+    /* Calculate heat map dimensions */
+    int heat_width = GUAC_COMMON_SURFACE_HEAT_DIMENSION(surface->width);
+
+    /* Calculate minimum X/Y coordinates intersecting given rect */
+    int min_x = rect->x / GUAC_COMMON_SURFACE_HEAT_CELL_SIZE;
+    int min_y = rect->y / GUAC_COMMON_SURFACE_HEAT_CELL_SIZE;
+
+    /* Calculate maximum X/Y coordinates intersecting given rect */
+    int max_x = min_x + (rect->width  - 1) / GUAC_COMMON_SURFACE_HEAT_CELL_SIZE;
+    int max_y = min_y + (rect->height - 1) / GUAC_COMMON_SURFACE_HEAT_CELL_SIZE;
+
+    /* Get start of buffer at given coordinates */
+    guac_common_surface_heat_cell* heat_row =
+        surface->heat_map + min_y * heat_width + min_x;
+
+    /* Update all heat map cells which intersect with rectangle */
+    for (y = min_y; y <= max_y; y++) {
+
+        /* Get current row of heat map */
+        guac_common_surface_heat_cell* heat_cell = heat_row;
+
+        /* For each cell in subset of row */
+        for (x = min_x; x <= max_x; x++) {
+
+            /* Replace oldest entry with new timestamp */
+            heat_cell->history[heat_cell->oldest_entry] = time;
+
+            /* Update to next oldest entry */
+            heat_cell->oldest_entry++;
+            if (heat_cell->oldest_entry >=
+                    GUAC_COMMON_SURFACE_HEAT_CELL_HISTORY_SIZE)
+                heat_cell->oldest_entry = 0;
+
+            /* Advance to next heat map cell */
+            heat_cell++;
+
+        }
+
+        /* Next heat map row */
+        heat_row += heat_width;
+
+    }
+
+}
+
+/**
+ * Flushes the bitmap update currently described by the dirty rectangle within the
+ * given surface to that surface's bitmap queue. There MUST be space within the
+ * queue.
+ *
+ * @param surface The surface to flush.
+ */
+static void __guac_common_surface_flush_to_queue(guac_common_surface* surface) {
+
+    guac_common_surface_bitmap_rect* rect;
+
+    /* Do not flush if not dirty */
+    if (!surface->dirty)
+        return;
+
+    /* Add new rect to queue */
+    rect = &(surface->bitmap_queue[surface->bitmap_queue_length++]);
+    rect->rect = surface->dirty_rect;
+    rect->flushed = 0;
+
+    /* Surface now flushed */
+    surface->dirty = 0;
+
+}
+
+void guac_common_surface_flush_deferred(guac_common_surface* surface) {
+
+    /* Do not flush if not dirty */
+    if (!surface->dirty)
+        return;
+
+    /* Flush if queue size has reached maximum (space is reserved for the final dirty rect,
+     * as guac_common_surface_flush() MAY add an additional rect to the queue */
+    if (surface->bitmap_queue_length == GUAC_COMMON_SURFACE_QUEUE_SIZE-1)
+        guac_common_surface_flush(surface);
+
+    /* Append dirty rect to queue */
+    __guac_common_surface_flush_to_queue(surface);
+
+}
+
+/**
+ * Transfers a single uint32_t using the given transfer function.
+ *
+ * @param op The transfer function to use.
+ * @param src The source of the uint32_t value.
+ * @param dst THe destination which will hold the result of the transfer.
+ * @return Non-zero if the destination value was changed, zero otherwise.
+ */
+static int __guac_common_surface_transfer_int(guac_transfer_function op, uint32_t* src, uint32_t* dst) {
+
+    uint32_t orig = *dst;
+
+    switch (op) {
+
+        case GUAC_TRANSFER_BINARY_BLACK:
+            *dst = 0xFF000000;
+            break;
+
+        case GUAC_TRANSFER_BINARY_WHITE:
+            *dst = 0xFFFFFFFF;
+            break;
+
+        case GUAC_TRANSFER_BINARY_SRC:
+            *dst = *src;
+            break;
+
+        case GUAC_TRANSFER_BINARY_DEST:
+            /* NOP */
+            break;
+
+        case GUAC_TRANSFER_BINARY_NSRC:
+            *dst = ~(*src);
+            break;
+
+        case GUAC_TRANSFER_BINARY_NDEST:
+            *dst = ~(*dst);
+            break;
+
+        case GUAC_TRANSFER_BINARY_AND:
+            *dst = (*dst) & (*src);
+            break;
+
+        case GUAC_TRANSFER_BINARY_NAND:
+            *dst = ~((*dst) & (*src));
+            break;
+
+        case GUAC_TRANSFER_BINARY_OR:
+            *dst = (*dst) | (*src);
+            break;
+
+        case GUAC_TRANSFER_BINARY_NOR:
+            *dst = ~((*dst) | (*src));
+            break;
+
+        case GUAC_TRANSFER_BINARY_XOR:
+            *dst = (*dst) ^ (*src);
+            break;
+
+        case GUAC_TRANSFER_BINARY_XNOR:
+            *dst = ~((*dst) ^ (*src));
+            break;
+
+        case GUAC_TRANSFER_BINARY_NSRC_AND:
+            *dst = (*dst) & ~(*src);
+            break;
+
+        case GUAC_TRANSFER_BINARY_NSRC_NAND:
+            *dst = ~((*dst) & ~(*src));
+            break;
+
+        case GUAC_TRANSFER_BINARY_NSRC_OR:
+            *dst = (*dst) | ~(*src);
+            break;
+
+        case GUAC_TRANSFER_BINARY_NSRC_NOR:
+            *dst = ~((*dst) | ~(*src));
+            break;
+
+    }
+
+    return *dst != orig;
+
+}
+
+/**
+ * Draws a rectangle of solid color within the backing surface of the
+ * given destination surface.
+ *
+ * @param dst The destination surface.
+ * @param rect The rectangle to draw.
+ * @param red The red component of the color of the rectangle.
+ * @param green The green component of the color of the rectangle.
+ * @param blue The blue component of the color of the rectangle.
+ */
+static void __guac_common_surface_rect(guac_common_surface* dst, guac_common_rect* rect,
+                                       int red, int green, int blue) {
+
+    int x, y;
+
+    int dst_stride;
+    unsigned char* dst_buffer;
+
+    uint32_t color = 0xFF000000 | (red << 16) | (green << 8) | blue;
+
+    int min_x = rect->width - 1;
+    int min_y = rect->height - 1;
+    int max_x = 0;
+    int max_y = 0;
+
+    dst_stride = dst->stride;
+    dst_buffer = dst->buffer + (dst_stride * rect->y) + (4 * rect->x);
+
+    /* For each row */
+    for (y=0; y < rect->height; y++) {
+
+        uint32_t* dst_current = (uint32_t*) dst_buffer;
+
+        /* Set row */
+        for (x=0; x < rect->width; x++) {
+
+            uint32_t old_color = *dst_current;
+
+            if (old_color != color) {
+                if (x < min_x) min_x = x;
+                if (y < min_y) min_y = y;
+                if (x > max_x) max_x = x;
+                if (y > max_y) max_y = y;
+                *dst_current = color;
+            }
+
+            dst_current++;
+        }
+
+        /* Next row */
+        dst_buffer += dst_stride;
+
+    }
+
+    /* Restrict destination rect to only updated pixels */
+    if (max_x >= min_x && max_y >= min_y) {
+        rect->x += min_x;
+        rect->y += min_y;
+        rect->width = max_x - min_x + 1;
+        rect->height = max_y - min_y + 1;
+    }
+    else {
+        rect->width = 0;
+        rect->height = 0;
+    }
+
+}
+
+/**
+ * Copies data from the given buffer to the surface at the given coordinates.
+ * The dimensions and location of the destination rectangle will be altered
+ * to remove as many unchanged pixels as possible.
+ *
+ * @param src_buffer The buffer to copy.
+ * @param src_stride The number of bytes in each row of the source buffer.
+ * @param sx The X coordinate of the source rectangle.
+ * @param sy The Y coordinate of the source rectangle.
+ * @param dst The destination surface.
+ * @param rect The destination rectangle.
+ * @param opaque Non-zero if the source surface is opaque (its alpha channel
+ *               should be ignored), zero otherwise.
+ */
+static void __guac_common_surface_put(unsigned char* src_buffer, int src_stride,
+                                      int* sx, int* sy,
+                                      guac_common_surface* dst, guac_common_rect* rect,
+                                      int opaque) {
+
+    unsigned char* dst_buffer = dst->buffer;
+    int dst_stride = dst->stride;
+
+    int x, y;
+
+    int min_x = rect->width;
+    int min_y = rect->height;
+    int max_x = 0;
+    int max_y = 0;
+
+    int orig_x = rect->x;
+    int orig_y = rect->y;
+
+    src_buffer += src_stride * (*sy) + 4 * (*sx);
+    dst_buffer += (dst_stride * rect->y) + (4 * rect->x);
+
+    /* For each row */
+    for (y=0; y < rect->height; y++) {
+
+        uint32_t* src_current = (uint32_t*) src_buffer;
+        uint32_t* dst_current = (uint32_t*) dst_buffer;
+
+        /* Copy row */
+        for (x=0; x < rect->width; x++) {
+
+            if (opaque || (*src_current & 0xFF000000)) {
+
+                uint32_t new_color = *src_current | 0xFF000000;
+                uint32_t old_color = *dst_current;
+
+                if (old_color != new_color) {
+                    if (x < min_x) min_x = x;
+                    if (y < min_y) min_y = y;
+                    if (x > max_x) max_x = x;
+                    if (y > max_y) max_y = y;
+                    *dst_current = new_color;
+                }
+            }
+
+            src_current++;
+            dst_current++;
+        }
+
+        /* Next row */
+        src_buffer += src_stride;
+        dst_buffer += dst_stride;
+
+    }
+
+    /* Restrict destination rect to only updated pixels */
+    if (max_x >= min_x && max_y >= min_y) {
+        rect->x += min_x;
+        rect->y += min_y;
+        rect->width = max_x - min_x + 1;
+        rect->height = max_y - min_y + 1;
+    }
+    else {
+        rect->width = 0;
+        rect->height = 0;
+    }
+
+    /* Update source X/Y */
+    *sx += rect->x - orig_x;
+    *sy += rect->y - orig_y;
+
+}
+
+/**
+ * Fills the given surface with color, using the given buffer as a mask. Color
+ * will be added to the given surface iff the corresponding pixel within the
+ * buffer is opaque.
+ *
+ * @param src_buffer The buffer to use as a mask.
+ * @param src_stride The number of bytes in each row of the source buffer.
+ * @param sx The X coordinate of the source rectangle.
+ * @param sy The Y coordinate of the source rectangle.
+ * @param dst The destination surface.
+ * @param rect The destination rectangle.
+ * @param red The red component of the color of the fill.
+ * @param green The green component of the color of the fill.
+ * @param blue The blue component of the color of the fill.
+ */
+static void __guac_common_surface_fill_mask(unsigned char* src_buffer, int src_stride,
+                                            int sx, int sy,
+                                            guac_common_surface* dst, guac_common_rect* rect,
+                                            int red, int green, int blue) {
+
+    unsigned char* dst_buffer = dst->buffer;
+    int dst_stride = dst->stride;
+
+    uint32_t color = 0xFF000000 | (red << 16) | (green << 8) | blue;
+    int x, y;
+
+    src_buffer += src_stride*sy + 4*sx;
+    dst_buffer += (dst_stride * rect->y) + (4 * rect->x);
+
+    /* For each row */
+    for (y=0; y < rect->height; y++) {
+
+        uint32_t* src_current = (uint32_t*) src_buffer;
+        uint32_t* dst_current = (uint32_t*) dst_buffer;
+
+        /* Stencil row */
+        for (x=0; x < rect->width; x++) {
+
+            /* Fill with color if opaque */
+            if (*src_current & 0xFF000000)
+                *dst_current = color;
+
+            src_current++;
+            dst_current++;
+        }
+
+        /* Next row */
+        src_buffer += src_stride;
+        dst_buffer += dst_stride;
+
+    }
+
+}
+
+/**
+ * Copies data from the given surface to the given destination surface using
+ * the specified transfer function.
+ *
+ * @param src_buffer The buffer to copy.
+ * @param src_stride The number of bytes in each row of the source buffer.
+ * @param sx The X coordinate of the source rectangle.
+ * @param sy The Y coordinate of the source rectangle.
+ * @param op The transfer function to use.
+ * @param dst The destination surface.
+ * @param rect The destination rectangle.
+ */
+static void __guac_common_surface_transfer(guac_common_surface* src, int* sx, int* sy,
+                                           guac_transfer_function op,
+                                           guac_common_surface* dst, guac_common_rect* rect) {
+
+    unsigned char* src_buffer = src->buffer;
+    unsigned char* dst_buffer = dst->buffer;
+
+    int x, y;
+    int src_stride, dst_stride;
+    int step = 1;
+
+    int min_x = rect->width - 1;
+    int min_y = rect->height - 1;
+    int max_x = 0;
+    int max_y = 0;
+
+    int orig_x = rect->x;
+    int orig_y = rect->y;
+
+    /* Copy forwards only if destination is in a different surface or is before source */
+    if (src != dst || rect->y < *sy || (rect->y == *sy && rect->x < *sx)) {
+        src_buffer += src->stride * (*sy) + 4 * (*sx);
+        dst_buffer += (dst->stride * rect->y) + (4 * rect->x);
+        src_stride = src->stride;
+        dst_stride = dst->stride;
+        step = 1;
+    }
+
+    /* Otherwise, copy backwards */
+    else {
+        src_buffer += src->stride * (*sy + rect->height - 1) + 4 * (*sx + rect->width - 1);
+        dst_buffer += dst->stride * (rect->y + rect->height - 1) + 4 * (rect->x + rect->width - 1);
+        src_stride = -src->stride;
+        dst_stride = -dst->stride;
+        step = -1;
+    }
+
+    /* For each row */
+    for (y=0; y < rect->height; y++) {
+
+        uint32_t* src_current = (uint32_t*) src_buffer;
+        uint32_t* dst_current = (uint32_t*) dst_buffer;
+
+        /* Transfer each pixel in row */
+        for (x=0; x < rect->width; x++) {
+
+            if (__guac_common_surface_transfer_int(op, src_current, dst_current)) {
+                if (x < min_x) min_x = x;
+                if (y < min_y) min_y = y;
+                if (x > max_x) max_x = x;
+                if (y > max_y) max_y = y;
+            }
+
+            src_current += step;
+            dst_current += step;
+        }
+
+        /* Next row */
+        src_buffer += src_stride;
+        dst_buffer += dst_stride;
+
+    }
+
+    /* Translate X coordinate space of moving backwards */
+    if (step < 0) {
+        int old_max_x = max_x;
+        max_x = rect->width - 1 - min_x;
+        min_x = rect->width - 1 - old_max_x;
+    }
+
+    /* Translate Y coordinate space of moving backwards */
+    if (dst_stride < 0) {
+        int old_max_y = max_y;
+        max_y = rect->height - 1 - min_y;
+        min_y = rect->height - 1 - old_max_y;
+    }
+
+    /* Restrict destination rect to only updated pixels */
+    if (max_x >= min_x && max_y >= min_y) {
+        rect->x += min_x;
+        rect->y += min_y;
+        rect->width = max_x - min_x + 1;
+        rect->height = max_y - min_y + 1;
+    }
+    else {
+        rect->width = 0;
+        rect->height = 0;
+    }
+
+    /* Update source X/Y */
+    *sx += rect->x - orig_x;
+    *sy += rect->y - orig_y;
+
+}
+
+guac_common_surface* guac_common_surface_alloc(guac_client* client,
+        guac_socket* socket, const guac_layer* layer, int w, int h) {
+
+    /* Calculate heat map dimensions */
+    int heat_width = GUAC_COMMON_SURFACE_HEAT_DIMENSION(w);
+    int heat_height = GUAC_COMMON_SURFACE_HEAT_DIMENSION(h);
+
+    /* Init surface */
+    guac_common_surface* surface = calloc(1, sizeof(guac_common_surface));
+    surface->client = client;
+    surface->socket = socket;
+    surface->layer = layer;
+    surface->width = w;
+    surface->height = h;
+
+    /* Create corresponding Cairo surface */
+    surface->stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, w);
+    surface->buffer = calloc(h, surface->stride);
+
+    /* Create corresponding heat map */
+    surface->heat_map = calloc(heat_width * heat_height,
+            sizeof(guac_common_surface_heat_cell));
+
+    /* Reset clipping rect */
+    guac_common_surface_reset_clip(surface);
+
+    /* Layers must initially exist */
+    if (layer->index >= 0) {
+        guac_protocol_send_size(socket, layer, w, h);
+        surface->realized = 1;
+    }
+
+    /* Defer creation of buffers */
+    else
+        surface->realized = 0;
+
+    return surface;
+}
+
+void guac_common_surface_free(guac_common_surface* surface) {
+
+    /* Only dispose of surface if it exists */
+    if (surface->realized)
+        guac_protocol_send_dispose(surface->socket, surface->layer);
+
+    free(surface->heat_map);
+    free(surface->buffer);
+    free(surface);
+
+}
+
+void guac_common_surface_resize(guac_common_surface* surface, int w, int h) {
+
+    guac_socket* socket = surface->socket;
+    const guac_layer* layer = surface->layer;
+
+    unsigned char* old_buffer;
+    int old_stride;
+    guac_common_rect old_rect;
+
+    int sx = 0;
+    int sy = 0;
+
+    /* Calculate heat map dimensions */
+    int heat_width = GUAC_COMMON_SURFACE_HEAT_DIMENSION(w);
+    int heat_height = GUAC_COMMON_SURFACE_HEAT_DIMENSION(h);
+
+    /* Copy old surface data */
+    old_buffer = surface->buffer;
+    old_stride = surface->stride;
+    guac_common_rect_init(&old_rect, 0, 0, surface->width, surface->height);
+
+    /* Re-initialize at new size */
+    surface->width  = w;
+    surface->height = h;
+    surface->stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, w);
+    surface->buffer = calloc(h, surface->stride);
+    __guac_common_bound_rect(surface, &surface->clip_rect, NULL, NULL);
+
+    /* Copy relevant old data */
+    __guac_common_bound_rect(surface, &old_rect, NULL, NULL);
+    __guac_common_surface_put(old_buffer, old_stride, &sx, &sy, surface, &old_rect, 1);
+
+    /* Free old data */
+    free(old_buffer);
+
+    /* Allocate completely new heat map (can safely discard old stats) */
+    free(surface->heat_map);
+    surface->heat_map = calloc(heat_width * heat_height,
+            sizeof(guac_common_surface_heat_cell));
+
+    /* Resize dirty rect to fit new surface dimensions */
+    if (surface->dirty) {
+        __guac_common_bound_rect(surface, &surface->dirty_rect, NULL, NULL);
+        if (surface->dirty_rect.width <= 0 || surface->dirty_rect.height <= 0)
+            surface->dirty = 0;
+    }
+
+    /* Update Guacamole layer */
+    if (surface->realized)
+        guac_protocol_send_size(socket, layer, w, h);
+
+}
+
+void guac_common_surface_draw(guac_common_surface* surface, int x, int y, cairo_surface_t* src) {
+
+    unsigned char* buffer = cairo_image_surface_get_data(src);
+    cairo_format_t format = cairo_image_surface_get_format(src);
+    int stride = cairo_image_surface_get_stride(src);
+    int w = cairo_image_surface_get_width(src);
+    int h = cairo_image_surface_get_height(src);
+
+    int sx = 0;
+    int sy = 0;
+
+    guac_common_rect rect;
+    guac_common_rect_init(&rect, x, y, w, h);
+
+    /* Clip operation */
+    __guac_common_clip_rect(surface, &rect, &sx, &sy);
+    if (rect.width <= 0 || rect.height <= 0)
+        return;
+
+    /* Update backing surface */
+    __guac_common_surface_put(buffer, stride, &sx, &sy, surface, &rect, format != CAIRO_FORMAT_ARGB32);
+    if (rect.width <= 0 || rect.height <= 0)
+        return;
+
+    /* Update the heat map for the update rectangle. */
+    guac_timestamp time = guac_timestamp_current();
+    __guac_common_surface_touch_rect(surface, &rect, time);
+
+    /* Flush if not combining */
+    if (!__guac_common_should_combine(surface, &rect, 0))
+        guac_common_surface_flush_deferred(surface);
+
+    /* Always defer draws */
+    __guac_common_mark_dirty(surface, &rect);
+
+}
+
+void guac_common_surface_paint(guac_common_surface* surface, int x, int y, cairo_surface_t* src,
+                               int red, int green, int blue) {
+
+    unsigned char* buffer = cairo_image_surface_get_data(src);
+    int stride = cairo_image_surface_get_stride(src);
+    int w = cairo_image_surface_get_width(src);
+    int h = cairo_image_surface_get_height(src);
+
+    int sx = 0;
+    int sy = 0;
+
+    guac_common_rect rect;
+    guac_common_rect_init(&rect, x, y, w, h);
+
+    /* Clip operation */
+    __guac_common_clip_rect(surface, &rect, &sx, &sy);
+    if (rect.width <= 0 || rect.height <= 0)
+        return;
+
+    /* Update backing surface */
+    __guac_common_surface_fill_mask(buffer, stride, sx, sy, surface, &rect, red, green, blue);
+
+    /* Flush if not combining */
+    if (!__guac_common_should_combine(surface, &rect, 0))
+        guac_common_surface_flush_deferred(surface);
+
+    /* Always defer draws */
+    __guac_common_mark_dirty(surface, &rect);
+
+}
+
+void guac_common_surface_copy(guac_common_surface* src, int sx, int sy, int w, int h,
+                              guac_common_surface* dst, int dx, int dy) {
+
+    guac_socket* socket = dst->socket;
+    const guac_layer* src_layer = src->layer;
+    const guac_layer* dst_layer = dst->layer;
+
+    guac_common_rect rect;
+    guac_common_rect_init(&rect, dx, dy, w, h);
+
+    /* Clip operation */
+    __guac_common_clip_rect(dst, &rect, &sx, &sy);
+    if (rect.width <= 0 || rect.height <= 0)
+        return;
+
+    /* Update backing surface first only if destination rect cannot intersect source rect */
+    if (src != dst) {
+        __guac_common_surface_transfer(src, &sx, &sy, GUAC_TRANSFER_BINARY_SRC, dst, &rect);
+        if (rect.width <= 0 || rect.height <= 0)
+            return;
+    }
+
+    /* Defer if combining */
+    if (__guac_common_should_combine(dst, &rect, 1))
+        __guac_common_mark_dirty(dst, &rect);
+
+    /* Otherwise, flush and draw immediately */
+    else {
+        guac_common_surface_flush(dst);
+        guac_common_surface_flush(src);
+        guac_protocol_send_copy(socket, src_layer, sx, sy, rect.width, rect.height,
+                                GUAC_COMP_OVER, dst_layer, rect.x, rect.y);
+        dst->realized = 1;
+    }
+
+    /* Update backing surface last if destination rect can intersect source rect */
+    if (src == dst)
+        __guac_common_surface_transfer(src, &sx, &sy, GUAC_TRANSFER_BINARY_SRC, dst, &rect);
+
+}
+
+void guac_common_surface_transfer(guac_common_surface* src, int sx, int sy, int w, int h,
+                                  guac_transfer_function op, guac_common_surface* dst, int dx, int dy) {
+
+    guac_socket* socket = dst->socket;
+    const guac_layer* src_layer = src->layer;
+    const guac_layer* dst_layer = dst->layer;
+
+    guac_common_rect rect;
+    guac_common_rect_init(&rect, dx, dy, w, h);
+
+    /* Clip operation */
+    __guac_common_clip_rect(dst, &rect, &sx, &sy);
+    if (rect.width <= 0 || rect.height <= 0)
+        return;
+
+    /* Update backing surface first only if destination rect cannot intersect source rect */
+    if (src != dst) {
+        __guac_common_surface_transfer(src, &sx, &sy, op, dst, &rect);
+        if (rect.width <= 0 || rect.height <= 0)
+            return;
+    }
+
+    /* Defer if combining */
+    if (__guac_common_should_combine(dst, &rect, 1))
+        __guac_common_mark_dirty(dst, &rect);
+
+    /* Otherwise, flush and draw immediately */
+    else {
+        guac_common_surface_flush(dst);
+        guac_common_surface_flush(src);
+        guac_protocol_send_transfer(socket, src_layer, sx, sy, rect.width, rect.height, op, dst_layer, rect.x, rect.y);
+        dst->realized = 1;
+    }
+
+    /* Update backing surface last if destination rect can intersect source rect */
+    if (src == dst)
+        __guac_common_surface_transfer(src, &sx, &sy, op, dst, &rect);
+
+}
+
+void guac_common_surface_rect(guac_common_surface* surface,
+                              int x, int y, int w, int h,
+                              int red, int green, int blue) {
+
+    guac_socket* socket = surface->socket;
+    const guac_layer* layer = surface->layer;
+
+    guac_common_rect rect;
+    guac_common_rect_init(&rect, x, y, w, h);
+
+    /* Clip operation */
+    __guac_common_clip_rect(surface, &rect, NULL, NULL);
+    if (rect.width <= 0 || rect.height <= 0)
+        return;
+
+    /* Update backing surface */
+    __guac_common_surface_rect(surface, &rect, red, green, blue);
+    if (rect.width <= 0 || rect.height <= 0)
+        return;
+
+    /* Defer if combining */
+    if (__guac_common_should_combine(surface, &rect, 1))
+        __guac_common_mark_dirty(surface, &rect);
+
+    /* Otherwise, flush and draw immediately */
+    else {
+        guac_common_surface_flush(surface);
+        guac_protocol_send_rect(socket, layer, rect.x, rect.y, rect.width, rect.height);
+        guac_protocol_send_cfill(socket, GUAC_COMP_OVER, layer, red, green, blue, 0xFF);
+        surface->realized = 1;
+    }
+
+}
+
+void guac_common_surface_clip(guac_common_surface* surface, int x, int y, int w, int h) {
+
+    guac_common_rect clip;
+
+    /* Init clipping rectangle if clipping not already applied */
+    if (!surface->clipped) {
+        guac_common_rect_init(&surface->clip_rect, 0, 0, surface->width, surface->height);
+        surface->clipped = 1;
+    }
+
+    guac_common_rect_init(&clip, x, y, w, h);
+    guac_common_rect_constrain(&surface->clip_rect, &clip);
+
+}
+
+void guac_common_surface_reset_clip(guac_common_surface* surface) {
+    surface->clipped = 0;
+}
+
+/**
+ * Flushes the bitmap update currently described by the dirty rectangle within
+ * the given surface directly via an "img" instruction as PNG data. The
+ * resulting instructions will be sent over the socket associated with the
+ * given surface.
+ *
+ * @param surface
+ *     The surface to flush.
+ */
+static void __guac_common_surface_flush_to_png(guac_common_surface* surface) {
+
+    if (surface->dirty) {
+
+        guac_socket* socket = surface->socket;
+        const guac_layer* layer = surface->layer;
+
+        /* Get Cairo surface for specified rect */
+        unsigned char* buffer = surface->buffer + surface->dirty_rect.y * surface->stride + surface->dirty_rect.x * 4;
+        cairo_surface_t* rect = cairo_image_surface_create_for_data(buffer, CAIRO_FORMAT_RGB24,
+                                                                    surface->dirty_rect.width,
+                                                                    surface->dirty_rect.height,
+                                                                    surface->stride);
+
+        /* Send PNG for rect */
+        guac_client_stream_png(surface->client, socket, GUAC_COMP_OVER,
+                layer, surface->dirty_rect.x, surface->dirty_rect.y, rect);
+        cairo_surface_destroy(rect);
+        surface->realized = 1;
+
+        /* Surface is no longer dirty */
+        surface->dirty = 0;
+
+    }
+
+}
+
+/**
+ * Flushes the bitmap update currently described by the dirty rectangle within
+ * the given surface directly via an "img" instruction as JPEG data. The
+ * resulting instructions will be sent over the socket associated with the
+ * given surface.
+ *
+ * @param surface
+ *     The surface to flush.
+ */
+static void __guac_common_surface_flush_to_jpeg(guac_common_surface* surface) {
+
+    if (surface->dirty) {
+
+        guac_socket* socket = surface->socket;
+        const guac_layer* layer = surface->layer;
+
+        guac_common_rect max;
+        guac_common_rect_init(&max, 0, 0, surface->width, surface->height);
+
+        /* Expand the dirty rect size to fit in a grid with cells equal to the
+         * minimum JPEG block size */
+        guac_common_rect_expand_to_grid(GUAC_SURFACE_JPEG_BLOCK_SIZE,
+                                        &surface->dirty_rect, &max);
+
+        /* Get Cairo surface for specified rect */
+        unsigned char* buffer = surface->buffer + surface->dirty_rect.y * surface->stride + surface->dirty_rect.x * 4;
+        cairo_surface_t* rect = cairo_image_surface_create_for_data(buffer, CAIRO_FORMAT_RGB24,
+                                                                    surface->dirty_rect.width,
+                                                                    surface->dirty_rect.height,
+                                                                    surface->stride);
+
+        /* Send JPEG for rect */
+        guac_client_stream_jpeg(surface->client, socket, GUAC_COMP_OVER, layer,
+                surface->dirty_rect.x, surface->dirty_rect.y, rect,
+                GUAC_SURFACE_JPEG_IMAGE_QUALITY);
+        cairo_surface_destroy(rect);
+        surface->realized = 1;
+
+        /* Surface is no longer dirty */
+        surface->dirty = 0;
+
+    }
+
+}
+
+/**
+ * Flushes the bitmap update currently described by the dirty rectangle within
+ * the given surface directly via an "img" instruction as WebP data. The
+ * resulting instructions will be sent over the socket associated with the
+ * given surface.
+ *
+ * @param surface
+ *     The surface to flush.
+ */
+static void __guac_common_surface_flush_to_webp(guac_common_surface* surface) {
+
+    if (surface->dirty) {
+
+        guac_socket* socket = surface->socket;
+        const guac_layer* layer = surface->layer;
+
+        guac_common_rect max;
+        guac_common_rect_init(&max, 0, 0, surface->width, surface->height);
+
+        /* Expand the dirty rect size to fit in a grid with cells equal to the
+         * minimum WebP block size */
+        guac_common_rect_expand_to_grid(GUAC_SURFACE_WEBP_BLOCK_SIZE,
+                                        &surface->dirty_rect, &max);
+
+        /* Get Cairo surface for specified rect */
+        unsigned char* buffer = surface->buffer
+            + surface->dirty_rect.y * surface->stride
+            + surface->dirty_rect.x * 4;
+
+        cairo_surface_t* rect = cairo_image_surface_create_for_data(buffer,
+                CAIRO_FORMAT_RGB24,
+                surface->dirty_rect.width, surface->dirty_rect.height,
+                surface->stride);
+
+        /* Send WebP for rect */
+        guac_client_stream_webp(surface->client, socket, GUAC_COMP_OVER, layer,
+                surface->dirty_rect.x, surface->dirty_rect.y, rect,
+                GUAC_SURFACE_WEBP_IMAGE_QUALITY, 0);
+        cairo_surface_destroy(rect);
+        surface->realized = 1;
+
+        /* Surface is no longer dirty */
+        surface->dirty = 0;
+
+    }
+
+}
+
+
+/**
+ * Comparator for instances of guac_common_surface_bitmap_rect, the elements
+ * which make up a surface's bitmap buffer.
+ *
+ * @see qsort
+ */
+static int __guac_common_surface_bitmap_rect_compare(const void* a, const void* b) {
+
+    guac_common_surface_bitmap_rect* ra = (guac_common_surface_bitmap_rect*) a;
+    guac_common_surface_bitmap_rect* rb = (guac_common_surface_bitmap_rect*) b;
+
+    /* Order roughly top to bottom, left to right */
+    if (ra->rect.y != rb->rect.y) return ra->rect.y - rb->rect.y;
+    if (ra->rect.x != rb->rect.x) return ra->rect.x - rb->rect.x;
+
+    /* Wider updates should come first (more likely to intersect later) */
+    if (ra->rect.width != rb->rect.width) return rb->rect.width - ra->rect.width;
+
+    /* Shorter updates should come first (less likely to increase cost) */
+    return ra->rect.height - rb->rect.height;
+
+}
+
+void guac_common_surface_flush(guac_common_surface* surface) {
+
+    /* Flush final dirty rectangle to queue. */
+    __guac_common_surface_flush_to_queue(surface);
+
+    guac_common_surface_bitmap_rect* current = surface->bitmap_queue;
+    int i, j;
+    int original_queue_length;
+    int flushed = 0;
+
+    original_queue_length = surface->bitmap_queue_length;
+
+    /* Sort updates to make combination less costly */
+    qsort(surface->bitmap_queue, surface->bitmap_queue_length, sizeof(guac_common_surface_bitmap_rect),
+          __guac_common_surface_bitmap_rect_compare);
+
+    /* Flush all rects in queue */
+    for (i=0; i < surface->bitmap_queue_length; i++) {
+
+        /* Get next unflushed candidate */
+        guac_common_surface_bitmap_rect* candidate = current;
+        if (!candidate->flushed) {
+
+            int combined = 0;
+
+            /* Build up rect as much as possible */
+            for (j=i; j < surface->bitmap_queue_length; j++) {
+
+                if (!candidate->flushed) {
+
+                    /* Clip candidate within current bounds */
+                    __guac_common_bound_rect(surface, &candidate->rect, NULL, NULL);
+                    if (candidate->rect.width <= 0 || candidate->rect.height <= 0)
+                        candidate->flushed = 1;
+
+                    /* Combine if reasonable */
+                    else if (__guac_common_should_combine(surface, &candidate->rect, 0) || !surface->dirty) {
+                        __guac_common_mark_dirty(surface, &candidate->rect);
+                        candidate->flushed = 1;
+                        combined++;
+                    }
+
+                }
+
+                candidate++;
+
+            }
+
+            /* Re-add to queue if there's room and this update was modified or we expect others might be */
+            if ((combined > 1 || i < original_queue_length)
+                    && surface->bitmap_queue_length < GUAC_COMMON_SURFACE_QUEUE_SIZE)
+                __guac_common_surface_flush_to_queue(surface);
+
+            /* Flush as bitmap otherwise */
+            else if (surface->dirty) {
+
+                flushed++;
+
+                /* Prefer WebP when reasonable */
+                if (__guac_common_surface_should_use_webp(surface,
+                            &surface->dirty_rect))
+                    __guac_common_surface_flush_to_webp(surface);
+
+                /* If not WebP, JPEG is the next best (lossy) choice */
+                else if (__guac_common_surface_should_use_jpeg(surface,
+                            &surface->dirty_rect))
+                    __guac_common_surface_flush_to_jpeg(surface);
+
+                /* Use PNG if no lossy formats are appropriate */
+                else
+                    __guac_common_surface_flush_to_png(surface);
+
+            }
+
+        }
+
+        current++;
+
+    }
+
+    /* Flush complete */
+    surface->bitmap_queue_length = 0;
+
+}
+
diff --git a/src/common/guac_surface.h b/src/common/guac_surface.h
new file mode 100644
index 0000000..93d7c8b
--- /dev/null
+++ b/src/common/guac_surface.h
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef __GUAC_COMMON_SURFACE_H
+#define __GUAC_COMMON_SURFACE_H
+
+#include "config.h"
+#include "guac_rect.h"
+
+#include <cairo/cairo.h>
+#include <guacamole/client.h>
+#include <guacamole/layer.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+
+/**
+ * The maximum number of updates to allow within the bitmap queue.
+ */
+#define GUAC_COMMON_SURFACE_QUEUE_SIZE 256
+
+/**
+ * Heat map cell size in pixels. Each side of each heat map cell will consist
+ * of this many pixels.
+ */
+#define GUAC_COMMON_SURFACE_HEAT_CELL_SIZE 64
+
+/**
+ * The width or height of the heat map (in cells) given the width or height of
+ * the image (in pixels).
+ */
+#define GUAC_COMMON_SURFACE_HEAT_DIMENSION(x) (       \
+        (x + GUAC_COMMON_SURFACE_HEAT_CELL_SIZE - 1)  \
+            / GUAC_COMMON_SURFACE_HEAT_CELL_SIZE      \
+)
+
+/**
+ * The number of entries to collect within each heat map cell. Collected
+ * history entries are used to determine the framerate of the region associated
+ * with that cell.
+ */
+#define GUAC_COMMON_SURFACE_HEAT_CELL_HISTORY_SIZE 5
+
+/**
+ * Representation of a cell in the refresh heat map. This cell is used to keep
+ * track of how often an area on a surface is refreshed.
+ */
+typedef struct guac_common_surface_heat_cell {
+
+    /**
+     * Timestamps of each of the last N updates covering the location
+     * associated with this heat map cell. This is used to calculate the
+     * framerate. This array is structured as a ring buffer containing history
+     * entries in chronologically-ascending order, starting at the entry
+     * pointed to by oldest_entry and proceeding through all other entries,
+     * wrapping around if the end of the array is reached.
+     */
+    guac_timestamp history[GUAC_COMMON_SURFACE_HEAT_CELL_HISTORY_SIZE];
+
+    /**
+     * Index of the oldest entry within the history.
+     */
+    int oldest_entry;
+
+} guac_common_surface_heat_cell;
+
+/**
+ * Representation of a bitmap update, having a rectangle of image data (stored
+ * elsewhere) and a flushed/not-flushed state.
+ */
+typedef struct guac_common_surface_bitmap_rect {
+
+    /**
+     * Whether this rectangle has been flushed.
+     */
+    int flushed;
+
+    /**
+     * The rectangle containing the bitmap update.
+     */
+    guac_common_rect rect;
+
+} guac_common_surface_bitmap_rect;
+
+/**
+ * Surface which backs a Guacamole buffer or layer, automatically
+ * combining updates when possible.
+ */
+typedef struct guac_common_surface {
+
+    /**
+     * The layer this surface will draw to.
+     */
+    const guac_layer* layer;
+
+    /**
+     * The client associated with this surface.
+     */
+    guac_client* client;
+
+    /**
+     * The socket to send instructions on when flushing.
+     */
+    guac_socket* socket;
+
+    /**
+     * The width of this layer, in pixels.
+     */
+    int width;
+
+    /**
+     * The height of this layer, in pixels.
+     */
+    int height;
+
+    /**
+     * The size of each image row, in bytes.
+     */
+    int stride;
+
+    /**
+     * The underlying buffer of the Cairo surface.
+     */
+    unsigned char* buffer;
+
+    /**
+     * Non-zero if this surface is dirty and needs to be flushed, 0 otherwise.
+     */
+    int dirty;
+
+    /**
+     * The dirty rectangle.
+     */
+    guac_common_rect dirty_rect;
+
+    /**
+     * Whether the surface actually exists on the client.
+     */
+    int realized;
+
+    /**
+     * Whether drawing operations are currently clipped by the clipping
+     * rectangle.
+     */
+    int clipped;
+
+    /**
+     * The clipping rectangle.
+     */
+    guac_common_rect clip_rect;
+
+    /**
+     * The number of updates in the bitmap queue.
+     */
+    int bitmap_queue_length;
+
+    /**
+     * All queued bitmap updates.
+     */
+    guac_common_surface_bitmap_rect bitmap_queue[GUAC_COMMON_SURFACE_QUEUE_SIZE];
+
+    /**
+     * A heat map keeping track of the refresh frequency of
+     * the areas of the screen.
+     */
+    guac_common_surface_heat_cell* heat_map;
+
+} guac_common_surface;
+
+/**
+ * Allocates a new guac_common_surface, assigning it to the given layer.
+ *
+ * @param client
+ *     The client associated with the given layer.
+ *
+ * @param socket
+ *     The socket to send instructions on when flushing.
+ *
+ * @param layer
+ *     The layer to associate with the new surface.
+ *
+ * @param w
+ *     The width of the surface.
+ *
+ * @param h
+ *     The height of the surface.
+ *
+ * @return
+ *     A newly-allocated guac_common_surface.
+ */
+guac_common_surface* guac_common_surface_alloc(guac_client* client,
+        guac_socket* socket, const guac_layer* layer, int w, int h);
+
+/**
+ * Frees the given guac_common_surface. Beware that this will NOT free any
+ * associated layers, which must be freed manually.
+ *
+ * @param surface The surface to free.
+ */
+void guac_common_surface_free(guac_common_surface* surface);
+
+ /**
+ * Resizes the given surface to the given size.
+ *
+ * @param surface The surface to resize.
+ * @param w The width of the surface.
+ * @param h The height of the surface.
+ */
+void guac_common_surface_resize(guac_common_surface* surface, int w, int h);
+
+/**
+ * Draws the given data to the given guac_common_surface.
+ *
+ * @param surface The surface to draw to.
+ * @param x The X coordinate of the draw location.
+ * @param y The Y coordinate of the draw location.
+ * @param src The Cairo surface to retrieve data from.
+ */
+void guac_common_surface_draw(guac_common_surface* surface, int x, int y, cairo_surface_t* src);
+
+/**
+ * Paints to the given guac_common_surface using the given data as a stencil,
+ * filling opaque regions with the specified color, and leaving transparent
+ * regions untouched.
+ *
+ * @param surface The surface to draw to.
+ * @param x The X coordinate of the draw location.
+ * @param y The Y coordinate of the draw location.
+ * @param src The Cairo surface to retrieve data from.
+ * @param red The red component of the fill color.
+ * @param green The green component of the fill color.
+ * @param blue The blue component of the fill color.
+ */
+void guac_common_surface_paint(guac_common_surface* surface, int x, int y, cairo_surface_t* src,
+                              int red, int green, int blue);
+
+/**
+ * Copies a rectangle of data between two surfaces.
+ *
+ * @param src The source surface.
+ * @param sx The X coordinate of the upper-left corner of the source rect.
+ * @param sy The Y coordinate of the upper-left corner of the source rect.
+ * @param w The width of the source rect.
+ * @param h The height of the source rect.
+ * @param dst The destination surface.
+ * @param dx The X coordinate of the upper-left corner of the destination rect.
+ * @param dy The Y coordinate of the upper-left corner of the destination rect.
+ */
+void guac_common_surface_copy(guac_common_surface* src, int sx, int sy, int w, int h,
+                              guac_common_surface* dst, int dx, int dy);
+
+/**
+ * Transfers a rectangle of data between two surfaces.
+ *
+ * @param src The source surface.
+ * @param sx The X coordinate of the upper-left corner of the source rect.
+ * @param sy The Y coordinate of the upper-left corner of the source rect.
+ * @param w The width of the source rect.
+ * @param h The height of the source rect.
+ * @param op The transfer function.
+ * @param dst The destination surface.
+ * @param dx The X coordinate of the upper-left corner of the destination rect.
+ * @param dy The Y coordinate of the upper-left corner of the destination rect.
+ */
+void guac_common_surface_transfer(guac_common_surface* src, int sx, int sy, int w, int h,
+                                  guac_transfer_function op, guac_common_surface* dst, int dx, int dy);
+
+/**
+ * Draws a solid color rectangle at the given coordinates on the given surface.
+ *
+ * @param surface The surface to draw upon.
+ * @param x The X coordinate of the upper-left corner of the rectangle.
+ * @param y The Y coordinate of the upper-left corner of the rectangle.
+ * @param w The width of the rectangle.
+ * @param h The height of the rectangle.
+ * @param red The red component of the color of the rectangle.
+ * @param green The green component of the color of the rectangle.
+ * @param blue The blue component of the color of the rectangle.
+ */
+void guac_common_surface_rect(guac_common_surface* surface,
+                              int x, int y, int w, int h,
+                              int red, int green, int blue);
+
+/**
+ * Given the coordinates and dimensions of a rectangle, clips all future
+ * operations within that rectangle.
+ *
+ * @param surface The surface whose clipping rectangle should be changed.
+ * @param x The X coordinate of the upper-left corner of the bounding rectangle.
+ * @param y The Y coordinate of the upper-left corner of the bounding rectangle.
+ * @param w The width of the bounding rectangle.
+ * @param h The height of the bounding rectangle.
+ */
+void guac_common_surface_clip(guac_common_surface* surface, int x, int y, int w, int h);
+
+/**
+ * Resets the clipping rectangle, allowing drawing operations throughout the
+ * entire surface.
+ *
+ * @param surface The surface whose clipping rectangle should be reset.
+ */
+void guac_common_surface_reset_clip(guac_common_surface* surface);
+
+/**
+ * Flushes the given surface, drawing any pending operations on the remote
+ * display.
+ *
+ * @param surface The surface to flush.
+ */
+void guac_common_surface_flush(guac_common_surface* surface);
+
+/**
+ * Schedules a deferred flush of the given surface. This will not immediately
+ * flush the surface to the client. Instead, the result of the flush is
+ * added to a queue which is reinspected and combined (if possible) with other
+ * deferred flushes during the call to guac_common_surface_flush().
+ *
+ * @param surface The surface to flush.
+ */
+void guac_common_surface_flush_deferred(guac_common_surface* surface);
+
+#endif
+
diff --git a/src/guacd/Makefile.am b/src/guacd/Makefile.am
index 91cc102..db1e7a5 100644
--- a/src/guacd/Makefile.am
+++ b/src/guacd/Makefile.am
@@ -1,52 +1,68 @@
-# ***** BEGIN LICENSE BLOCK *****
-# Version: MPL 1.1/GPL 2.0/LGPL 2.1
 #
-# The contents of this file are subject to the Mozilla Public License Version
-# 1.1 (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.mozilla.org/MPL/
+# Copyright (C) 2015 Glyptodon LLC
 #
-# Software distributed under the License is distributed on an "AS IS" basis,
-# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-# for the specific language governing rights and limitations under the
-# License.
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
 #
-# The Original Code is guacd.
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
 #
-# The Initial Developer of the Original Code is
-# Michael Jumper.
-# Portions created by the Initial Developer are Copyright (C) 2010
-# the Initial Developer. All Rights Reserved.
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
 #
-# Contributor(s):
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either the GNU General Public License Version 2 or later (the "GPL"), or
-# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-# in which case the provisions of the GPL or the LGPL are applicable instead
-# of those above. If you wish to allow use of your version of this file only
-# under the terms of either the GPL or the LGPL, and not to allow others to
-# use your version of this file under the terms of the MPL, indicate your
-# decision by deleting the provisions above and replace them with the notice
-# and other provisions required by the GPL or the LGPL. If you do not delete
-# the provisions above, a recipient may use your version of this file under
-# the terms of any one of the MPL, the GPL or the LGPL.
-#
-# ***** END LICENSE BLOCK *****
 
 AUTOMAKE_OPTIONS = foreign 
 
-AM_CFLAGS = -Werror -Wall -pedantic @LIBGUAC_INCLUDE@
-
 sbin_PROGRAMS = guacd
-man_MANS = man/guacd.8
 
-noinst_HEADERS = client.h log.h
-guacd_SOURCES  = daemon.c client.c log.c
-guacd_LDADD    = @LIBGUAC_LTLIB@
-guacd_LDFLAGS  = @PTHREAD_LIBS@ @SSL_LIBS@
+man_MANS =           \
+    man/guacd.8      \
+    man/guacd.conf.5
+
+noinst_HEADERS =  \
+    client.h      \
+    client-map.h  \
+    conf-args.h   \
+    conf-file.h   \
+    conf-parse.h  \
+    log.h
+
+guacd_SOURCES =   \
+    daemon.c      \
+    client.c      \
+    client-map.c  \
+    conf-args.c   \
+    conf-file.c   \
+    conf-parse.c  \
+    log.c
+
+guacd_CFLAGS =              \
+    -Werror -Wall -pedantic \
+    @COMMON_INCLUDE@        \
+    @LIBGUAC_INCLUDE@
+
+guacd_LDADD =       \
+    @COMMON_LTLIB@  \
+    @LIBGUAC_LTLIB@
+
+guacd_LDFLAGS =    \
+    @PTHREAD_LIBS@ \
+    @SSL_LIBS@
+
+EXTRA_DIST =         \
+    init.d/guacd.in  \
+    man/guacd.8      \
+    man/guacd.conf.5
 
-EXTRA_DIST = init.d/guacd.in man/guacd.8
 CLEANFILES = $(init_SCRIPTS)
 
 # SSL support
diff --git a/src/guacd/Makefile.in b/src/guacd/Makefile.in
index 9f25609..f85c003 100644
--- a/src/guacd/Makefile.in
+++ b/src/guacd/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.6 from Makefile.am.
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software
-# Foundation, Inc.
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
@@ -15,62 +14,76 @@
 
 @SET_MAKE@
 
-# ***** BEGIN LICENSE BLOCK *****
-# Version: MPL 1.1/GPL 2.0/LGPL 2.1
-#
-# The contents of this file are subject to the Mozilla Public License Version
-# 1.1 (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.mozilla.org/MPL/
-#
-# Software distributed under the License is distributed on an "AS IS" basis,
-# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-# for the specific language governing rights and limitations under the
-# License.
 #
-# The Original Code is guacd.
+# Copyright (C) 2015 Glyptodon LLC
 #
-# The Initial Developer of the Original Code is
-# Michael Jumper.
-# Portions created by the Initial Developer are Copyright (C) 2010
-# the Initial Developer. All Rights Reserved.
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
 #
-# Contributor(s):
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
 #
-# Alternatively, the contents of this file may be used under the terms of
-# either the GNU General Public License Version 2 or later (the "GPL"), or
-# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-# in which case the provisions of the GPL or the LGPL are applicable instead
-# of those above. If you wish to allow use of your version of this file only
-# under the terms of either the GPL or the LGPL, and not to allow others to
-# use your version of this file under the terms of the MPL, indicate your
-# decision by deleting the provisions above and replace them with the notice
-# and other provisions required by the GPL or the LGPL. If you do not delete
-# the provisions above, a recipient may use your version of this file under
-# the terms of any one of the MPL, the GPL or the LGPL.
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
 #
-# ***** END LICENSE BLOCK *****
 
 
 
 VPATH = @srcdir@
-am__make_dryrun = \
-  { \
-    am__dry=no; \
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
     case $$MAKEFLAGS in \
       *\\[\ \	]*) \
-        echo 'am--echo: ; @echo "AM"  OK' | $(MAKE) -f - 2>/dev/null \
-          | grep '^AM OK$$' >/dev/null || am__dry=yes;; \
-      *) \
-        for am__flg in $$MAKEFLAGS; do \
-          case $$am__flg in \
-            *=*|--*) ;; \
-            *n*) am__dry=yes; break;; \
-          esac; \
-        done;; \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
     esac; \
-    test $$am__dry = yes; \
-  }
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
 pkgdatadir = $(datadir)/@PACKAGE@
 pkgincludedir = $(includedir)/@PACKAGE@
 pkglibdir = $(libdir)/@PACKAGE@
@@ -95,8 +108,8 @@ sbin_PROGRAMS = guacd$(EXEEXT)
 @ENABLE_SSL_TRUE at am__append_1 = socket-ssl.h
 @ENABLE_SSL_TRUE at am__append_2 = socket-ssl.c
 subdir = src/guacd
-DIST_COMMON = $(am__noinst_HEADERS_DIST) $(srcdir)/Makefile.am \
-	$(srcdir)/Makefile.in
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(top_srcdir)/depcomp $(am__noinst_HEADERS_DIST)
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
 	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
@@ -105,20 +118,28 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
 CONFIG_CLEAN_FILES =
 CONFIG_CLEAN_VPATH_FILES =
 am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(initdir)" \
-	"$(DESTDIR)$(man8dir)"
+	"$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)"
 PROGRAMS = $(sbin_PROGRAMS)
-am__guacd_SOURCES_DIST = daemon.c client.c log.c socket-ssl.c
- at ENABLE_SSL_TRUE@am__objects_1 = socket-ssl.$(OBJEXT)
-am_guacd_OBJECTS = daemon.$(OBJEXT) client.$(OBJEXT) log.$(OBJEXT) \
-	$(am__objects_1)
+am__guacd_SOURCES_DIST = daemon.c client.c client-map.c conf-args.c \
+	conf-file.c conf-parse.c log.c socket-ssl.c
+ at ENABLE_SSL_TRUE@am__objects_1 = guacd-socket-ssl.$(OBJEXT)
+am_guacd_OBJECTS = guacd-daemon.$(OBJEXT) guacd-client.$(OBJEXT) \
+	guacd-client-map.$(OBJEXT) guacd-conf-args.$(OBJEXT) \
+	guacd-conf-file.$(OBJEXT) guacd-conf-parse.$(OBJEXT) \
+	guacd-log.$(OBJEXT) $(am__objects_1)
 guacd_OBJECTS = $(am_guacd_OBJECTS)
 guacd_DEPENDENCIES =
-guacd_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
-	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(guacd_LDFLAGS) \
-	$(LDFLAGS) -o $@
+AM_V_lt = $(am__v_lt_ at AM_V@)
+am__v_lt_ = $(am__v_lt_ at AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+guacd_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(guacd_CFLAGS) $(CFLAGS) \
+	$(guacd_LDFLAGS) $(LDFLAGS) -o $@
 am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
 am__vpath_adj = case $$p in \
     $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
@@ -147,19 +168,40 @@ am__uninstall_files_from_dir = { \
          $(am__cd) "$$dir" && rm -f $$files; }; \
   }
 SCRIPTS = $(init_SCRIPTS)
-DEFAULT_INCLUDES = -I. at am__isrc@
+AM_V_P = $(am__v_P_ at AM_V@)
+am__v_P_ = $(am__v_P_ at AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_ at AM_V@)
+am__v_GEN_ = $(am__v_GEN_ at AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_ at AM_V@)
+am__v_at_ = $(am__v_at_ at AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I. at am__isrc@ -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/depcomp
 am__depfiles_maybe = depfiles
 am__mv = mv -f
 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
 	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
-	--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
-	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_ at AM_V@)
+am__v_CC_ = $(am__v_CC_ at AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
 CCLD = $(CC)
-LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
-	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
-	$(LDFLAGS) -o $@
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_ at AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_ at AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
 SOURCES = $(guacd_SOURCES)
 DIST_SOURCES = $(am__guacd_SOURCES_DIST)
 am__can_run_installinfo = \
@@ -167,16 +209,36 @@ am__can_run_installinfo = \
     n|no|NO) false;; \
     *) (install-info --version) >/dev/null 2>&1;; \
   esac
+man5dir = $(mandir)/man5
 man8dir = $(mandir)/man8
 NROFF = nroff
 MANS = $(man_MANS)
-am__noinst_HEADERS_DIST = client.h log.h socket-ssl.h
+am__noinst_HEADERS_DIST = client.h client-map.h conf-args.h \
+	conf-file.h conf-parse.h log.h socket-ssl.h
 HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
 ETAGS = etags
 CTAGS = ctags
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 ACLOCAL = @ACLOCAL@
 AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -186,6 +248,10 @@ CAIRO_LIBS = @CAIRO_LIBS@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+COMMON_INCLUDE = @COMMON_INCLUDE@
+COMMON_LTLIB = @COMMON_LTLIB@
+COMMON_SSH_INCLUDE = @COMMON_SSH_INCLUDE@
+COMMON_SSH_LTLIB = @COMMON_SSH_LTLIB@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
 CUNIT_LIBS = @CUNIT_LIBS@
@@ -193,7 +259,6 @@ CYGPATH_W = @CYGPATH_W@
 DEFS = @DEFS@
 DEPDIR = @DEPDIR@
 DLLTOOL = @DLLTOOL@
-DL_LIBS = @DL_LIBS@
 DSYMUTIL = @DSYMUTIL@
 DUMPBIN = @DUMPBIN@
 ECHO_C = @ECHO_C@
@@ -208,8 +273,10 @@ INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
 INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+JPEG_LIBS = @JPEG_LIBS@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
+LIBADD_DLOPEN = @LIBADD_DLOPEN@
 LIBGUAC_INCLUDE = @LIBGUAC_INCLUDE@
 LIBGUAC_LTLIB = @LIBGUAC_LTLIB@
 LIBOBJS = @LIBOBJS@
@@ -220,6 +287,7 @@ LN_S = @LN_S@
 LTLIBOBJS = @LTLIBOBJS@
 MAKEINFO = @MAKEINFO@
 MANIFEST_TOOL = @MANIFEST_TOOL@
+MATH_LIBS = @MATH_LIBS@
 MKDIR_P = @MKDIR_P@
 NM = @NM@
 NMEDIT = @NMEDIT@
@@ -253,9 +321,14 @@ SHELL = @SHELL@
 SSH_LIBS = @SSH_LIBS@
 SSL_LIBS = @SSL_LIBS@
 STRIP = @STRIP@
+TELNET_LIBS = @TELNET_LIBS@
+TERMINAL_INCLUDE = @TERMINAL_INCLUDE@
+TERMINAL_LTLIB = @TERMINAL_LTLIB@
+UUID_LIBS = @UUID_LIBS@
 VERSION = @VERSION@
 VNC_LIBS = @VNC_LIBS@
 VORBIS_LIBS = @VORBIS_LIBS@
+WEBP_LIBS = @WEBP_LIBS@
 abs_builddir = @abs_builddir@
 abs_srcdir = @abs_srcdir@
 abs_top_builddir = @abs_top_builddir@
@@ -310,13 +383,32 @@ top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
 AUTOMAKE_OPTIONS = foreign 
-AM_CFLAGS = -Werror -Wall -pedantic @LIBGUAC_INCLUDE@
-man_MANS = man/guacd.8
-noinst_HEADERS = client.h log.h $(am__append_1)
-guacd_SOURCES = daemon.c client.c log.c $(am__append_2)
-guacd_LDADD = @LIBGUAC_LTLIB@
-guacd_LDFLAGS = @PTHREAD_LIBS@ @SSL_LIBS@
-EXTRA_DIST = init.d/guacd.in man/guacd.8
+man_MANS = \
+    man/guacd.8      \
+    man/guacd.conf.5
+
+noinst_HEADERS = client.h client-map.h conf-args.h conf-file.h \
+	conf-parse.h log.h $(am__append_1)
+guacd_SOURCES = daemon.c client.c client-map.c conf-args.c conf-file.c \
+	conf-parse.c log.c $(am__append_2)
+guacd_CFLAGS = \
+    -Werror -Wall -pedantic \
+    @COMMON_INCLUDE@        \
+    @LIBGUAC_INCLUDE@
+
+guacd_LDADD = \
+    @COMMON_LTLIB@  \
+    @LIBGUAC_LTLIB@
+
+guacd_LDFLAGS = \
+    @PTHREAD_LIBS@ \
+    @SSL_LIBS@
+
+EXTRA_DIST = \
+    init.d/guacd.in  \
+    man/guacd.8      \
+    man/guacd.conf.5
+
 CLEANFILES = $(init_SCRIPTS)
 
 # Init script
@@ -365,10 +457,12 @@ install-sbinPROGRAMS: $(sbin_PROGRAMS)
 	fi; \
 	for p in $$list; do echo "$$p $$p"; done | \
 	sed 's/$(EXEEXT)$$//' | \
-	while read p p1; do if test -f $$p || test -f $$p1; \
-	  then echo "$$p"; echo "$$p"; else :; fi; \
+	while read p p1; do if test -f $$p \
+	 || test -f $$p1 \
+	  ; then echo "$$p"; echo "$$p"; else :; fi; \
 	done | \
-	sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+	sed -e 'p;s,.*/,,;n;h' \
+	    -e 's|.*|.|' \
 	    -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
 	sed 'N;N;N;s,\n, ,g' | \
 	$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
@@ -389,7 +483,8 @@ uninstall-sbinPROGRAMS:
 	@list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
 	files=`for p in $$list; do echo "$$p"; done | \
 	  sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
-	      -e 's/$$/$(EXEEXT)/' `; \
+	      -e 's/$$/$(EXEEXT)/' \
+	`; \
 	test -n "$$list" || exit 0; \
 	echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
 	cd "$(DESTDIR)$(sbindir)" && rm -f $$files
@@ -402,9 +497,10 @@ clean-sbinPROGRAMS:
 	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
 	echo " rm -f" $$list; \
 	rm -f $$list
+
 guacd$(EXEEXT): $(guacd_OBJECTS) $(guacd_DEPENDENCIES) $(EXTRA_guacd_DEPENDENCIES) 
 	@rm -f guacd$(EXEEXT)
-	$(guacd_LINK) $(guacd_OBJECTS) $(guacd_LDADD) $(LIBS)
+	$(AM_V_CCLD)$(guacd_LINK) $(guacd_OBJECTS) $(guacd_LDADD) $(LIBS)
 install-initSCRIPTS: $(init_SCRIPTS)
 	@$(NORMAL_INSTALL)
 	@list='$(init_SCRIPTS)'; test -n "$(initdir)" || list=; \
@@ -447,37 +543,199 @@ mostlyclean-compile:
 distclean-compile:
 	-rm -f *.tab.c
 
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/client.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/daemon.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/log.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/socket-ssl.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/guacd-client-map.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/guacd-client.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/guacd-conf-args.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/guacd-conf-file.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/guacd-conf-parse.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/guacd-daemon.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/guacd-log.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/guacd-socket-ssl.Po at am__quote@
 
 .c.o:
- at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+ at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(COMPILE) -c $<
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(COMPILE) -c -o $@ $<
 
 .c.obj:
- at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+ at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(COMPILE) -c `$(CYGPATH_W) '$<'`
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
 
 .c.lo:
- at am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+ at am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+guacd-daemon.o: daemon.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -MT guacd-daemon.o -MD -MP -MF $(DEPDIR)/guacd-daemon.Tpo -c -o guacd-daemon.o `test -f 'daemon.c' || echo '$(srcdir)/'`daemon.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/guacd-daemon.Tpo $(DEPDIR)/guacd-daemon.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='daemon.c' object='guacd-daemon.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -c -o guacd-daemon.o `test -f 'daemon.c' || echo '$(srcdir)/'`daemon.c
+
+guacd-daemon.obj: daemon.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -MT guacd-daemon.obj -MD -MP -MF $(DEPDIR)/guacd-daemon.Tpo -c -o guacd-daemon.obj `if test -f 'daemon.c'; then $(CYGPATH_W) 'daemon.c'; else $(CYGPATH_W) '$(srcdir)/daemon.c'; fi`
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/guacd-daemon.Tpo $(DEPDIR)/guacd-daemon.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='daemon.c' object='guacd-daemon.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -c -o guacd-daemon.obj `if test -f 'daemon.c'; then $(CYGPATH_W) 'daemon.c'; else $(CYGPATH_W) '$(srcdir)/daemon.c'; fi`
+
+guacd-client.o: client.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -MT guacd-client.o -MD -MP -MF $(DEPDIR)/guacd-client.Tpo -c -o guacd-client.o `test -f 'client.c' || echo '$(srcdir)/'`client.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/guacd-client.Tpo $(DEPDIR)/guacd-client.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='client.c' object='guacd-client.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -c -o guacd-client.o `test -f 'client.c' || echo '$(srcdir)/'`client.c
+
+guacd-client.obj: client.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -MT guacd-client.obj -MD -MP -MF $(DEPDIR)/guacd-client.Tpo -c -o guacd-client.obj `if test -f 'client.c'; then $(CYGPATH_W) 'client.c'; else $(CYGPATH_W) '$(srcdir)/client.c'; fi`
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/guacd-client.Tpo $(DEPDIR)/guacd-client.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='client.c' object='guacd-client.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -c -o guacd-client.obj `if test -f 'client.c'; then $(CYGPATH_W) 'client.c'; else $(CYGPATH_W) '$(srcdir)/client.c'; fi`
+
+guacd-client-map.o: client-map.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -MT guacd-client-map.o -MD -MP -MF $(DEPDIR)/guacd-client-map.Tpo -c -o guacd-client-map.o `test -f 'client-map.c' || echo '$(srcdir)/'`client-map.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/guacd-client-map.Tpo $(DEPDIR)/guacd-client-map.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='client-map.c' object='guacd-client-map.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -c -o guacd-client-map.o `test -f 'client-map.c' || echo '$(srcdir)/'`client-map.c
+
+guacd-client-map.obj: client-map.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -MT guacd-client-map.obj -MD -MP -MF $(DEPDIR)/guacd-client-map.Tpo -c -o guacd-client-map.obj `if test -f 'client-map.c'; then $(CYGPATH_W) 'client-map.c'; else $(CYGPATH_W) '$(srcdir)/client-map.c'; fi`
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/guacd-client-map.Tpo $(DEPDIR)/guacd-client-map.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='client-map.c' object='guacd-client-map.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -c -o guacd-client-map.obj `if test -f 'client-map.c'; then $(CYGPATH_W) 'client-map.c'; else $(CYGPATH_W) '$(srcdir)/client-map.c'; fi`
+
+guacd-conf-args.o: conf-args.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -MT guacd-conf-args.o -MD -MP -MF $(DEPDIR)/guacd-conf-args.Tpo -c -o guacd-conf-args.o `test -f 'conf-args.c' || echo '$(srcdir)/'`conf-args.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/guacd-conf-args.Tpo $(DEPDIR)/guacd-conf-args.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='conf-args.c' object='guacd-conf-args.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -c -o guacd-conf-args.o `test -f 'conf-args.c' || echo '$(srcdir)/'`conf-args.c
+
+guacd-conf-args.obj: conf-args.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -MT guacd-conf-args.obj -MD -MP -MF $(DEPDIR)/guacd-conf-args.Tpo -c -o guacd-conf-args.obj `if test -f 'conf-args.c'; then $(CYGPATH_W) 'conf-args.c'; else $(CYGPATH_W) '$(srcdir)/conf-args.c'; fi`
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/guacd-conf-args.Tpo $(DEPDIR)/guacd-conf-args.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='conf-args.c' object='guacd-conf-args.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -c -o guacd-conf-args.obj `if test -f 'conf-args.c'; then $(CYGPATH_W) 'conf-args.c'; else $(CYGPATH_W) '$(srcdir)/conf-args.c'; fi`
+
+guacd-conf-file.o: conf-file.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -MT guacd-conf-file.o -MD -MP -MF $(DEPDIR)/guacd-conf-file.Tpo -c -o guacd-conf-file.o `test -f 'conf-file.c' || echo '$(srcdir)/'`conf-file.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/guacd-conf-file.Tpo $(DEPDIR)/guacd-conf-file.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='conf-file.c' object='guacd-conf-file.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -c -o guacd-conf-file.o `test -f 'conf-file.c' || echo '$(srcdir)/'`conf-file.c
+
+guacd-conf-file.obj: conf-file.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -MT guacd-conf-file.obj -MD -MP -MF $(DEPDIR)/guacd-conf-file.Tpo -c -o guacd-conf-file.obj `if test -f 'conf-file.c'; then $(CYGPATH_W) 'conf-file.c'; else $(CYGPATH_W) '$(srcdir)/conf-file.c'; fi`
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/guacd-conf-file.Tpo $(DEPDIR)/guacd-conf-file.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='conf-file.c' object='guacd-conf-file.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -c -o guacd-conf-file.obj `if test -f 'conf-file.c'; then $(CYGPATH_W) 'conf-file.c'; else $(CYGPATH_W) '$(srcdir)/conf-file.c'; fi`
+
+guacd-conf-parse.o: conf-parse.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -MT guacd-conf-parse.o -MD -MP -MF $(DEPDIR)/guacd-conf-parse.Tpo -c -o guacd-conf-parse.o `test -f 'conf-parse.c' || echo '$(srcdir)/'`conf-parse.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/guacd-conf-parse.Tpo $(DEPDIR)/guacd-conf-parse.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='conf-parse.c' object='guacd-conf-parse.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -c -o guacd-conf-parse.o `test -f 'conf-parse.c' || echo '$(srcdir)/'`conf-parse.c
+
+guacd-conf-parse.obj: conf-parse.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -MT guacd-conf-parse.obj -MD -MP -MF $(DEPDIR)/guacd-conf-parse.Tpo -c -o guacd-conf-parse.obj `if test -f 'conf-parse.c'; then $(CYGPATH_W) 'conf-parse.c'; else $(CYGPATH_W) '$(srcdir)/conf-parse.c'; fi`
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/guacd-conf-parse.Tpo $(DEPDIR)/guacd-conf-parse.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='conf-parse.c' object='guacd-conf-parse.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -c -o guacd-conf-parse.obj `if test -f 'conf-parse.c'; then $(CYGPATH_W) 'conf-parse.c'; else $(CYGPATH_W) '$(srcdir)/conf-parse.c'; fi`
+
+guacd-log.o: log.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -MT guacd-log.o -MD -MP -MF $(DEPDIR)/guacd-log.Tpo -c -o guacd-log.o `test -f 'log.c' || echo '$(srcdir)/'`log.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/guacd-log.Tpo $(DEPDIR)/guacd-log.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='log.c' object='guacd-log.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -c -o guacd-log.o `test -f 'log.c' || echo '$(srcdir)/'`log.c
+
+guacd-log.obj: log.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -MT guacd-log.obj -MD -MP -MF $(DEPDIR)/guacd-log.Tpo -c -o guacd-log.obj `if test -f 'log.c'; then $(CYGPATH_W) 'log.c'; else $(CYGPATH_W) '$(srcdir)/log.c'; fi`
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/guacd-log.Tpo $(DEPDIR)/guacd-log.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='log.c' object='guacd-log.obj' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -c -o guacd-log.obj `if test -f 'log.c'; then $(CYGPATH_W) 'log.c'; else $(CYGPATH_W) '$(srcdir)/log.c'; fi`
+
+guacd-socket-ssl.o: socket-ssl.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -MT guacd-socket-ssl.o -MD -MP -MF $(DEPDIR)/guacd-socket-ssl.Tpo -c -o guacd-socket-ssl.o `test -f 'socket-ssl.c' || echo '$(srcdir)/'`socket-ssl.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/guacd-socket-ssl.Tpo $(DEPDIR)/guacd-socket-ssl.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='socket-ssl.c' object='guacd-socket-ssl.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -c -o guacd-socket-ssl.o `test -f 'socket-ssl.c' || echo '$(srcdir)/'`socket-ssl.c
+
+guacd-socket-ssl.obj: socket-ssl.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -MT guacd-socket-ssl.obj -MD -MP -MF $(DEPDIR)/guacd-socket-ssl.Tpo -c -o guacd-socket-ssl.obj `if test -f 'socket-ssl.c'; then $(CYGPATH_W) 'socket-ssl.c'; else $(CYGPATH_W) '$(srcdir)/socket-ssl.c'; fi`
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/guacd-socket-ssl.Tpo $(DEPDIR)/guacd-socket-ssl.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='socket-ssl.c' object='guacd-socket-ssl.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacd_CFLAGS) $(CFLAGS) -c -o guacd-socket-ssl.obj `if test -f 'socket-ssl.c'; then $(CYGPATH_W) 'socket-ssl.c'; else $(CYGPATH_W) '$(srcdir)/socket-ssl.c'; fi`
 
 mostlyclean-libtool:
 	-rm -f *.lo
 
 clean-libtool:
 	-rm -rf .libs _libs
+install-man5: $(man_MANS)
+	@$(NORMAL_INSTALL)
+	@list1=''; \
+	list2='$(man_MANS)'; \
+	test -n "$(man5dir)" \
+	  && test -n "`echo $$list1$$list2`" \
+	  || exit 0; \
+	echo " $(MKDIR_P) '$(DESTDIR)$(man5dir)'"; \
+	$(MKDIR_P) "$(DESTDIR)$(man5dir)" || exit 1; \
+	{ for i in $$list1; do echo "$$i"; done;  \
+	if test -n "$$list2"; then \
+	  for i in $$list2; do echo "$$i"; done \
+	    | sed -n '/\.5[a-z]*$$/p'; \
+	fi; \
+	} | while read p; do \
+	  if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; echo "$$p"; \
+	done | \
+	sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \
+	      -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+	sed 'N;N;s,\n, ,g' | { \
+	list=; while read file base inst; do \
+	  if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+	    echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man5dir)/$$inst'"; \
+	    $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$inst" || exit $$?; \
+	  fi; \
+	done; \
+	for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+	while read files; do \
+	  test -z "$$files" || { \
+	    echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man5dir)'"; \
+	    $(INSTALL_DATA) $$files "$(DESTDIR)$(man5dir)" || exit $$?; }; \
+	done; }
+
+uninstall-man5:
+	@$(NORMAL_UNINSTALL)
+	@list=''; test -n "$(man5dir)" || exit 0; \
+	files=`{ for i in $$list; do echo "$$i"; done; \
+	l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+	  sed -n '/\.5[a-z]*$$/p'; \
+	} | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \
+	      -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+	dir='$(DESTDIR)$(man5dir)'; $(am__uninstall_files_from_dir)
 install-man8: $(man_MANS)
 	@$(NORMAL_INSTALL)
 	@list1=''; \
@@ -522,26 +780,15 @@ uninstall-man8:
 	      -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
 	dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
 
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
-	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
-	unique=`for i in $$list; do \
-	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-	  done | \
-	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
-	      END { if (nonempty) { for (i in files) print i; }; }'`; \
-	mkid -fID $$unique
-tags: TAGS
-
-TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
-		$(TAGS_FILES) $(LISP)
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
 	set x; \
 	here=`pwd`; \
-	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
-	unique=`for i in $$list; do \
-	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-	  done | \
-	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
-	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	$(am__define_uniq_tagged_files); \
 	shift; \
 	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
 	  test -n "$$unique" || unique=$$empty_fix; \
@@ -553,15 +800,11 @@ TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
 	      $$unique; \
 	  fi; \
 	fi
-ctags: CTAGS
-CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
-		$(TAGS_FILES) $(LISP)
-	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
-	unique=`for i in $$list; do \
-	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-	  done | \
-	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
-	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
 	test -z "$(CTAGS_ARGS)$$unique" \
 	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
 	     $$unique
@@ -570,24 +813,26 @@ GTAGS:
 	here=`$(am__cd) $(top_builddir) && pwd` \
 	  && $(am__cd) $(top_srcdir) \
 	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
 
 distclean-tags:
 	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
 
 distdir: $(DISTFILES)
-	@list='$(MANS)'; if test -n "$$list"; then \
-	  list=`for p in $$list; do \
-	    if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
-	    if test -f "$$d$$p"; then echo "$$d$$p"; else :; fi; done`; \
-	  if test -n "$$list" && \
-	    grep 'ab help2man is required to generate this page' $$list >/dev/null; then \
-	    echo "error: found man pages containing the \`missing help2man' replacement text:" >&2; \
-	    grep -l 'ab help2man is required to generate this page' $$list | sed 's/^/         /' >&2; \
-	    echo "       to fix them, install help2man, remove and regenerate the man pages;" >&2; \
-	    echo "       typically \`make maintainer-clean' will remove them" >&2; \
-	    exit 1; \
-	  else :; fi; \
-	else :; fi
 	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
 	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
 	list='$(DISTFILES)'; \
@@ -621,7 +866,7 @@ check-am: all-am
 check: check-am
 all-am: Makefile $(PROGRAMS) $(SCRIPTS) $(MANS) $(HEADERS)
 installdirs:
-	for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(initdir)" "$(DESTDIR)$(man8dir)"; do \
+	for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(initdir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)"; do \
 	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
 	done
 install: install-am
@@ -694,7 +939,7 @@ install-info: install-info-am
 
 install-info-am:
 
-install-man: install-man8
+install-man: install-man5 install-man8
 
 install-pdf: install-pdf-am
 
@@ -727,24 +972,25 @@ ps-am:
 uninstall-am: uninstall-initSCRIPTS uninstall-man \
 	uninstall-sbinPROGRAMS
 
-uninstall-man: uninstall-man8
+uninstall-man: uninstall-man5 uninstall-man8
 
 .MAKE: install-am install-strip
 
-.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
-	clean-libtool clean-sbinPROGRAMS ctags distclean \
-	distclean-compile distclean-generic distclean-libtool \
-	distclean-tags distdir dvi dvi-am html html-am info info-am \
-	install install-am install-data install-data-am install-dvi \
-	install-dvi-am install-exec install-exec-am install-html \
-	install-html-am install-info install-info-am \
-	install-initSCRIPTS install-man install-man8 install-pdf \
-	install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \
-	install-strip installcheck installcheck-am installdirs \
-	maintainer-clean maintainer-clean-generic mostlyclean \
-	mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
-	pdf pdf-am ps ps-am tags uninstall uninstall-am \
-	uninstall-initSCRIPTS uninstall-man uninstall-man8 \
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
+	clean-libtool clean-sbinPROGRAMS cscopelist-am ctags ctags-am \
+	distclean distclean-compile distclean-generic \
+	distclean-libtool distclean-tags distdir dvi dvi-am html \
+	html-am info info-am install install-am install-data \
+	install-data-am install-dvi install-dvi-am install-exec \
+	install-exec-am install-html install-html-am install-info \
+	install-info-am install-initSCRIPTS install-man install-man5 \
+	install-man8 install-pdf install-pdf-am install-ps \
+	install-ps-am install-sbinPROGRAMS install-strip installcheck \
+	installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+	tags tags-am uninstall uninstall-am uninstall-initSCRIPTS \
+	uninstall-man uninstall-man5 uninstall-man8 \
 	uninstall-sbinPROGRAMS
 
 
diff --git a/src/guacd/client-map.c b/src/guacd/client-map.c
new file mode 100644
index 0000000..a13478a
--- /dev/null
+++ b/src/guacd/client-map.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+#include "client.h"
+#include "client-map.h"
+#include "guac_list.h"
+
+#include <guacamole/client.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * Returns a hash code based on the connection ID of the given client.
+ */
+static unsigned int __guacd_client_hash(const char* str) {
+
+    unsigned int hash_value = 0;
+    int c;
+
+    /* Apply each character in string to the hash code */
+    while ((c = *(str++)))
+        hash_value = hash_value * 65599 + c;
+
+    return hash_value;
+
+}
+
+/**
+ * Locates the bucket corresponding to the hash code indicated by the give id.
+ * Each bucket is an instance of guac_common_list.
+ */
+static guac_common_list* __guacd_client_find_bucket(guacd_client_map* map, const char* id) {
+
+    const int index = __guacd_client_hash(id) % GUACD_CLIENT_MAP_BUCKETS;
+    return map->__buckets[index];
+
+}
+
+/**
+ * Given a list of guac_client instances, returns the guac_client having the
+ * given ID, or NULL if no such client is stored.
+ */
+static guac_common_list_element* __guacd_client_find(guac_common_list* bucket, const char* id) {
+
+    guac_common_list_element* current = bucket->head;
+
+    /* Search for matching element within bucket */
+    while (current != NULL) {
+
+        /* Check connection ID */
+        guac_client* client = (guac_client*) current->data;
+        if (strcmp(client->connection_id, id) == 0)
+            break;
+
+        current = current->next;
+    }
+
+    return current;
+
+}
+
+guacd_client_map* guacd_client_map_alloc() {
+
+    guacd_client_map* map = malloc(sizeof(guacd_client_map));
+    guac_common_list** current;
+
+    int i;
+
+    /* Init all buckets */
+    current = map->__buckets;
+
+    for (i=0; i<GUACD_CLIENT_MAP_BUCKETS; i++) {
+        *current = guac_common_list_alloc();
+        current++;
+    }
+
+    return map;
+
+}
+
+int guacd_client_map_add(guacd_client_map* map, guac_client* client) {
+
+    guac_common_list* bucket = __guacd_client_find_bucket(map, client->connection_id);
+    guac_common_list_element* found;
+
+    /* Retrieve corresponding element, if any */
+    guac_common_list_lock(bucket);
+    found = __guacd_client_find(bucket, client->connection_id);
+
+    /* If no such element, we can add the new client successfully */
+    if (found == NULL) {
+        guac_common_list_add(bucket, client);
+        guac_common_list_unlock(bucket);
+        return 0;
+    }
+
+    /* Otherwise, fail - already exists */
+    guac_common_list_unlock(bucket);
+    return 1;
+
+}
+
+guac_client* guacd_client_map_retrieve(guacd_client_map* map, const char* id) {
+
+    guac_client* client;
+
+    guac_common_list* bucket = __guacd_client_find_bucket(map, id);
+    guac_common_list_element* found;
+
+    /* Retrieve corresponding element, if any */
+    guac_common_list_lock(bucket);
+    found = __guacd_client_find(bucket, id);
+
+    /* If no such element, fail */
+    if (found == NULL) {
+        guac_common_list_unlock(bucket);
+        return NULL;
+    }
+
+    client = (guac_client*) found->data;
+
+    guac_common_list_unlock(bucket);
+    return client;
+
+}
+
+guac_client* guacd_client_map_remove(guacd_client_map* map, const char* id) {
+
+    guac_client* client;
+
+    guac_common_list* bucket = __guacd_client_find_bucket(map, id);
+    guac_common_list_element* found;
+
+    /* Retrieve corresponding element, if any */
+    guac_common_list_lock(bucket);
+    found = __guacd_client_find(bucket, id);
+
+    /* If no such element, fail */
+    if (found == NULL) {
+        guac_common_list_unlock(bucket);
+        return NULL;
+    }
+
+    client = (guac_client*) found->data;
+    guac_common_list_remove(bucket, found);
+
+    guac_common_list_unlock(bucket);
+    return client;
+
+}
+
diff --git a/src/guacd/client-map.h b/src/guacd/client-map.h
new file mode 100644
index 0000000..8544bc4
--- /dev/null
+++ b/src/guacd/client-map.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef _GUACD_CLIENT_MAP_H
+#define _GUACD_CLIENT_MAP_H
+
+#include "config.h"
+#include "client.h"
+#include "guac_list.h"
+
+#include <guacamole/client.h>
+
+#define GUACD_CLIENT_MAP_BUCKETS GUACD_CLIENT_MAX_CONNECTIONS*2
+
+/**
+ * Set of all active connections to guacd, indexed by connection ID.
+ */
+typedef struct guacd_client_map {
+
+    /**
+     * Internal hash buckets. Each bucket is a linked list containing all
+     * guac_client instances which hash to this bucket location.
+     */
+    guac_common_list* __buckets[GUACD_CLIENT_MAP_BUCKETS];
+
+} guacd_client_map;
+
+/**
+ * Allocates a new client map, which persists for the life of guacd.
+ */
+guacd_client_map* guacd_client_map_alloc();
+
+/**
+ * Adds the given client to the given client map. On success, zero is returned.
+ * If adding the client fails (due to lack of space, or duplicate ID), a
+ * non-zero value is returned instead.
+ */
+int guacd_client_map_add(guacd_client_map* map, guac_client* client);
+
+/**
+ * Retrieves the client having the given ID, or NULL if no such client
+ * is stored.
+ */
+guac_client* guacd_client_map_retrieve(guacd_client_map* map, const char* id);
+
+/**
+ * Removes the client having the given ID, returning the corresponding client.
+ * If no such client exists, NULL is returned.
+ */
+guac_client* guacd_client_map_remove(guacd_client_map* map, const char* id);
+
+#endif
+
diff --git a/src/guacd/client.c b/src/guacd/client.c
index 82debe8..075887c 100644
--- a/src/guacd/client.c
+++ b/src/guacd/client.c
@@ -1,52 +1,40 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is guacd.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <stdlib.h>
-#include <time.h>
-#include <pthread.h>
+#include "config.h"
+
+#include "client.h"
+#include "log.h"
 
-#include <guacamole/socket.h>
 #include <guacamole/client.h>
 #include <guacamole/error.h>
+#include <guacamole/instruction.h>
 #include <guacamole/protocol.h>
+#include <guacamole/socket.h>
 #include <guacamole/timestamp.h>
 
-#include "client.h"
-#include "log.h"
+#include <pthread.h>
+#include <stdlib.h>
+#include <time.h>
 
 /**
  * Sleep for the given number of milliseconds.
@@ -69,36 +57,12 @@ void* __guacd_client_output_thread(void* data) {
     guac_client* client = (guac_client*) data;
     guac_socket* socket = client->socket;
 
-    guac_timestamp last_ping_timestamp = guac_timestamp_current();
+    guac_client_log(client, GUAC_LOG_DEBUG,
+            "Starting output thread.");
 
     /* Guacamole client output loop */
     while (client->state == GUAC_CLIENT_RUNNING) {
 
-        /* Occasionally ping client with repeat of last sync */
-        guac_timestamp timestamp = guac_timestamp_current();
-        if (timestamp - last_ping_timestamp > GUACD_SYNC_FREQUENCY) {
-
-            /* Record time of last synnc */
-            last_ping_timestamp = timestamp;
-
-            /* Send sync */
-            if (guac_protocol_send_sync(socket, client->last_sent_timestamp)) {
-                guacd_client_log_guac_error(client,
-                        "Error sending \"sync\" instruction");
-                guac_client_stop(client);
-                return NULL;
-            }
-
-            /* Flush */
-            if (guac_socket_flush(socket)) {
-                guacd_client_log_guac_error(client,
-                        "Error flushing output");
-                guac_client_stop(client);
-                return NULL;
-            }
-
-        }
-
         /* Handle server messages */
         if (client->handle_messages) {
 
@@ -108,7 +72,7 @@ void* __guacd_client_output_thread(void* data) {
 
                 int retval = client->handle_messages(client);
                 if (retval) {
-                    guacd_client_log_guac_error(client,
+                    guacd_client_log_guac_error(client, GUAC_LOG_DEBUG,
                             "Error handling server messages");
                     guac_client_stop(client);
                     return NULL;
@@ -117,7 +81,7 @@ void* __guacd_client_output_thread(void* data) {
                 /* Send sync instruction */
                 client->last_sent_timestamp = guac_timestamp_current();
                 if (guac_protocol_send_sync(socket, client->last_sent_timestamp)) {
-                    guacd_client_log_guac_error(client, 
+                    guacd_client_log_guac_error(client, GUAC_LOG_DEBUG,
                             "Error sending \"sync\" instruction");
                     guac_client_stop(client);
                     return NULL;
@@ -125,7 +89,7 @@ void* __guacd_client_output_thread(void* data) {
 
                 /* Flush */
                 if (guac_socket_flush(socket)) {
-                    guacd_client_log_guac_error(client,
+                    guacd_client_log_guac_error(client, GUAC_LOG_DEBUG,
                             "Error flushing output");
                     guac_client_stop(client);
                     return NULL;
@@ -145,6 +109,9 @@ void* __guacd_client_output_thread(void* data) {
 
     } /* End of output loop */
 
+    guac_client_log(client, GUAC_LOG_DEBUG,
+            "Output thread terminated.");
+
     guac_client_stop(client);
     return NULL;
 
@@ -155,6 +122,9 @@ void* __guacd_client_input_thread(void* data) {
     guac_client* client = (guac_client*) data;
     guac_socket* socket = client->socket;
 
+    guac_client_log(client, GUAC_LOG_DEBUG,
+            "Starting input thread.");
+
     /* Guacamole client input loop */
     while (client->state == GUAC_CLIENT_RUNNING) {
 
@@ -164,9 +134,17 @@ void* __guacd_client_input_thread(void* data) {
 
         /* Stop on error */
         if (instruction == NULL) {
-            guacd_client_log_guac_error(client,
-                    "Error reading instruction");
-            guac_client_stop(client);
+
+            if (guac_error == GUAC_STATUS_TIMEOUT)
+                guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_TIMEOUT, "Client is not responding.");
+
+            else {
+                if (guac_error != GUAC_STATUS_CLOSED)
+                    guacd_client_log_guac_error(client, GUAC_LOG_WARNING,
+                            "Guacamole connection failure");
+                guac_client_stop(client);
+            }
+
             return NULL;
         }
 
@@ -179,11 +157,11 @@ void* __guacd_client_input_thread(void* data) {
         if (guac_client_handle_instruction(client, instruction) < 0) {
 
             /* Log error */
-            guacd_client_log_guac_error(client,
-                    "Client instruction handler error");
+            guacd_client_log_guac_error(client, GUAC_LOG_WARNING,
+                    "Connection aborted");
 
             /* Log handler details */
-            guac_client_log_info(client,
+            guac_client_log(client, GUAC_LOG_DEBUG,
                     "Failing instruction handler in client was \"%s\"",
                     instruction->opcode);
 
@@ -197,6 +175,9 @@ void* __guacd_client_input_thread(void* data) {
 
     }
 
+    guac_client_log(client, GUAC_LOG_DEBUG,
+            "Input thread terminated.");
+
     return NULL;
 
 }
@@ -206,12 +187,12 @@ int guacd_client_start(guac_client* client) {
     pthread_t input_thread, output_thread;
 
     if (pthread_create(&output_thread, NULL, __guacd_client_output_thread, (void*) client)) {
-        guac_client_log_error(client, "Unable to start output thread");
+        guac_client_log(client, GUAC_LOG_ERROR, "Unable to start output thread");
         return -1;
     }
 
     if (pthread_create(&input_thread, NULL, __guacd_client_input_thread, (void*) client)) {
-        guac_client_log_error(client, "Unable to start input thread");
+        guac_client_log(client, GUAC_LOG_ERROR, "Unable to start input thread");
         guac_client_stop(client);
         pthread_join(output_thread, NULL);
         return -1;
diff --git a/src/guacd/client.h b/src/guacd/client.h
index 25792bd..214af2a 100644
--- a/src/guacd/client.h
+++ b/src/guacd/client.h
@@ -1,44 +1,33 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is guacd.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
 
 #ifndef _GUACD_CLIENT_H
 #define _GUACD_CLIENT_H
 
+#include "config.h"
+
+#include <guacamole/client.h>
+
 /**
  * The time to allow between sync responses in milliseconds. If a sync
  * instruction is sent to the client and no response is received within this
@@ -65,7 +54,6 @@
  */
 #define GUACD_MESSAGE_HANDLE_FREQUENCY 50
 
-
 /**
  * The number of milliseconds to wait for messages in any phase before
  * timing out and closing the connection with an error.
@@ -79,6 +67,13 @@
  */
 #define GUACD_USEC_TIMEOUT (GUACD_TIMEOUT*1000)
 
+/**
+ * The maximum number of concurrent connections to a single instance
+ * of guacd.
+ */
+#define GUACD_CLIENT_MAX_CONNECTIONS 65536
+
 int guacd_client_start(guac_client* client);
 
 #endif
+
diff --git a/src/guacd/conf-args.c b/src/guacd/conf-args.c
new file mode 100644
index 0000000..51199f2
--- /dev/null
+++ b/src/guacd/conf-args.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "conf-args.h"
+#include "conf-file.h"
+#include "conf-parse.h"
+
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int guacd_conf_parse_args(guacd_config* config, int argc, char** argv) {
+
+    /* Parse arguments */
+    int opt;
+    while ((opt = getopt(argc, argv, "l:b:p:L:C:K:f")) != -1) {
+
+        /* -l: Bind port */
+        if (opt == 'l') {
+            free(config->bind_port);
+            config->bind_port = strdup(optarg);
+        }
+
+        /* -b: Bind host */
+        else if (opt == 'b') {
+            free(config->bind_host);
+            config->bind_host = strdup(optarg);
+        }
+
+        /* -f: Run in foreground */
+        else if (opt == 'f') {
+            config->foreground = 1;
+        }
+
+        /* -p: PID file */
+        else if (opt == 'p') {
+            free(config->pidfile);
+            config->pidfile = strdup(optarg);
+        }
+
+        /* -L: Log level */
+        else if (opt == 'L') {
+
+            /* Validate and parse log level */
+            int level = guacd_parse_log_level(optarg);
+            if (level == -1) {
+                fprintf(stderr, "Invalid log level. Valid levels are: \"debug\", \"info\", \"warning\", and \"error\".\n");
+                return 1;
+            }
+
+            config->max_log_level = level;
+
+        }
+
+#ifdef ENABLE_SSL
+        /* -C SSL certificate */
+        else if (opt == 'C') {
+            free(config->cert_file);
+            config->cert_file = strdup(optarg);
+        }
+
+        /* -K SSL key */
+        else if (opt == 'K') {
+            free(config->key_file);
+            config->key_file = strdup(optarg);
+        }
+#else
+        else if (opt == 'C' || opt == 'K') {
+            fprintf(stderr,
+                    "This guacd does not have SSL/TLS support compiled in.\n\n"
+
+                    "If you wish to enable support for the -%c option, please install libssl and\n"
+                    "recompile guacd.\n",
+                    opt);
+            return 1;
+        }
+#endif
+        else {
+
+            fprintf(stderr, "USAGE: %s"
+                    " [-l LISTENPORT]"
+                    " [-b LISTENADDRESS]"
+                    " [-p PIDFILE]"
+                    " [-L LEVEL]"
+#ifdef ENABLE_SSL
+                    " [-C CERTIFICATE_FILE]"
+                    " [-K PEM_FILE]"
+#endif
+                    " [-f]\n", argv[0]);
+
+            return 1;
+        }
+    }
+
+    /* Success */
+    return 0;
+
+}
+
diff --git a/src/guacd/conf-args.h b/src/guacd/conf-args.h
new file mode 100644
index 0000000..6cb97d4
--- /dev/null
+++ b/src/guacd/conf-args.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _GUACD_CONF_ARGS_H
+#define _GUACD_CONF_ARGS_H
+
+#include "config.h"
+
+#include "conf-file.h"
+
+/**
+ * Parses the given arguments into the given configuration. Zero is returned on
+ * success, and non-zero is returned if arguments cannot be parsed.
+ */
+int guacd_conf_parse_args(guacd_config* config, int argc, char** argv);
+
+#endif
+
diff --git a/src/guacd/conf-file.c b/src/guacd/conf-file.c
new file mode 100644
index 0000000..102db9c
--- /dev/null
+++ b/src/guacd/conf-file.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "conf-file.h"
+#include "conf-parse.h"
+
+#include <guacamole/client.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/**
+ * Updates the configuration with the given parameter/value pair, flagging
+ * errors as necessary.
+ */
+static int guacd_conf_callback(const char* section, const char* param, const char* value, void* data) {
+
+    guacd_config* config = (guacd_config*) data;
+
+    /* Network server options */
+    if (strcmp(section, "server") == 0) {
+
+        /* Bind host */
+        if (strcmp(param, "bind_host") == 0) {
+            free(config->bind_host);
+            config->bind_host = strdup(value);
+            return 0;
+        }
+
+        /* Bind port */
+        else if (strcmp(param, "bind_port") == 0) {
+            free(config->bind_port);
+            config->bind_port = strdup(value);
+            return 0;
+        }
+
+    }
+
+    /* Options related to daemon startup */
+    else if (strcmp(section, "daemon") == 0) {
+
+        /* PID file */
+        if (strcmp(param, "pid_file") == 0) {
+            free(config->pidfile);
+            config->pidfile = strdup(value);
+            return 0;
+        }
+
+        /* Max log level */
+        else if (strcmp(param, "log_level") == 0) {
+
+            int level = guacd_parse_log_level(value);
+
+            /* Invalid log level */
+            if (level < 0) {
+                guacd_conf_parse_error = "Invalid log level. Valid levels are: \"debug\", \"info\", \"warning\", and \"error\".";
+                return 1;
+            }
+
+            /* Valid log level */
+            config->max_log_level = level;
+            return 0;
+
+        }
+
+    }
+
+    /* SSL-specific options */
+    else if (strcmp(section, "ssl") == 0) {
+#ifdef ENABLE_SSL
+        /* SSL certificate */
+        if (strcmp(param, "server_certificate") == 0) {
+            free(config->cert_file);
+            config->cert_file = strdup(value);
+            return 0;
+        }
+
+        /* SSL key */
+        else if (strcmp(param, "server_key") == 0) {
+            free(config->key_file);
+            config->key_file = strdup(value);
+            return 0;
+        }
+#else
+        guacd_conf_parse_error = "SSL support not compiled in";
+        return 1;
+#endif
+
+    }
+
+    /* If still unhandled, the parameter/section is invalid */
+    guacd_conf_parse_error = "Invalid parameter or section name";
+    return 1;
+
+}
+
+int guacd_conf_parse_file(guacd_config* conf, int fd) {
+
+    int chars_read;
+
+    char buffer[8192];
+    int length = 0;
+
+    int line = 1;
+    char* line_start = buffer;
+    int parsed = 0;
+
+    /* Attempt to fill remaining space in buffer */
+    while ((chars_read = read(fd, buffer + length, sizeof(buffer) -  length)) > 0) {
+
+        length += chars_read;
+
+        line_start = buffer;
+
+        /* Attempt to parse entire buffer */
+        while ((parsed = guacd_parse_conf(guacd_conf_callback, line_start, length, conf)) > 0) {
+            line_start += parsed;
+            length -= parsed;
+            line++;
+        }
+
+        /* Shift contents to front */
+        memmove(buffer, line_start, length);
+
+    }
+
+    /* Handle parse errors */
+    if (parsed < 0) {
+        int column = guacd_conf_parse_error_location - line_start + 1;
+        fprintf(stderr, "Parse error at line %i, column %i: %s.\n",
+                line, column, guacd_conf_parse_error);
+        return 1;
+    }
+
+    /* Check for error conditions */
+    if (chars_read < 0) {
+        fprintf(stderr, "Error reading configuration: %s\n", strerror(errno));
+        return 1;
+    }
+
+    /* Read successfully */
+    return 0;
+
+}
+
+guacd_config* guacd_conf_load() {
+
+    guacd_config* conf = malloc(sizeof(guacd_config));
+    if (conf == NULL)
+        return NULL;
+
+    /* Load defaults */
+    conf->bind_host = NULL;
+    conf->bind_port = strdup("4822");
+    conf->pidfile = NULL;
+    conf->foreground = 0;
+    conf->max_log_level = GUAC_LOG_INFO;
+
+#ifdef ENABLE_SSL
+    conf->cert_file = NULL;
+    conf->key_file = NULL;
+#endif
+
+    /* Read configuration from file */
+    int fd = open(GUACD_CONF_FILE, O_RDONLY);
+    if (fd > 0) {
+
+        int retval = guacd_conf_parse_file(conf, fd);
+        close(fd);
+
+        if (retval != 0) {
+            fprintf(stderr, "Unable to parse \"" GUACD_CONF_FILE "\".\n");
+            return NULL;
+        }
+
+    }
+
+    /* Notify of errors preventing reading */
+    else if (errno != ENOENT) {
+        fprintf(stderr, "Unable to open \"" GUACD_CONF_FILE "\": %s\n", strerror(errno));
+        return NULL;
+    }
+
+    return conf;
+
+}
+
diff --git a/src/guacd/conf-file.h b/src/guacd/conf-file.h
new file mode 100644
index 0000000..d934494
--- /dev/null
+++ b/src/guacd/conf-file.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _GUACD_CONF_FILE_H
+#define _GUACD_CONF_FILE_H
+
+#include "config.h"
+
+#include <guacamole/client.h>
+
+/**
+ * The contents of a guacd configuration file.
+ */
+typedef struct guacd_config {
+
+    /**
+     * The host to bind on.
+     */
+    char* bind_host;
+
+    /**
+     * The port to bind on.
+     */
+    char* bind_port;
+
+    /**
+     * The file to write the PID in, if any.
+     */
+    char* pidfile;
+
+    /**
+     * Whether guacd should run in the foreground.
+     */
+    int foreground;
+
+#ifdef ENABLE_SSL
+    /**
+     * SSL certificate file.
+     */
+    char* cert_file;
+
+    /**
+     * SSL private key file.
+     */
+    char* key_file;
+#endif
+
+    /**
+     * The maximum log level to be logged by guacd.
+     */
+    guac_client_log_level max_log_level;
+
+} guacd_config;
+
+/**
+ * Reads the given file descriptor, parsing its contents into the guacd_config.
+ * On success, zero is returned. If parsing fails, non-zero is returned, and an
+ * error message is printed to stderr.
+ */
+int guacd_conf_parse_file(guacd_config* conf, int fd);
+
+/**
+ * Loads the configuration from any of several default locations, if found. If
+ * parsing fails, NULL is returned, and an error message is printed to stderr.
+ */
+guacd_config* guacd_conf_load();
+
+#endif
+
diff --git a/src/guacd/conf-parse.c b/src/guacd/conf-parse.c
new file mode 100644
index 0000000..551b342
--- /dev/null
+++ b/src/guacd/conf-parse.c
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "conf-parse.h"
+
+#include <guacamole/client.h>
+
+#include <ctype.h>
+#include <string.h>
+
+/*
+ * Simple recursive descent parser for an INI-like conf file grammar.  The
+ * grammar is, roughly:
+ *
+ * <line>            ::= <opt-whitespace> <declaration> <line-end>
+ * <line-end>        ::= <opt-whitespace> <opt-comment> <EOL>
+ * <declaration>     ::= <section-name> | <parameter-value> | ""
+ * <section-name>    ::= "[" <name> "]"
+ * <parameter-value> ::= <name> <opt-whitespace> "=" <opt-whitespace> <value>
+ *
+ * Where:
+ *  <opt-whitespace> is any number of tabs or spaces.
+ *  <opt-comment> is a # character followed by any length of text without an EOL.
+ *  <name> is an alpha-numeric string consisting of: [A-Za-z0-9_-].
+ *  <value> is any length of text without an EOL or # character, or a double-quoted string (backslash escapes legal).
+ *  <EOL> is a carriage return or line feed character.
+ */
+
+/**
+ * The current section. Note that this means the parser is NOT threadsafe.
+ */
+char __guacd_current_section[GUACD_CONF_MAX_NAME_LENGTH + 1] = "";
+
+char* guacd_conf_parse_error = NULL;
+
+char* guacd_conf_parse_error_location = NULL;
+
+/**
+ * Reads through all whitespace at the beginning of the buffer, returning the
+ * number of characters read. This is <opt-whitespace> in the grammar above. As
+ * the whitespace is zero or more whitespace characters, this function cannot
+ * fail, but it may read zero chars overall.
+ */
+static int guacd_parse_whitespace(char* buffer, int length) {
+
+    int chars_read = 0;
+
+    /* Read through all whitespace */
+    while (chars_read < length) {
+
+        /* Read character */
+        char c = *buffer;
+
+        /* Stop at non-whitespace */
+        if (c != ' ' && c != '\t')
+            break;
+
+        chars_read++;
+        buffer++;
+
+    }
+
+    return chars_read;
+
+}
+
+/**
+ * Parses the name of a parameter, section, etc. A section/parameter name can
+ * consist only of alphanumeric characters and underscores. The resulting name
+ * will be stored in the name string, which must have at least 256 bytes
+ * available.
+ */
+static int guacd_parse_name(char* buffer, int length, char* name) {
+
+    char* name_start = buffer;
+    int chars_read = 0;
+
+    /* Read through all valid name chars */
+    while (chars_read < length) {
+
+        /* Read character */
+        char c = *buffer;
+
+        /* Stop at non-name characters */
+        if (!isalnum(c) && c != '_')
+            break;
+
+        chars_read++;
+        buffer++;
+
+        /* Ensure name does not exceed maximum length */
+        if (chars_read > GUACD_CONF_MAX_NAME_LENGTH) {
+            guacd_conf_parse_error = "Names can be no more than 255 characters long";
+            guacd_conf_parse_error_location = buffer;
+            return -1;
+        }
+
+    }
+
+    /* Names must contain at least one character */
+    if (chars_read == 0)
+        return 0;
+
+    /* Copy name from buffer */
+    memcpy(name, name_start, chars_read);
+    name[chars_read] = '\0';
+
+    return chars_read;
+
+}
+
+/**
+ * Parses the value of a parameter. A value can consist of any character except
+ * '#', whitespace, or EOL. The resulting value will be stored in the value
+ * string, which must have at least 256 bytes available.
+ */ 
+static int guacd_parse_value(char* buffer, int length, char* value) {
+
+    char* value_start = buffer;
+    int chars_read = 0;
+
+    /* Read through all valid value chars */
+    while (chars_read < length) {
+
+        /* Read character */
+        char c = *buffer;
+
+        /* Stop at invalid character */
+        if (c == '#' || c == '"' || c == '\r' || c == '\n' || c == ' ' || c == '\t')
+            break;
+
+        chars_read++;
+        buffer++;
+
+        /* Ensure value does not exceed maximum length */
+        if (chars_read > GUACD_CONF_MAX_VALUE_LENGTH) {
+            guacd_conf_parse_error = "Values can be no more than 8191 characters long";
+            guacd_conf_parse_error_location = buffer;
+            return -1;
+        }
+
+    }
+
+    /* Values must contain at least one character */
+    if (chars_read == 0) {
+        guacd_conf_parse_error = "Unquoted values must contain at least one character";
+        guacd_conf_parse_error_location = buffer;
+        return -1;
+    }
+
+    /* Copy value from buffer */
+    memcpy(value, value_start, chars_read);
+    value[chars_read] = '\0';
+
+    return chars_read;
+
+}
+
+/**
+ * Parses the quoted value of a parameter. Quoted values may contain any
+ * character except double quotes or backslashes, which must be
+ * backslash-escaped.
+ */ 
+static int guacd_parse_quoted_value(char* buffer, int length, char* value) {
+
+    int escaped = 0;
+
+    /* Assert first character is '"' */
+    if (length == 0 || *buffer != '"')
+        return 0;
+
+    int chars_read = 1;
+    buffer++;
+    length--;
+
+    /* Read until end of quoted value */
+    while (chars_read < length) {
+
+        /* Read character */
+        char c = *buffer;
+
+        /* Handle special characters if not escaped */
+        if (!escaped) {
+
+            /* Stop at quote or invalid character */
+            if (c == '"' || c == '\r' || c == '\n')
+                break;
+
+            /* Backslash escaping */
+            else if (c == '\\')
+                escaped = 1;
+
+            else
+                *(value++) = c;
+
+        }
+
+        /* Reset escape flag */
+        else {
+            escaped = 0;
+            *(value++) = c;
+        }
+
+        chars_read++;
+        buffer++;
+
+        /* Ensure value does not exceed maximum length */
+        if (chars_read > GUACD_CONF_MAX_VALUE_LENGTH) {
+            guacd_conf_parse_error = "Values can be no more than 8191 characters long";
+            guacd_conf_parse_error_location = buffer;
+            return -1;
+        }
+
+    }
+
+    /* Assert value ends with '"' */
+    if (length == 0 || *buffer != '"') {
+        guacd_conf_parse_error = "'\"' expected";
+        guacd_conf_parse_error_location = buffer;
+        return -1;
+    }
+
+    chars_read++;
+
+    /* Terminate read value */
+    *value = '\0';
+
+    return chars_read;
+
+}
+
+/**
+ * Reads a parameter/value pair, separated by an '=' character. If the
+ * parameter/value pair is invalid for any reason, a negative value is
+ * returned.
+ */
+static int guacd_parse_parameter(guacd_param_callback* callback, char* buffer, int length, void* data) {
+
+    char param_name[GUACD_CONF_MAX_NAME_LENGTH + 1];
+    char param_value[GUACD_CONF_MAX_VALUE_LENGTH + 1];
+
+    int retval;
+    int chars_read = 0;
+
+    char* param_start = buffer;
+
+    retval = guacd_parse_name(buffer, length, param_name);
+    if (retval < 0)
+        return -1;
+
+    /* If no name found, no parameter/value pair */
+    if (retval == 0)
+        return 0;
+
+    /* Validate presence of section header */
+    if (__guacd_current_section[0] == '\0') {
+        guacd_conf_parse_error = "Parameters must have a corresponding section";
+        guacd_conf_parse_error_location = buffer;
+        return -1;
+    }
+
+    chars_read += retval;
+    buffer += retval;
+    length -= retval;
+
+    /* Optional whitespace before '=' */
+    retval = guacd_parse_whitespace(buffer, length);
+    chars_read += retval;
+    buffer += retval;
+    length -= retval;
+
+    /* Required '=' */
+    if (length == 0 || *buffer != '=') {
+        guacd_conf_parse_error = "'=' expected";
+        guacd_conf_parse_error_location = buffer;
+        return -1;
+    }
+
+    chars_read++;
+    buffer++;
+    length--;
+
+    /* Optional whitespace before value */
+    retval = guacd_parse_whitespace(buffer, length);
+    chars_read += retval;
+    buffer += retval;
+    length -= retval;
+
+    /* Quoted parameter value */
+    retval = guacd_parse_quoted_value(buffer, length, param_value);
+    if (retval < 0)
+        return -1;
+
+    /* Non-quoted parameter value (required if no quoted value given) */
+    if (retval == 0) retval = guacd_parse_value(buffer, length, param_value);
+    if (retval < 0)
+        return -1;
+    
+    chars_read += retval;
+
+    /* Call callback, handling error code */
+    if (callback(__guacd_current_section, param_name, param_value, data)) {
+        guacd_conf_parse_error_location = param_start;
+        return -1;
+    }
+
+    return chars_read;
+
+}
+
+/**
+ * Reads a section name from the beginning of the given buffer. This section
+ * name must conform to the grammar definition. If the section name does not
+ * match, a negative value is returned.
+ */
+static int guacd_parse_section(char* buffer, int length) {
+
+    int retval;
+
+    /* Assert first character is '[' */
+    if (length == 0 || *buffer != '[')
+        return 0;
+
+    int chars_read = 1;
+    buffer++;
+    length--;
+
+    retval = guacd_parse_name(buffer, length, __guacd_current_section);
+    if (retval < 0)
+        return -1;
+
+    /* If no name found, invalid section */
+    if (retval == 0) {
+        guacd_conf_parse_error = "Section names must contain at least one character";
+        guacd_conf_parse_error_location = buffer;
+        return -1;
+    }
+
+    chars_read += retval;
+    buffer += retval;
+    length -= retval;
+
+    /* Name must end with ']' */
+    if (length == 0 || *buffer != ']') {
+        guacd_conf_parse_error = "']' expected";
+        guacd_conf_parse_error_location = buffer;
+        return -1;
+    }
+
+    chars_read++;
+    
+    return chars_read;
+
+}
+
+/**
+ * Parses a declaration, which may be either a section name or a
+ * parameter/value pair. The empty string is acceptable, as well, as a
+ * "null declaration".
+ */
+static int guacd_parse_declaration(guacd_param_callback* callback, char* buffer, int length, void* data) {
+
+    int retval;
+
+    /* Look for section name */
+    retval = guacd_parse_section(buffer, length);
+    if (retval != 0)
+        return retval;
+
+    /* Lacking a section name, read parameter/value pair */
+    retval = guacd_parse_parameter(callback, buffer, length, data);
+    if (retval != 0)
+        return retval;
+
+    /* Null declaration (default) */
+    return 0;
+
+}
+
+/**
+ * Parses a comment, which must start with a '#' character, and terminate with
+ * an end-of-line character. If no EOL is found, or the first character is not
+ * a '#', a negative value is returned. Otherwise, the number of characters
+ * parsed is returned. If no comment is present, zero is returned.
+ */
+static int guacd_parse_comment(char* buffer, int length) {
+
+    /* Need at least one character */
+    if (length == 0)
+        return 0;
+
+    /* Assert first character is '#' */
+    if (*(buffer++) != '#')
+        return 0;
+
+    int chars_read = 1;
+
+    /* Advance to first non-space character */
+    while (chars_read < length) {
+
+        /* Read character */
+        char c = *buffer;
+
+        /* End of comment found at end of line */
+        if (c == '\n' || c == '\r')
+            return chars_read;
+
+        chars_read++;
+        buffer++;
+
+    }
+
+    /* No end of line in comment */
+    guacd_conf_parse_error = "expected end-of-line";
+    guacd_conf_parse_error_location = buffer;
+    return -1;
+
+}
+
+/**
+ * Parses the end of a line, which may contain a comment. If a parse error
+ * occurs, a negative value is returned. Otherwise, the number of characters
+ * parsed is returned.
+ */
+static int guacd_parse_line_end(char* buffer, int length) {
+
+    int chars_read = 0;
+    int retval;
+  
+    /* Initial optional whitespace */ 
+    retval = guacd_parse_whitespace(buffer, length);
+    chars_read += retval;
+    buffer += retval;
+    length -= retval;
+
+    /* Optional comment */ 
+    retval = guacd_parse_comment(buffer, length);
+    if (retval < 0)
+        return -1;
+
+    chars_read += retval;
+    buffer += retval;
+    length -= retval;
+
+    /* Assert EOL */
+    if (length == 0 || (*buffer != '\r' && *buffer != '\n')) {
+        guacd_conf_parse_error = "expected end-of-line";
+        guacd_conf_parse_error_location = buffer;
+        return -1;
+    }
+
+    chars_read++;
+
+    /* Line is valid */
+    return chars_read;
+
+}
+
+/**
+ * Parses an entire line - declaration, comment, and all. If a parse error
+ * occurs, a negative value is returned. Otherwise, the number of characters
+ * parsed is returned.
+ */
+static int guacd_parse_line(guacd_param_callback* callback, char* buffer, int length, void* data) {
+
+    int chars_read = 0;
+    int retval;
+  
+    /* Initial optional whitespace */ 
+    retval = guacd_parse_whitespace(buffer, length);
+    chars_read += retval;
+    buffer += retval;
+    length -= retval;
+
+    /* Declaration (which may be the empty string) */
+    retval = guacd_parse_declaration(callback, buffer, length, data);
+    if (retval < 0)
+        return retval;
+
+    chars_read += retval;
+    buffer += retval;
+    length -= retval;
+
+    /* End of line */
+    retval = guacd_parse_line_end(buffer, length);
+    if (retval < 0)
+        return retval;
+
+    chars_read += retval;
+
+    return chars_read;
+
+}
+
+int guacd_parse_conf(guacd_param_callback* callback, char* buffer, int length, void* data) {
+
+    /* Empty buffers are valid */
+    if (length == 0)
+        return 0;
+
+    return guacd_parse_line(callback, buffer, length, data);
+
+}
+
+int guacd_parse_log_level(const char* name) {
+
+    /* Translate log level name */
+    if (strcmp(name, "info")    == 0) return GUAC_LOG_INFO;
+    if (strcmp(name, "error")   == 0) return GUAC_LOG_ERROR;
+    if (strcmp(name, "warning") == 0) return GUAC_LOG_WARNING;
+    if (strcmp(name, "debug")   == 0) return GUAC_LOG_DEBUG;
+
+    /* No such log level */
+    return -1;
+
+}
+
diff --git a/src/guacd/conf-parse.h b/src/guacd/conf-parse.h
new file mode 100644
index 0000000..3d9c1df
--- /dev/null
+++ b/src/guacd/conf-parse.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _GUACD_CONF_PARSE_H
+#define _GUACD_CONF_PARSE_H
+
+/**
+ * The maximum length of a name, in characters.
+ */
+#define GUACD_CONF_MAX_NAME_LENGTH 255
+
+/**
+ * The maximum length of a value, in characters.
+ */
+#define GUACD_CONF_MAX_VALUE_LENGTH 8191 
+
+/**
+ * A callback function which is provided to guacd_parse_conf() and is called
+ * for each parameter/value pair set. The current section is always given. This
+ * function will not be called for parameters outside of sections, which are
+ * illegal.
+ */
+typedef int guacd_param_callback(const char* section, const char* param, const char* value, void* data);
+
+/**
+ * Parses an arbitrary buffer of configuration file data, calling the given
+ * callback for each valid parameter/value pair. Upon success, the number of
+ * characters parsed is returned. On failure, a negative value is returned, and
+ * guacd_conf_parse_error and guacd_conf_parse_error_location are set. The
+ * provided data will be passed to the callback for each invocation.
+ */
+int guacd_parse_conf(guacd_param_callback* callback, char* buffer, int length, void* data);
+
+/**
+ * Parses the given level name, returning the corresponding log level, or -1 if
+ * no such log level exists.
+ */
+int guacd_parse_log_level(const char* name);
+
+/**
+ * Human-readable description of the current error, if any.
+ */
+extern char* guacd_conf_parse_error;
+
+/**
+ * The location of the most recent parse error. This will be a pointer to the
+ * location of the error within the buffer passed to guacd_parse_conf().
+ */
+extern char* guacd_conf_parse_error_location;
+
+#endif
+
diff --git a/src/guacd/daemon.c b/src/guacd/daemon.c
index 2939aaf..e732704 100644
--- a/src/guacd/daemon.c
+++ b/src/guacd/daemon.c
@@ -1,78 +1,147 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is guacd.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
- * David PHAM-VAN <d.pham-van at ulteo.com> Ulteo SAS - http://www.ulteo.com
- * Alex Bligh <alex at alex.org.uk>
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
-
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <signal.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <ctype.h>
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <sys/socket.h>
-#include <netdb.h>
-#include <netinet/in.h>
+#include "config.h"
 
-#include <errno.h>
-#include <syslog.h>
-#include <libgen.h>
-
-#ifdef ENABLE_SSL
-#include <openssl/ssl.h>
-#include "socket-ssl.h"
-#endif
+#include "client.h"
+#include "client-map.h"
+#include "conf-args.h"
+#include "conf-file.h"
+#include "log.h"
 
 #include <guacamole/client.h>
 #include <guacamole/error.h>
 #include <guacamole/instruction.h>
 #include <guacamole/plugin.h>
 #include <guacamole/protocol.h>
+#include <guacamole/socket.h>
 
-#include "client.h"
-#include "log.h"
+#ifdef ENABLE_SSL
+#include <openssl/ssl.h>
+#include "socket-ssl.h"
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #define GUACD_DEV_NULL "/dev/null"
 #define GUACD_ROOT     "/"
 
-void guacd_handle_connection(guac_socket* socket) {
+/**
+ * Logs a reasonable explanatory message regarding handshake failure based on
+ * the current value of guac_error.
+ */
+static void guacd_log_handshake_failure() {
+
+    if (guac_error == GUAC_STATUS_CLOSED)
+        guacd_log(GUAC_LOG_INFO,
+                "Guacamole connection closed during handshake");
+    else if (guac_error == GUAC_STATUS_PROTOCOL_ERROR)
+        guacd_log(GUAC_LOG_ERROR,
+                "Guacamole protocol violation. Perhaps the version of "
+                "guacamole-client is incompatible with this version of "
+                "guacd?");
+    else
+        guacd_log(GUAC_LOG_WARNING,
+                "Guacamole handshake failed: %s",
+                guac_status_string(guac_error));
+
+}
+
+/**
+ * Copies the given array of mimetypes (strings) into a newly-allocated NULL-
+ * terminated array of strings. Both the array and the strings within the array
+ * are newly-allocated and must be later freed via guacd_free_mimetypes().
+ *
+ * @param mimetypes
+ *     The array of mimetypes to copy.
+ *
+ * @param count
+ *     The number of mimetypes in the given array.
+ *
+ * @return
+ *     A newly-allocated, NULL-terminated array containing newly-allocated
+ *     copies of each of the mimetypes provided in the original mimetypes
+ *     array.
+ */
+static char** guacd_copy_mimetypes(char** mimetypes, int count) {
+
+    int i;
+
+    /* Allocate sufficient space for NULL-terminated array of mimetypes */
+    char** mimetypes_copy = malloc(sizeof(char*) * (count+1));
+
+    /* Copy each provided mimetype */
+    for (i = 0; i < count; i++)
+        mimetypes_copy[i] = strdup(mimetypes[i]);
+
+    /* Terminate with NULL */
+    mimetypes_copy[count] = NULL;
+
+    return mimetypes_copy;
+
+}
+
+/**
+ * Frees the given array of mimetypes, including the space allocated to each
+ * mimetype string within the array. The provided array of mimetypes MUST have
+ * been allocated with guacd_copy_mimetypes().
+ *
+ * @param mimetypes
+ *     The NULL-terminated array of mimetypes to free. This array MUST have
+ *     been previously allocated with guacd_copy_mimetypes().
+ */
+static void guacd_free_mimetypes(char** mimetypes) {
+
+    char** current_mimetype = mimetypes;
+
+    /* Free all strings within NULL-terminated mimetype array */
+    while (*current_mimetype != NULL) {
+        free(*current_mimetype);
+        current_mimetype++;
+    }
+
+    /* Free the array itself, now that its contents have been freed */
+    free(mimetypes);
+
+}
+
+/**
+ * Creates a new guac_client for the connection on the given socket, adding
+ * it to the client map based on its ID.
+ */
+static void guacd_handle_connection(guacd_client_map* map, guac_socket* socket) {
 
     guac_client* client;
     guac_client_plugin* plugin;
@@ -80,27 +149,35 @@ void guacd_handle_connection(guac_socket* socket) {
     guac_instruction* size;
     guac_instruction* audio;
     guac_instruction* video;
+    guac_instruction* image;
     guac_instruction* connect;
     int init_result;
 
+    /* Reset guac_error */
+    guac_error = GUAC_STATUS_SUCCESS;
+    guac_error_message = NULL;
+
     /* Get protocol from select instruction */
-    select = guac_instruction_expect(
-            socket, GUACD_USEC_TIMEOUT, "select");
+    select = guac_instruction_expect(socket, GUACD_USEC_TIMEOUT, "select");
     if (select == NULL) {
 
         /* Log error */
-        guacd_log_guac_error("Error reading \"select\"");
+        guacd_log_handshake_failure();
+        guacd_log_guac_error(GUAC_LOG_DEBUG,
+                "Error reading \"select\"");
 
         /* Free resources */
         guac_socket_free(socket);
         return;
+
     }
 
     /* Validate args to select */
     if (select->argc != 1) {
 
         /* Log error */
-        guacd_log_error("Bad number of arguments to \"select\" (%i)",
+        guacd_log_handshake_failure();
+        guacd_log(GUAC_LOG_ERROR, "Bad number of arguments to \"select\" (%i)",
                 select->argc);
 
         /* Free resources */
@@ -108,7 +185,7 @@ void guacd_handle_connection(guac_socket* socket) {
         return;
     }
 
-    guacd_log_info("Protocol \"%s\" selected", select->argv[0]);
+    guacd_log(GUAC_LOG_INFO, "Protocol \"%s\" selected", select->argv[0]);
 
     /* Get plugin from protocol in select */
     plugin = guac_client_plugin_open(select->argv[0]);
@@ -117,7 +194,12 @@ void guacd_handle_connection(guac_socket* socket) {
     if (plugin == NULL) {
 
         /* Log error */
-        guacd_log_guac_error("Error loading client plugin");
+        if (guac_error == GUAC_STATUS_NOT_FOUND)
+            guacd_log(GUAC_LOG_WARNING,
+                    "Support for selected protocol is not installed");
+        else
+            guacd_log_guac_error(GUAC_LOG_ERROR,
+                    "Unable to load client plugin");
 
         /* Free resources */
         guac_socket_free(socket);
@@ -129,90 +211,145 @@ void guacd_handle_connection(guac_socket* socket) {
             || guac_socket_flush(socket)) {
 
         /* Log error */
-        guacd_log_guac_error("Error sending \"args\"");
+        guacd_log_handshake_failure();
+        guacd_log_guac_error(GUAC_LOG_DEBUG, "Error sending \"args\"");
 
         if (guac_client_plugin_close(plugin))
-            guacd_log_guac_error("Error closing client plugin");
+            guacd_log_guac_error(GUAC_LOG_WARNING,
+                    "Unable to close client plugin");
 
         guac_socket_free(socket);
         return;
     }
 
+    /* Get client */
+    client = guac_client_alloc();
+    if (client == NULL) {
+        guacd_log_guac_error(GUAC_LOG_ERROR, "Unable to create client");
+        guac_socket_free(socket);
+        return;
+    }
+
     /* Get optimal screen size */
     size = guac_instruction_expect(
             socket, GUACD_USEC_TIMEOUT, "size");
     if (size == NULL) {
 
         /* Log error */
-        guacd_log_guac_error("Error reading \"size\"");
+        guacd_log_handshake_failure();
+        guacd_log_guac_error(GUAC_LOG_DEBUG, "Error reading \"size\"");
 
         /* Free resources */
+        guac_client_free(client);
         guac_socket_free(socket);
         return;
     }
 
+    /* Parse optimal screen dimensions from size instruction */
+    client->info.optimal_width  = atoi(size->argv[0]);
+    client->info.optimal_height = atoi(size->argv[1]);
+
+    /* If DPI given, set the client resolution */
+    if (size->argc >= 3)
+        client->info.optimal_resolution = atoi(size->argv[2]);
+
+    /* Otherwise, use a safe default for rough backwards compatibility */
+    else
+        client->info.optimal_resolution = 96;
+
+    guac_instruction_free(size);
+
     /* Get supported audio formats */
     audio = guac_instruction_expect(
             socket, GUACD_USEC_TIMEOUT, "audio");
     if (audio == NULL) {
 
         /* Log error */
-        guacd_log_guac_error("Error reading \"audio\"");
+        guacd_log_handshake_failure();
+        guacd_log_guac_error(GUAC_LOG_DEBUG, "Error reading \"audio\"");
 
         /* Free resources */
+        guac_client_free(client);
         guac_socket_free(socket);
         return;
     }
 
+    /* Store audio mimetypes */
+    client->info.audio_mimetypes =
+        guacd_copy_mimetypes(audio->argv, audio->argc);
+
+    guac_instruction_free(audio);
+
     /* Get supported video formats */
     video = guac_instruction_expect(
             socket, GUACD_USEC_TIMEOUT, "video");
     if (video == NULL) {
 
         /* Log error */
-        guacd_log_guac_error("Error reading \"video\"");
+        guacd_log_handshake_failure();
+        guacd_log_guac_error(GUAC_LOG_DEBUG, "Error reading \"video\"");
+
+        /* Free resources */
+        guac_client_free(client);
+        guac_socket_free(socket);
+        return;
+    }
+
+    /* Store video mimetypes */
+    client->info.video_mimetypes =
+        guacd_copy_mimetypes(video->argv, video->argc);
+
+    guac_instruction_free(video);
+
+    /* Get supported image formats */
+    image = guac_instruction_expect(
+            socket, GUACD_USEC_TIMEOUT, "image");
+    if (image == NULL) {
+
+        /* Log error */
+        guacd_log_handshake_failure();
+        guacd_log_guac_error(GUAC_LOG_DEBUG, "Error reading \"image\"");
 
         /* Free resources */
+        guac_client_free(client);
         guac_socket_free(socket);
         return;
     }
 
+    /* Store image mimetypes */
+    client->info.image_mimetypes =
+        guacd_copy_mimetypes(image->argv, image->argc);
+
+    guac_instruction_free(image);
+
     /* Get args from connect instruction */
     connect = guac_instruction_expect(
             socket, GUACD_USEC_TIMEOUT, "connect");
     if (connect == NULL) {
 
         /* Log error */
-        guacd_log_guac_error("Error reading \"connect\"");
+        guacd_log_handshake_failure();
+        guacd_log_guac_error(GUAC_LOG_DEBUG, "Error reading \"connect\"");
 
         if (guac_client_plugin_close(plugin))
-            guacd_log_guac_error("Error closing client plugin");
+            guacd_log_guac_error(GUAC_LOG_WARNING,
+                    "Unable to close client plugin");
 
+        guac_client_free(client);
         guac_socket_free(socket);
         return;
     }
 
-    /* Get client */
-    client = guac_client_alloc();
     client->socket = socket;
-    client->log_info_handler = guacd_client_log_info;
-    client->log_error_handler = guacd_client_log_error;
-
-    /* Parse optimal screen dimensions from size instruction */
-    client->info.optimal_width  = atoi(size->argv[0]);
-    client->info.optimal_height = atoi(size->argv[1]);
+    client->log_handler = guacd_client_log;
 
-    /* Store audio mimetypes */
-    client->info.audio_mimetypes = malloc(sizeof(char*) * (audio->argc+1));
-    memcpy(client->info.audio_mimetypes, audio->argv,
-            sizeof(char*) * audio->argc);
-    client->info.audio_mimetypes[audio->argc] = NULL;
+    /* Store client */
+    if (guacd_client_map_add(map, client))
+        guacd_log(GUAC_LOG_ERROR, "Unable to add client. Internal client storage has failed");
 
-    /* Store video mimetypes */
-    client->info.video_mimetypes = malloc(sizeof(char*) * (video->argc+1));
-    memcpy(client->info.video_mimetypes, video->argv,
-            sizeof(char*) * video->argc);
-    client->info.video_mimetypes[video->argc] = NULL;
+    /* Send connection ID */
+    guacd_log(GUAC_LOG_INFO, "Connection ID is \"%s\"", client->connection_id);
+    guac_protocol_send_ready(socket, client->connection_id);
 
     /* Init client */
     init_result = guac_client_plugin_init_client(plugin,
@@ -225,35 +362,37 @@ void guacd_handle_connection(guac_socket* socket) {
 
         guac_client_free(client);
 
-        guacd_log_guac_error("Error instantiating client");
+        guacd_log_guac_error(GUAC_LOG_INFO, "Connection did not succeed");
 
         if (guac_client_plugin_close(plugin))
-            guacd_log_guac_error("Error closing client plugin");
+            guacd_log_guac_error(GUAC_LOG_WARNING,
+                    "Unable to close client plugin");
 
         guac_socket_free(socket);
         return;
     }
 
     /* Start client threads */
-    guacd_log_info("Starting client");
+    guacd_log(GUAC_LOG_INFO, "Starting client");
     if (guacd_client_start(client))
-        guacd_log_error("Client finished abnormally");
+        guacd_log(GUAC_LOG_WARNING, "Client finished abnormally");
     else
-        guacd_log_info("Client finished normally");
+        guacd_log(GUAC_LOG_INFO, "Client disconnected");
 
-    /* Free mimetype lists */
-    free(client->info.audio_mimetypes);
-    free(client->info.video_mimetypes);
+    /* Remove client */
+    if (guacd_client_map_remove(map, client->connection_id) == NULL)
+        guacd_log(GUAC_LOG_ERROR, "Unable to remove client. Internal client storage has failed");
 
-    /* Free remaining instructions */
-    guac_instruction_free(audio);
-    guac_instruction_free(video);
-    guac_instruction_free(size);
+    /* Free mimetype lists */
+    guacd_free_mimetypes(client->info.audio_mimetypes);
+    guacd_free_mimetypes(client->info.video_mimetypes);
+    guacd_free_mimetypes(client->info.image_mimetypes);
 
     /* Clean up */
     guac_client_free(client);
     if (guac_client_plugin_close(plugin))
-        guacd_log_error("Error closing client plugin");
+        guacd_log_guac_error(GUAC_LOG_WARNING,
+                "Unable to close client plugin");
 
     /* Close socket */
     guac_socket_free(socket);
@@ -284,13 +423,13 @@ int daemonize() {
     /* Fork once to ensure we aren't the process group leader */
     pid = fork();
     if (pid < 0) {
-        guacd_log_error("Could not fork() parent: %s", strerror(errno));
+        guacd_log(GUAC_LOG_ERROR, "Could not fork() parent: %s", strerror(errno));
         return 1;
     }
 
     /* Exit if we are the parent */
     if (pid > 0) {
-        guacd_log_info("Exiting and passing control to PID %i", pid);
+        guacd_log(GUAC_LOG_DEBUG, "Exiting and passing control to PID %i", pid);
         _exit(0);
     }
 
@@ -300,19 +439,19 @@ int daemonize() {
     /* Fork again so the session group leader exits */
     pid = fork();
     if (pid < 0) {
-        guacd_log_error("Could not fork() group leader: %s", strerror(errno));
+        guacd_log(GUAC_LOG_ERROR, "Could not fork() group leader: %s", strerror(errno));
         return 1;
     }
 
     /* Exit if we are the parent */
     if (pid > 0) {
-        guacd_log_info("Exiting and passing control to PID %i", pid);
+        guacd_log(GUAC_LOG_DEBUG, "Exiting and passing control to PID %i", pid);
         _exit(0);
     }
 
     /* Change to root directory */
     if (chdir(GUACD_ROOT) < 0) {
-        guacd_log_error(
+        guacd_log(GUAC_LOG_ERROR, 
                 "Unable to change working directory to "
                 GUACD_ROOT);
         return 1;
@@ -324,7 +463,7 @@ int daemonize() {
     || redirect_fd(STDOUT_FILENO, O_WRONLY)
     || redirect_fd(STDERR_FILENO, O_WRONLY)) {
 
-        guacd_log_error(
+        guacd_log(GUAC_LOG_ERROR, 
                 "Unable to redirect standard file descriptors to "
                 GUACD_DEV_NULL);
         return 1;
@@ -335,7 +474,6 @@ int daemonize() {
 
 }
 
-
 int main(int argc, char* argv[]) {
 
     /* Server */
@@ -357,85 +495,32 @@ int main(int argc, char* argv[]) {
     socklen_t client_addr_len;
     int connected_socket_fd;
 
-    /* Arguments */
-    char* listen_address = NULL; /* Default address of INADDR_ANY */
-    char* listen_port = "4822";  /* Default port */
-    char* pidfile = NULL;
-    int opt;
-    int foreground = 0;
-
 #ifdef ENABLE_SSL
-    /* SSL */
-    char* cert_file = NULL;
-    char* key_file = NULL;
     SSL_CTX* ssl_context = NULL;
 #endif
 
+    guacd_client_map* map = guacd_client_map_alloc();
+
     /* General */
     int retval;
 
-    /* Parse arguments */
-    while ((opt = getopt(argc, argv, "l:b:p:C:K:f")) != -1) {
-        if (opt == 'l') {
-            listen_port = strdup(optarg);
-        }
-        else if (opt == 'b') {
-            listen_address = strdup(optarg);
-        }
-        else if (opt == 'f') {
-            foreground = 1;
-        }
-        else if (opt == 'p') {
-            pidfile = strdup(optarg);
-        }
-#ifdef ENABLE_SSL
-        else if (opt == 'C') {
-            cert_file = strdup(optarg);
-        }
-        else if (opt == 'K') {
-            key_file = strdup(optarg);
-        }
-#else
-        else if (opt == 'C' || opt == 'K') {
-            fprintf(stderr,
-                    "This guacd does not have SSL/TLS support compiled in.\n\n"
-
-                    "If you wish to enable support for the -%c option, please install libssl and\n"
-                    "recompile guacd.\n",
-                    opt);
-            exit(EXIT_FAILURE);
-        }
-#endif
-        else {
+    /* Load configuration */
+    guacd_config* config = guacd_conf_load();
+    if (config == NULL || guacd_conf_parse_args(config, argc, argv))
+       exit(EXIT_FAILURE);
 
-            fprintf(stderr, "USAGE: %s"
-                    " [-l LISTENPORT]"
-                    " [-b LISTENADDRESS]"
-                    " [-p PIDFILE]"
-#ifdef ENABLE_SSL
-                    " [-C CERTIFICATE_FILE]"
-                    " [-K PEM_FILE]"
-#endif
-                    " [-f]\n", argv[0]);
-
-            exit(EXIT_FAILURE);
-        }
-    }
-
-    /* Set up logging prefix */
-    strncpy(log_prefix, basename(argv[0]), sizeof(log_prefix));
-
-    /* Open log as early as we can */
-    openlog(NULL, LOG_PID, LOG_DAEMON);
+    /* Init logging as early as possible */
+    guacd_log_level = config->max_log_level;
+    openlog(GUACD_LOG_NAME, LOG_PID, LOG_DAEMON);
 
     /* Log start */
-    guacd_log_info("Guacamole proxy daemon (guacd) version " VERSION);
+    guacd_log(GUAC_LOG_INFO, "Guacamole proxy daemon (guacd) version " VERSION " started");
 
     /* Get addresses for binding */
-    if ((retval = getaddrinfo(listen_address, listen_port,
+    if ((retval = getaddrinfo(config->bind_host, config->bind_port,
                     &hints, &addresses))) {
 
-        guacd_log_error("Error parsing given address or port: %s",
+        guacd_log(GUAC_LOG_ERROR, "Error parsing given address or port: %s",
                 gai_strerror(retval));
         exit(EXIT_FAILURE);
 
@@ -444,14 +529,14 @@ int main(int argc, char* argv[]) {
     /* Get socket */
     socket_fd = socket(AF_INET, SOCK_STREAM, 0);
     if (socket_fd < 0) {
-        guacd_log_error("Error opening socket: %s", strerror(errno));
+        guacd_log(GUAC_LOG_ERROR, "Error opening socket: %s", strerror(errno));
         exit(EXIT_FAILURE);
     }
 
     /* Allow socket reuse */
     if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR,
                 (void*) &opt_on, sizeof(opt_on))) {
-        guacd_log_info("Unable to set socket options for reuse: %s",
+        guacd_log(GUAC_LOG_WARNING, "Unable to set socket options for reuse: %s",
                 strerror(errno));
     }
 
@@ -467,7 +552,7 @@ int main(int argc, char* argv[]) {
                 bound_address, sizeof(bound_address),
                 bound_port, sizeof(bound_port),
                 NI_NUMERICHOST | NI_NUMERICSERV)))
-            guacd_log_error("Unable to resolve host: %s",
+            guacd_log(GUAC_LOG_ERROR, "Unable to resolve host: %s",
                     gai_strerror(retval));
 
         /* Attempt to bind socket to address */
@@ -475,7 +560,7 @@ int main(int argc, char* argv[]) {
                     current_address->ai_addr,
                     current_address->ai_addrlen) == 0) {
 
-            guacd_log_info("Successfully bound socket to "
+            guacd_log(GUAC_LOG_DEBUG, "Successfully bound socket to "
                     "host %s, port %s", bound_address, bound_port);
 
             /* Done if successful bind */
@@ -485,7 +570,7 @@ int main(int argc, char* argv[]) {
 
         /* Otherwise log information regarding bind failure */
         else
-            guacd_log_info("Unable to bind socket to "
+            guacd_log(GUAC_LOG_DEBUG, "Unable to bind socket to "
                     "host %s, port %s: %s",
                     bound_address, bound_port, strerror(errno));
 
@@ -495,61 +580,61 @@ int main(int argc, char* argv[]) {
 
     /* If unable to bind to anything, fail */
     if (current_address == NULL) {
-        guacd_log_error("Unable to bind socket to any addresses.");
+        guacd_log(GUAC_LOG_ERROR, "Unable to bind socket to any addresses.");
         exit(EXIT_FAILURE);
     }
 
 #ifdef ENABLE_SSL
     /* Init SSL if enabled */
-    if (key_file != NULL || cert_file != NULL) {
+    if (config->key_file != NULL || config->cert_file != NULL) {
 
         /* Init SSL */
-        guacd_log_info("Communication will require SSL/TLS.");
+        guacd_log(GUAC_LOG_INFO, "Communication will require SSL/TLS.");
         SSL_library_init();
         SSL_load_error_strings();
         ssl_context = SSL_CTX_new(SSLv23_server_method());
 
         /* Load key */
-        if (key_file != NULL) {
-            guacd_log_info("Using PEM keyfile %s", key_file);
-            if (!SSL_CTX_use_PrivateKey_file(ssl_context, key_file, SSL_FILETYPE_PEM)) {
-                guacd_log_error("Unable to load keyfile.");
+        if (config->key_file != NULL) {
+            guacd_log(GUAC_LOG_INFO, "Using PEM keyfile %s", config->key_file);
+            if (!SSL_CTX_use_PrivateKey_file(ssl_context, config->key_file, SSL_FILETYPE_PEM)) {
+                guacd_log(GUAC_LOG_ERROR, "Unable to load keyfile.");
                 exit(EXIT_FAILURE);
             }
         }
         else
-            guacd_log_info("No PEM keyfile given - SSL/TLS may not work.");
+            guacd_log(GUAC_LOG_WARNING, "No PEM keyfile given - SSL/TLS may not work.");
 
         /* Load cert file if specified */
-        if (cert_file != NULL) {
-            guacd_log_info("Using certificate file %s", cert_file);
-            if (!SSL_CTX_use_certificate_file(ssl_context, cert_file, SSL_FILETYPE_PEM)) {
-                guacd_log_error("Unable to load certificate.");
+        if (config->cert_file != NULL) {
+            guacd_log(GUAC_LOG_INFO, "Using certificate file %s", config->cert_file);
+            if (!SSL_CTX_use_certificate_chain_file(ssl_context, config->cert_file)) {
+                guacd_log(GUAC_LOG_ERROR, "Unable to load certificate.");
                 exit(EXIT_FAILURE);
             }
         }
         else
-            guacd_log_info("No certificate file given - SSL/TLS may not work.");
+            guacd_log(GUAC_LOG_WARNING, "No certificate file given - SSL/TLS may not work.");
 
     }
 #endif
 
     /* Daemonize if requested */
-    if (!foreground) {
+    if (!config->foreground) {
 
         /* Attempt to daemonize process */
         if (daemonize()) {
-            guacd_log_error("Could not become a daemon.");
+            guacd_log(GUAC_LOG_ERROR, "Could not become a daemon.");
             exit(EXIT_FAILURE);
         }
 
     }
 
     /* Write PID file if requested */
-    if (pidfile != NULL) {
+    if (config->pidfile != NULL) {
 
         /* Attempt to open pidfile and write PID */
-        FILE* pidf = fopen(pidfile, "w");
+        FILE* pidf = fopen(config->pidfile, "w");
         if (pidf) {
             fprintf(pidf, "%d\n", getpid());
             fclose(pidf);
@@ -557,7 +642,7 @@ int main(int argc, char* argv[]) {
         
         /* Fail if could not write PID file*/
         else {
-            guacd_log_error("Could not write PID file: %s", strerror(errno));
+            guacd_log(GUAC_LOG_ERROR, "Could not write PID file: %s", strerror(errno));
             exit(EXIT_FAILURE);
         }
 
@@ -565,40 +650,40 @@ int main(int argc, char* argv[]) {
 
     /* Ignore SIGPIPE */
     if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
-        guacd_log_info("Could not set handler for SIGPIPE to ignore. "
+        guacd_log(GUAC_LOG_INFO, "Could not set handler for SIGPIPE to ignore. "
                 "SIGPIPE may cause termination of the daemon.");
     }
 
     /* Ignore SIGCHLD (force automatic removal of children) */
     if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) {
-        guacd_log_info("Could not set handler for SIGCHLD to ignore. "
+        guacd_log(GUAC_LOG_INFO, "Could not set handler for SIGCHLD to ignore. "
                 "Child processes may pile up in the process table.");
     }
 
     /* Log listening status */
-    guacd_log_info("Listening on host %s, port %s", bound_address, bound_port);
+    guacd_log(GUAC_LOG_INFO, "Listening on host %s, port %s", bound_address, bound_port);
 
     /* Free addresses */
     freeaddrinfo(addresses);
 
+    /* Listen for connections */
+    if (listen(socket_fd, 5) < 0) {
+        guacd_log(GUAC_LOG_ERROR, "Could not listen on socket: %s", strerror(errno));
+        return 3;
+    }
+
     /* Daemon loop */
     for (;;) {
 
         pid_t child_pid;
 
-        /* Listen for connections */
-        if (listen(socket_fd, 5) < 0) {
-            guacd_log_error("Could not listen on socket: %s", strerror(errno));
-            return 3;
-        }
-
         /* Accept connection */
         client_addr_len = sizeof(client_addr);
         connected_socket_fd = accept(socket_fd,
                 (struct sockaddr*) &client_addr, &client_addr_len);
 
         if (connected_socket_fd < 0) {
-            guacd_log_error("Could not accept client connection: %s",
+            guacd_log(GUAC_LOG_ERROR, "Could not accept client connection: %s",
                     strerror(errno));
             return 3;
         }
@@ -616,7 +701,7 @@ int main(int argc, char* argv[]) {
 
         /* If error, log */
         if (child_pid == -1)
-            guacd_log_error("Error forking child process: %s", strerror(errno));
+            guacd_log(GUAC_LOG_ERROR, "Error forking child process: %s", strerror(errno));
 
         /* If child, start client, and exit when finished */
         else if (child_pid == 0) {
@@ -629,7 +714,8 @@ int main(int argc, char* argv[]) {
             if (ssl_context != NULL) {
                 socket = guac_socket_open_secure(ssl_context, connected_socket_fd);
                 if (socket == NULL) {
-                    guacd_log_guac_error("Error opening secure connection");
+                    guacd_log_guac_error(GUAC_LOG_ERROR,
+                            "Unable to set up SSL/TLS");
                     return 0;
                 }
             }
@@ -640,14 +726,14 @@ int main(int argc, char* argv[]) {
             socket = guac_socket_open(connected_socket_fd);
 #endif
 
-            guacd_handle_connection(socket);
+            guacd_handle_connection(map, socket);
             close(connected_socket_fd);
             return 0;
         }
 
         /* If parent, close reference to child's descriptor */
         else if (close(connected_socket_fd) < 0) {
-            guacd_log_error("Error closing daemon reference to "
+            guacd_log(GUAC_LOG_ERROR, "Error closing daemon reference to "
                     "child descriptor: %s", strerror(errno));
         }
 
@@ -655,7 +741,7 @@ int main(int argc, char* argv[]) {
 
     /* Close socket */
     if (close(socket_fd) < 0) {
-        guacd_log_error("Could not close socket: %s", strerror(errno));
+        guacd_log(GUAC_LOG_ERROR, "Could not close socket: %s", strerror(errno));
         return 3;
     }
 
diff --git a/src/guacd/init.d/guacd.in b/src/guacd/init.d/guacd.in
index 1d93c65..d9de439 100644
--- a/src/guacd/init.d/guacd.in
+++ b/src/guacd/init.d/guacd.in
@@ -1,40 +1,25 @@
 #!/bin/sh
-
-# ***** BEGIN LICENSE BLOCK *****
-# Version: MPL 1.1/GPL 2.0/LGPL 2.1
-#
-# The contents of this file are subject to the Mozilla Public License Version
-# 1.1 (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.mozilla.org/MPL/
-#
-# Software distributed under the License is distributed on an "AS IS" basis,
-# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-# for the specific language governing rights and limitations under the
-# License.
 #
-# The Original Code is guacd.
+# Copyright (C) 2013 Glyptodon LLC
 #
-# The Initial Developer of the Original Code is
-# Michael Jumper.
-# Portions created by the Initial Developer are Copyright (C) 2010
-# the Initial Developer. All Rights Reserved.
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
 #
-# Contributor(s):
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
 #
-# Alternatively, the contents of this file may be used under the terms of
-# either the GNU General Public License Version 2 or later (the "GPL"), or
-# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-# in which case the provisions of the GPL or the LGPL are applicable instead
-# of those above. If you wish to allow use of your version of this file only
-# under the terms of either the GPL or the LGPL, and not to allow others to
-# use your version of this file under the terms of the MPL, indicate your
-# decision by deleting the provisions above and replace them with the notice
-# and other provisions required by the GPL or the LGPL. If you do not delete
-# the provisions above, a recipient may use your version of this file under
-# the terms of any one of the MPL, the GPL or the LGPL.
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
 #
-# ***** END LICENSE BLOCK *****
 
 # guacd
 #
diff --git a/src/guacd/log.c b/src/guacd/log.c
index d9efda1..375877c 100644
--- a/src/guacd/log.c
+++ b/src/guacd/log.c
@@ -1,136 +1,154 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is guacd.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+#include "log.h"
+
+#include <guacamole/client.h>
+#include <guacamole/error.h>
 
-#include <errno.h>
-#include <syslog.h>
 #include <stdarg.h>
 #include <stdio.h>
-#include <sys/types.h>
+#include <syslog.h>
 #include <unistd.h>
 
-#include <guacamole/client.h>
-#include <guacamole/error.h>
+int guacd_log_level = GUAC_LOG_INFO;
 
-/* Log prefix, defaulting to "guacd" */
-char log_prefix[64] = "guacd";
+void vguacd_log(guac_client_log_level level, const char* format,
+        va_list args) {
 
-void vguacd_log_info(const char* format, va_list args) {
+    const char* priority_name;
+    int priority;
 
-    /* Copy log message into buffer */
     char message[2048];
-    vsnprintf(message, sizeof(message), format, args);
-
-    /* Log to syslog */
-    syslog(LOG_INFO, "%s", message);
 
-    /* Log to STDERR */
-    fprintf(stderr, "%s[%i]: INFO:  %s\n", log_prefix, getpid(), message);
-
-}
-
-void vguacd_log_error(const char* format, va_list args) {
+    /* Don't bother if the log level is too high */
+    if (level > guacd_log_level)
+        return;
 
     /* Copy log message into buffer */
-    char message[2048];
     vsnprintf(message, sizeof(message), format, args);
 
+    /* Convert log level to syslog priority */
+    switch (level) {
+
+        /* Error log level */
+        case GUAC_LOG_ERROR:
+            priority = LOG_ERR;
+            priority_name = "ERROR";
+            break;
+
+        /* Warning log level */
+        case GUAC_LOG_WARNING:
+            priority = LOG_WARNING;
+            priority_name = "WARNING";
+            break;
+
+        /* Informational log level */
+        case GUAC_LOG_INFO:
+            priority = LOG_INFO;
+            priority_name = "INFO";
+            break;
+
+        /* Debug log level */
+        case GUAC_LOG_DEBUG:
+            priority = LOG_DEBUG;
+            priority_name = "DEBUG";
+            break;
+
+        /* Any unknown/undefined log level */
+        default:
+            priority = LOG_INFO;
+            priority_name = "UNKNOWN";
+            break;
+    }
+
     /* Log to syslog */
-    syslog(LOG_ERR, "%s", message);
+    syslog(priority, "%s", message);
 
     /* Log to STDERR */
-    fprintf(stderr, "%s[%i]: ERROR: %s\n", log_prefix, getpid(), message);
+    fprintf(stderr, GUACD_LOG_NAME "[%i]: %s:\t%s\n",
+            getpid(), priority_name, message);
 
 }
 
-void guacd_log_info(const char* format, ...) {
+void guacd_log(guac_client_log_level level, const char* format, ...) {
     va_list args;
     va_start(args, format);
-    vguacd_log_info(format, args);
+    vguacd_log(level, format, args);
     va_end(args);
 }
 
-void guacd_log_error(const char* format, ...) {
-    va_list args;
-    va_start(args, format);
-    vguacd_log_error(format, args);
-    va_end(args);
+void guacd_client_log(guac_client* client, guac_client_log_level level,
+        const char* format, va_list args) {
+    vguacd_log(level, format, args);
 }
 
-void guacd_client_log_info(guac_client* client, const char* format,
-        va_list args) {
-    vguacd_log_info(format, args);
-}
+void guacd_log_guac_error(guac_client_log_level level, const char* message) {
 
-void guacd_client_log_error(guac_client* client, const char* format,
-        va_list args) {
-    vguacd_log_error(format, args);
-}
+    if (guac_error != GUAC_STATUS_SUCCESS) {
 
-void guacd_log_guac_error(const char* message) {
+        /* If error message provided, include in log */
+        if (guac_error_message != NULL)
+            guacd_log(level, "%s: %s",
+                    message,
+                    guac_error_message);
 
-    /* If error message provided, include in log */
-    if (guac_error_message != NULL)
-        guacd_log_error("%s: %s: %s",
-                message,
-                guac_status_string(guac_error),
-                guac_error_message);
+        /* Otherwise just log with standard status string */
+        else
+            guacd_log(level, "%s: %s",
+                    message,
+                    guac_status_string(guac_error));
 
-    /* Otherwise just log with standard status string */
+    }
+
+    /* Just log message if no status code */
     else
-        guacd_log_error("%s: %s",
-                message,
-                guac_status_string(guac_error));
+        guacd_log(level, "%s", message);
 
 }
 
-void guacd_client_log_guac_error(guac_client* client, const char* message) {
+void guacd_client_log_guac_error(guac_client* client,
+        guac_client_log_level level, const char* message) {
+
+    if (guac_error != GUAC_STATUS_SUCCESS) {
+
+        /* If error message provided, include in log */
+        if (guac_error_message != NULL)
+            guac_client_log(client, level, "%s: %s",
+                    message,
+                    guac_error_message);
+
+        /* Otherwise just log with standard status string */
+        else
+            guac_client_log(client, level, "%s: %s",
+                    message,
+                    guac_status_string(guac_error));
 
-    /* If error message provided, include in log */
-    if (guac_error_message != NULL)
-        guac_client_log_error(client, "%s: %s: %s",
-                message,
-                guac_status_string(guac_error),
-                guac_error_message);
+    }
 
-    /* Otherwise just log with standard status string */
+    /* Just log message if no status code */
     else
-        guac_client_log_error(client, "%s: %s",
-                message,
-                guac_status_string(guac_error));
+        guac_client_log(client, level, "%s", message);
 
 }
 
diff --git a/src/guacd/log.h b/src/guacd/log.h
index 2c1f175..bdb8452 100644
--- a/src/guacd/log.h
+++ b/src/guacd/log.h
@@ -1,57 +1,77 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is guacd.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef __GUACD_LOG_H
 #define __GUACD_LOG_H
 
+#include "config.h"
+
 #include <guacamole/client.h>
 
-extern char log_prefix[64];
+/**
+ * The maximum level at which to log messages. All other messages will be
+ * dropped.
+ */
+extern int guacd_log_level;
+
+/**
+ * The string to prepend to all log messages.
+ */
+#define GUACD_LOG_NAME "guacd"
+
+/**
+ * Writes a message to guacd's logs. This function takes a format and va_list,
+ * similar to vprintf.
+ */
+void vguacd_log(guac_client_log_level level, const char* format, va_list args);
+
+/**
+ * Writes a message to guacd's logs. This function accepts parameters
+ * identically to printf.
+ */
+void guacd_log(guac_client_log_level level, const char* format, ...);
 
-void vguacd_log_info(const char* format, va_list args);
-void vguacd_log_error(const char* format, va_list args);
-void guacd_log_info(const char* format, ...);
-void guacd_log_error(const char* format, ...);
+/**
+ * Writes a message using the logging facilities of the given client. This
+ * function accepts parameters identically to printf.
+ */
+void guacd_client_log(guac_client* client, guac_client_log_level level,
+        const char* format, va_list args);
 
-void guacd_client_log_info(guac_client* client, const char* format, va_list args);
-void guacd_client_log_error(guac_client* client, const char* format, va_list args);
+/**
+ * Prints an error message to guacd's logs, automatically including any
+ * information present in guac_error. This function accepts parameters
+ * identically to printf.
+ */
+void guacd_log_guac_error(guac_client_log_level level, const char* message);
 
-void guacd_log_guac_error(const char* message);
-void guacd_client_log_guac_error(guac_client* client, const char* message);
+/**
+ * Prints an error message using the logging facilities of the given client,
+ * automatically including any information present in guac_error. This function
+ * accepts parameters identically to printf.
+ */
+void guacd_client_log_guac_error(guac_client* client,
+        guac_client_log_level level, const char* message);
 
 #endif
 
diff --git a/src/guacd/man/guacd.8 b/src/guacd/man/guacd.8
index ca4b34c..7a7b583 100644
--- a/src/guacd/man/guacd.8
+++ b/src/guacd/man/guacd.8
@@ -1,4 +1,4 @@
-.TH guacd 8 "22 Jul 2013" "version 0.8.2" "Guacamole"
+.TH guacd 8 "15 Dec 2015" "version 0.9.9" "Guacamole"
 .
 .SH NAME
 guacd \- Guacamole proxy daemon
@@ -8,6 +8,7 @@ guacd \- Guacamole proxy daemon
 [\fB-b\fR \fIHOST\fR]
 [\fB-l\fR \fIPORT\fR]
 [\fB-p\fR \fIPID FILE\fR]
+[\fB-L\fR \fILOG LEVEL\fR]
 [\fB-C\fR \fICERTIFICATE FILE\fR]
 [\fB-K\fR \fIKEY FILE\fR]
 [\fB-f\fR]
@@ -42,6 +43,19 @@ to write the PID of the daemon process to the specified
 file. This is useful for init scripts and is used by the provided init
 script.
 .TP
+\fB\-L\fR \fILEVEL\fR
+Sets the maximum level at which
+.B guacd
+will log messages to syslog and, if running in the foreground, the console.
+Legal values are
+.B debug,
+.B info,
+.B warning,
+and
+.B error.
+The default value is
+.B info.
+.TP
 \fB\-f\fR
 Causes
 .B guacd
@@ -55,6 +69,14 @@ was compiled, it will contain SSL/TLS support, and connections between the
 web application and
 .B guacd
 can be encrypted if a certificate file is given.
+.P
+When using a chain of certificates, you must append the additional certificates
+to your server certificate. This can be done easily with the standard
+.B cat
+command. Beware that the certificate for
+.B guacd
+.I must
+be the first certificate in the file.
 .TP
 \fB-C\fR \fICERTIFICATE FILE\fR
 Enables SSL/TLS using the given cerficiate file. Future connections to
@@ -70,5 +92,8 @@ this instance of
 will require SSL/TLS enabled in the client (the web application). If
 this option is not given, communication with guacd must be unencrypted.
 .
+.SH SEE ALSO
+.BR guacd.conf (5)
+.
 .SH AUTHOR
 Written by Michael Jumper <mike.jumper at guac-dev.org>
diff --git a/src/guacd/man/guacd.conf.5 b/src/guacd/man/guacd.conf.5
new file mode 100644
index 0000000..d98c71d
--- /dev/null
+++ b/src/guacd/man/guacd.conf.5
@@ -0,0 +1,162 @@
+.TH guacd.conf 5 "15 Dec 2015" "version 0.9.9" "Guacamole"
+.
+.SH NAME
+/etc/guacamole/guacd.conf \- Configuration file for guacd
+.
+.SH DESCRIPTION
+.B /etc/guacamole/guacd.conf
+is the configuration file for the Guacamole proxy daemon used by the Guacamole
+web application and framework,
+.B guacd.
+Use of this file is entirely optional, and all of its options can be specified
+from the command line when running
+.B guacd.
+If you provide both the
+.B guacd.conf
+file and command line options, the command line options will take precedence.
+.
+.SH SYNTAX
+.B guacd.conf
+is made up of sections, where each section contains a set of parameter/value
+pairs. The parameters available are dictated by the section in use, and
+parameters may only be specified within a section.
+.P
+The beginning of each section is denoted with a section name in brackets, and
+each section ends implicitly with the beginning of a new section, or at the end
+of the file.
+.TP
+\fB[server]\fR
+Contains parameters which control how
+.B guacd
+behaves as a server, from a network perspective.
+.TP
+\fB[daemon]\fR
+Parameters which configure how
+.B guacd
+behaves as a daemon, such as what file should contain the PID, if any.
+.TP
+\fB[ssl]\fR
+Parameters which control the SSL support of
+.B guacd,
+such as the certificate and private key used for encryption of the Guacamole
+protocol. This section and its parameters are only valid if
+.B guacd
+was built with SSL support.
+.P
+Parameters within sections are written as a parameter name, followed by an
+equals sign, followed by the parameter value, all on one line. Comments may be
+placed anywhere, and consist of arbitrary text following a
+.B #
+symbol until end-of-line:
+.TP
+\fIname\fR \fB=\fR \fIvalue\fR \fB#\fR \fISome arbitrary comment text\fR
+.P
+Beware that it is the combination of the section name with the parameter name
+that makes up the fully qualified name of a parameter. Each parameter
+absolutely
+.I must
+be placed only within its proper section, or
+.B guacd.conf
+will fail to be parsed, and
+.B guacd
+will not start.
+.P
+If special characters need to be placed within a parameter value, such as
+whitespace, \fB#\fR, \fB"\fR, or \fB\\\fR, the entire value must be enclosed in
+double quotes, and each occurrence of \fB"\fR or \fB\\\fR within the value must
+be escaped with backslashes:
+.TP
+\fIname\fR \fB=\fR \fB"\fR\fIquoted # value \\\\ with \\" special characters\fR\fB"\fR
+.
+.SH SERVER PARAMETERS
+.TP
+\fBbind_host\fR \fB=\fR \fIHOSTNAME\fR
+Requires
+.B guacd
+to bind to a specific host when listening for connections. By default,
+.B guacd
+will bind to localhost only.
+.TP
+\fBbind_port\fR \fB=\fR \fIPORT\fR
+Requires
+.B guacd
+to bind to a specific port when listening for connections. By default,
+.B guacd
+will bind to port 4822.
+.
+.SH DAEMON PARAMETERS
+.TP
+\fBlog_level\fR \fB=\fR \fILEVEL\fR
+Sets the maximum level at which
+.B guacd
+will log messages to syslog and, if running in the foreground, the console.
+Legal values are
+.B debug,
+.B info,
+.B warning,
+and
+.B error.
+The default value is
+.B info.
+.TP
+\fBpid_file\fR \fB=\fR \fIFILE\fR
+Causes
+.B guacd
+to write its PID to the specified file upon startup. Note that
+.B guacd
+must have sufficient privileges to create or write this file, or it will fail
+to start. This parameter is typically needed for startup scripts, such that the
+script can report on the status of
+.B guacd
+and kill it if necessary.
+.
+.SH SSL PARAMETERS
+If
+.B guacd
+was built with SSL support, then connections between the web application and
+.B guacd
+can be encrypted if an SSL certificate and key file are given.
+.P
+When using a chain of certificates, you must append the additional certificates
+to your server certificate. This can be done easily with the standard
+.B cat
+command. Beware that the certificate for
+.B guacd
+.I must
+be the first certificate in the file.
+.TP
+\fBserver_certificate\fR \fB=\fR \fICERTIFICATE FILE\fR
+Enables SSL/TLS using the given cerficiate file. Future connections to
+.B guacd
+will require SSL/TLS enabled in the client (the web application).
+.TP
+\fBserver_key\fR \fB=\fR \fIKEY FILE\fR
+Enables SSL/TLS using the given private key file. Future connections to
+.B guacd
+will require SSL/TLS enabled in the client (the web application).
+.
+.SH EXAMPLE
+.nf
+.RS
+#
+# guacd.conf example
+#
+
+[daemon]
+
+pid_file = /var/run/guacd.pid
+
+[server]
+
+bind_host = localhost
+bind_port = 4822
+
+[ssl]
+
+server_certificate = /etc/ssl/certs/guacd.crt
+server_key = /etc/ssl/private/guacd.key
+.RE
+.fi
+.
+.SH AUTHOR
+Written by Michael Jumper <mike.jumper at guac-dev.org>
diff --git a/src/guacd/socket-ssl.c b/src/guacd/socket-ssl.c
index 4a75b85..adbbd94 100644
--- a/src/guacd/socket-ssl.c
+++ b/src/guacd/socket-ssl.c
@@ -1,50 +1,35 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is guacd.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "socket-ssl.h"
 
 #include <stdlib.h>
 #include <sys/select.h>
 
-#include <openssl/ssl.h>
-
-#include <guacamole/socket.h>
 #include <guacamole/error.h>
-
-#include "socket-ssl.h"
-
+#include <guacamole/socket.h>
+#include <openssl/ssl.h>
 
 static ssize_t __guac_socket_ssl_read_handler(guac_socket* socket,
         void* buf, size_t count) {
@@ -114,7 +99,7 @@ static int __guac_socket_ssl_select_handler(guac_socket* socket, int usec_timeou
     }
 
     if (retval == 0) {
-        guac_error = GUAC_STATUS_INPUT_TIMEOUT;
+        guac_error = GUAC_STATUS_TIMEOUT;
         guac_error_message = "Timeout while waiting for data on secure socket";
     }
 
@@ -146,7 +131,7 @@ guac_socket* guac_socket_open_secure(SSL_CTX* context, int fd) {
     /* Accept SSL connection, handle errors */
     if (SSL_accept(data->ssl) <= 0) {
 
-        guac_error = GUAC_STATUS_BAD_STATE;
+        guac_error = GUAC_STATUS_INTERNAL_ERROR;
         guac_error_message = "SSL accept failed";
 
         free(data);
diff --git a/src/guacd/socket-ssl.h b/src/guacd/socket-ssl.h
index 8dfc8cf..3ad0a4d 100644
--- a/src/guacd/socket-ssl.h
+++ b/src/guacd/socket-ssl.h
@@ -1,45 +1,33 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is guacd.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef __GUACD_SOCKET_SSL_H
 #define __GUACD_SOCKET_SSL_H
 
-#include <openssl/ssl.h>
+#include "config.h"
+
 #include <guacamole/socket.h>
+#include <openssl/ssl.h>
 
 /**
  * SSL socket-specific data.
diff --git a/src/libguac/Makefile.am b/src/libguac/Makefile.am
index 8513054..039f23e 100644
--- a/src/libguac/Makefile.am
+++ b/src/libguac/Makefile.am
@@ -1,68 +1,80 @@
-# ***** BEGIN LICENSE BLOCK *****
-# Version: MPL 1.1/GPL 2.0/LGPL 2.1
 #
-# The contents of this file are subject to the Mozilla Public License Version
-# 1.1 (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.mozilla.org/MPL/
+# Copyright (C) 2015 Glyptodon LLC
 #
-# Software distributed under the License is distributed on an "AS IS" basis,
-# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-# for the specific language governing rights and limitations under the
-# License.
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
 #
-# The Original Code is libguac.
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
 #
-# The Initial Developer of the Original Code is
-# Michael Jumper.
-# Portions created by the Initial Developer are Copyright (C) 2010
-# the Initial Developer. All Rights Reserved.
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
 #
-# Contributor(s):
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either the GNU General Public License Version 2 or later (the "GPL"), or
-# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-# in which case the provisions of the GPL or the LGPL are applicable instead
-# of those above. If you wish to allow use of your version of this file only
-# under the terms of either the GPL or the LGPL, and not to allow others to
-# use your version of this file under the terms of the MPL, indicate your
-# decision by deleting the provisions above and replace them with the notice
-# and other provisions required by the GPL or the LGPL. If you do not delete
-# the provisions above, a recipient may use your version of this file under
-# the terms of any one of the MPL, the GPL or the LGPL.
-#
-# ***** END LICENSE BLOCK *****
 
 AUTOMAKE_OPTIONS = foreign 
 ACLOCAL_AMFLAGS = -I m4
-AM_CFLAGS = -Werror -Wall -pedantic -Iguacamole
+
+lib_LTLIBRARIES = libguac.la
 
 libguacincdir = $(includedir)/guacamole
-libguacinc_HEADERS =         \
-    guacamole/audio.h        \
-    guacamole/client.h       \
-    guacamole/error.h        \
-    guacamole/hash.h         \
-    guacamole/instruction.h  \
-    guacamole/layer.h        \
-    guacamole/plugin.h       \
-    guacamole/pool.h         \
-    guacamole/protocol.h     \
-    guacamole/socket.h       \
-    guacamole/stream.h       \
-    guacamole/timestamp.h    \
+
+libguacinc_HEADERS =                  \
+    guacamole/audio.h                 \
+    guacamole/audio-fntypes.h         \
+    guacamole/audio-types.h           \
+    guacamole/client-constants.h      \
+    guacamole/client.h                \
+    guacamole/client-fntypes.h        \
+    guacamole/client-types.h          \
+    guacamole/error.h                 \
+    guacamole/error-types.h           \
+    guacamole/hash.h                  \
+    guacamole/instruction-constants.h \
+    guacamole/instruction.h           \
+    guacamole/instruction-types.h     \
+    guacamole/layer.h                 \
+    guacamole/layer-types.h           \
+    guacamole/object.h                \
+    guacamole/object-types.h          \
+    guacamole/plugin-constants.h      \
+    guacamole/plugin.h                \
+    guacamole/plugin-types.h          \
+    guacamole/pool.h                  \
+    guacamole/pool-types.h            \
+    guacamole/protocol.h              \
+    guacamole/protocol-types.h        \
+    guacamole/socket-constants.h      \
+    guacamole/socket.h                \
+    guacamole/socket-fntypes.h        \
+    guacamole/socket-types.h          \
+    guacamole/stream.h                \
+    guacamole/stream-types.h          \
+    guacamole/timestamp.h             \
+    guacamole/timestamp-types.h       \
     guacamole/unicode.h
 
 noinst_HEADERS =      \
     client-handlers.h \
+    encode-jpeg.h     \
+    encode-png.h      \
     palette.h         \
-    wav_encoder.h
+    raw_encoder.h
 
 libguac_la_SOURCES =  \
     audio.c           \
     client.c          \
     client-handlers.c \
+    encode-jpeg.c     \
+    encode-png.c      \
     error.c           \
     hash.c            \
     instruction.c     \
@@ -70,19 +82,32 @@ libguac_la_SOURCES =  \
     plugin.c          \
     pool.c            \
     protocol.c        \
+    raw_encoder.c     \
     socket.c          \
     socket-fd.c       \
     socket-nest.c     \
     timestamp.c       \
-    unicode.c         \
-    wav_encoder.c
+    unicode.c
 
-# Compile OGG support if available
-if ENABLE_OGG
-libguac_la_SOURCES += ogg_encoder.c
-noinst_HEADERS += ogg_encoder.h
+# Compile WebP support if available
+if ENABLE_WEBP
+libguac_la_SOURCES += encode-webp.c
+noinst_HEADERS += encode-webp.h
 endif
 
-lib_LTLIBRARIES = libguac.la
-libguac_la_LDFLAGS = -version-info 5:0:0 @PTHREAD_LIBS@ @CAIRO_LIBS@ @PNG_LIBS@ @DL_LIBS@ @VORBIS_LIBS@
 
+libguac_la_CFLAGS = \
+    -Werror -Wall -pedantic -Iguacamole
+
+libguac_la_LDFLAGS =     \
+    -version-info 11:0:0 \
+    @CAIRO_LIBS@         \
+    @JPEG_LIBS@          \
+    @PNG_LIBS@           \
+    @PTHREAD_LIBS@       \
+    @UUID_LIBS@          \
+    @VORBIS_LIBS@        \
+    @WEBP_LIBS@
+
+libguac_la_LIBADD = \
+    @LIBADD_DLOPEN@ 
diff --git a/src/libguac/Makefile.in b/src/libguac/Makefile.in
index f3a14b6..c7dbe5c 100644
--- a/src/libguac/Makefile.in
+++ b/src/libguac/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.6 from Makefile.am.
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software
-# Foundation, Inc.
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
@@ -15,61 +14,75 @@
 
 @SET_MAKE@
 
-# ***** BEGIN LICENSE BLOCK *****
-# Version: MPL 1.1/GPL 2.0/LGPL 2.1
-#
-# The contents of this file are subject to the Mozilla Public License Version
-# 1.1 (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.mozilla.org/MPL/
 #
-# Software distributed under the License is distributed on an "AS IS" basis,
-# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-# for the specific language governing rights and limitations under the
-# License.
+# Copyright (C) 2015 Glyptodon LLC
 #
-# The Original Code is libguac.
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
 #
-# The Initial Developer of the Original Code is
-# Michael Jumper.
-# Portions created by the Initial Developer are Copyright (C) 2010
-# the Initial Developer. All Rights Reserved.
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
 #
-# Contributor(s):
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
 #
-# Alternatively, the contents of this file may be used under the terms of
-# either the GNU General Public License Version 2 or later (the "GPL"), or
-# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-# in which case the provisions of the GPL or the LGPL are applicable instead
-# of those above. If you wish to allow use of your version of this file only
-# under the terms of either the GPL or the LGPL, and not to allow others to
-# use your version of this file under the terms of the MPL, indicate your
-# decision by deleting the provisions above and replace them with the notice
-# and other provisions required by the GPL or the LGPL. If you do not delete
-# the provisions above, a recipient may use your version of this file under
-# the terms of any one of the MPL, the GPL or the LGPL.
-#
-# ***** END LICENSE BLOCK *****
 
 
 VPATH = @srcdir@
-am__make_dryrun = \
-  { \
-    am__dry=no; \
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
     case $$MAKEFLAGS in \
       *\\[\ \	]*) \
-        echo 'am--echo: ; @echo "AM"  OK' | $(MAKE) -f - 2>/dev/null \
-          | grep '^AM OK$$' >/dev/null || am__dry=yes;; \
-      *) \
-        for am__flg in $$MAKEFLAGS; do \
-          case $$am__flg in \
-            *=*|--*) ;; \
-            *n*) am__dry=yes; break;; \
-          esac; \
-        done;; \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
     esac; \
-    test $$am__dry = yes; \
-  }
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
 pkgdatadir = $(datadir)/@PACKAGE@
 pkgincludedir = $(includedir)/@PACKAGE@
 pkglibdir = $(libdir)/@PACKAGE@
@@ -89,12 +102,13 @@ POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
 
-# Compile OGG support if available
- at ENABLE_OGG_TRUE@am__append_1 = ogg_encoder.c
- at ENABLE_OGG_TRUE@am__append_2 = ogg_encoder.h
+# Compile WebP support if available
+ at ENABLE_WEBP_TRUE@am__append_1 = encode-webp.c
+ at ENABLE_WEBP_TRUE@am__append_2 = encode-webp.h
 subdir = src/libguac
-DIST_COMMON = $(am__noinst_HEADERS_DIST) $(libguacinc_HEADERS) \
-	$(srcdir)/Makefile.am $(srcdir)/Makefile.in
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(top_srcdir)/depcomp $(libguacinc_HEADERS) \
+	$(am__noinst_HEADERS_DIST)
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
 	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
@@ -103,6 +117,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
 CONFIG_CLEAN_FILES =
 CONFIG_CLEAN_VPATH_FILES =
 am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
@@ -134,33 +149,63 @@ am__uninstall_files_from_dir = { \
   }
 am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libguacincdir)"
 LTLIBRARIES = $(lib_LTLIBRARIES)
-libguac_la_LIBADD =
+libguac_la_DEPENDENCIES =
 am__libguac_la_SOURCES_DIST = audio.c client.c client-handlers.c \
-	error.c hash.c instruction.c palette.c plugin.c pool.c \
-	protocol.c socket.c socket-fd.c socket-nest.c timestamp.c \
-	unicode.c wav_encoder.c ogg_encoder.c
- at ENABLE_OGG_TRUE@am__objects_1 = ogg_encoder.lo
-am_libguac_la_OBJECTS = audio.lo client.lo client-handlers.lo error.lo \
-	hash.lo instruction.lo palette.lo plugin.lo pool.lo \
-	protocol.lo socket.lo socket-fd.lo socket-nest.lo timestamp.lo \
-	unicode.lo wav_encoder.lo $(am__objects_1)
+	encode-jpeg.c encode-png.c error.c hash.c instruction.c \
+	palette.c plugin.c pool.c protocol.c raw_encoder.c socket.c \
+	socket-fd.c socket-nest.c timestamp.c unicode.c encode-webp.c
+ at ENABLE_WEBP_TRUE@am__objects_1 = libguac_la-encode-webp.lo
+am_libguac_la_OBJECTS = libguac_la-audio.lo libguac_la-client.lo \
+	libguac_la-client-handlers.lo libguac_la-encode-jpeg.lo \
+	libguac_la-encode-png.lo libguac_la-error.lo \
+	libguac_la-hash.lo libguac_la-instruction.lo \
+	libguac_la-palette.lo libguac_la-plugin.lo libguac_la-pool.lo \
+	libguac_la-protocol.lo libguac_la-raw_encoder.lo \
+	libguac_la-socket.lo libguac_la-socket-fd.lo \
+	libguac_la-socket-nest.lo libguac_la-timestamp.lo \
+	libguac_la-unicode.lo $(am__objects_1)
 libguac_la_OBJECTS = $(am_libguac_la_OBJECTS)
-libguac_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
-	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
-	$(libguac_la_LDFLAGS) $(LDFLAGS) -o $@
-DEFAULT_INCLUDES = -I. at am__isrc@
+AM_V_lt = $(am__v_lt_ at AM_V@)
+am__v_lt_ = $(am__v_lt_ at AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+libguac_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(libguac_la_CFLAGS) \
+	$(CFLAGS) $(libguac_la_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_ at AM_V@)
+am__v_P_ = $(am__v_P_ at AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_ at AM_V@)
+am__v_GEN_ = $(am__v_GEN_ at AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_ at AM_V@)
+am__v_at_ = $(am__v_at_ at AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I. at am__isrc@ -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/depcomp
 am__depfiles_maybe = depfiles
 am__mv = mv -f
 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
 	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
-	--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
-	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_ at AM_V@)
+am__v_CC_ = $(am__v_CC_ at AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
 CCLD = $(CC)
-LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
-	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
-	$(LDFLAGS) -o $@
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_ at AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_ at AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
 SOURCES = $(libguac_la_SOURCES)
 DIST_SOURCES = $(am__libguac_la_SOURCES_DIST)
 am__can_run_installinfo = \
@@ -168,14 +213,32 @@ am__can_run_installinfo = \
     n|no|NO) false;; \
     *) (install-info --version) >/dev/null 2>&1;; \
   esac
-am__noinst_HEADERS_DIST = client-handlers.h palette.h wav_encoder.h \
-	ogg_encoder.h
+am__noinst_HEADERS_DIST = client-handlers.h encode-jpeg.h encode-png.h \
+	palette.h raw_encoder.h encode-webp.h
 HEADERS = $(libguacinc_HEADERS) $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
 ETAGS = etags
 CTAGS = ctags
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 ACLOCAL = @ACLOCAL@
 AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -185,6 +248,10 @@ CAIRO_LIBS = @CAIRO_LIBS@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+COMMON_INCLUDE = @COMMON_INCLUDE@
+COMMON_LTLIB = @COMMON_LTLIB@
+COMMON_SSH_INCLUDE = @COMMON_SSH_INCLUDE@
+COMMON_SSH_LTLIB = @COMMON_SSH_LTLIB@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
 CUNIT_LIBS = @CUNIT_LIBS@
@@ -192,7 +259,6 @@ CYGPATH_W = @CYGPATH_W@
 DEFS = @DEFS@
 DEPDIR = @DEPDIR@
 DLLTOOL = @DLLTOOL@
-DL_LIBS = @DL_LIBS@
 DSYMUTIL = @DSYMUTIL@
 DUMPBIN = @DUMPBIN@
 ECHO_C = @ECHO_C@
@@ -207,8 +273,10 @@ INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
 INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+JPEG_LIBS = @JPEG_LIBS@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
+LIBADD_DLOPEN = @LIBADD_DLOPEN@
 LIBGUAC_INCLUDE = @LIBGUAC_INCLUDE@
 LIBGUAC_LTLIB = @LIBGUAC_LTLIB@
 LIBOBJS = @LIBOBJS@
@@ -219,6 +287,7 @@ LN_S = @LN_S@
 LTLIBOBJS = @LTLIBOBJS@
 MAKEINFO = @MAKEINFO@
 MANIFEST_TOOL = @MANIFEST_TOOL@
+MATH_LIBS = @MATH_LIBS@
 MKDIR_P = @MKDIR_P@
 NM = @NM@
 NMEDIT = @NMEDIT@
@@ -252,9 +321,14 @@ SHELL = @SHELL@
 SSH_LIBS = @SSH_LIBS@
 SSL_LIBS = @SSL_LIBS@
 STRIP = @STRIP@
+TELNET_LIBS = @TELNET_LIBS@
+TERMINAL_INCLUDE = @TERMINAL_INCLUDE@
+TERMINAL_LTLIB = @TERMINAL_LTLIB@
+UUID_LIBS = @UUID_LIBS@
 VERSION = @VERSION@
 VNC_LIBS = @VNC_LIBS@
 VORBIS_LIBS = @VORBIS_LIBS@
+WEBP_LIBS = @WEBP_LIBS@
 abs_builddir = @abs_builddir@
 abs_srcdir = @abs_srcdir@
 abs_top_builddir = @abs_top_builddir@
@@ -310,31 +384,65 @@ top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
 AUTOMAKE_OPTIONS = foreign 
 ACLOCAL_AMFLAGS = -I m4
-AM_CFLAGS = -Werror -Wall -pedantic -Iguacamole
+lib_LTLIBRARIES = libguac.la
 libguacincdir = $(includedir)/guacamole
 libguacinc_HEADERS = \
-    guacamole/audio.h        \
-    guacamole/client.h       \
-    guacamole/error.h        \
-    guacamole/hash.h         \
-    guacamole/instruction.h  \
-    guacamole/layer.h        \
-    guacamole/plugin.h       \
-    guacamole/pool.h         \
-    guacamole/protocol.h     \
-    guacamole/socket.h       \
-    guacamole/stream.h       \
-    guacamole/timestamp.h    \
+    guacamole/audio.h                 \
+    guacamole/audio-fntypes.h         \
+    guacamole/audio-types.h           \
+    guacamole/client-constants.h      \
+    guacamole/client.h                \
+    guacamole/client-fntypes.h        \
+    guacamole/client-types.h          \
+    guacamole/error.h                 \
+    guacamole/error-types.h           \
+    guacamole/hash.h                  \
+    guacamole/instruction-constants.h \
+    guacamole/instruction.h           \
+    guacamole/instruction-types.h     \
+    guacamole/layer.h                 \
+    guacamole/layer-types.h           \
+    guacamole/object.h                \
+    guacamole/object-types.h          \
+    guacamole/plugin-constants.h      \
+    guacamole/plugin.h                \
+    guacamole/plugin-types.h          \
+    guacamole/pool.h                  \
+    guacamole/pool-types.h            \
+    guacamole/protocol.h              \
+    guacamole/protocol-types.h        \
+    guacamole/socket-constants.h      \
+    guacamole/socket.h                \
+    guacamole/socket-fntypes.h        \
+    guacamole/socket-types.h          \
+    guacamole/stream.h                \
+    guacamole/stream-types.h          \
+    guacamole/timestamp.h             \
+    guacamole/timestamp-types.h       \
     guacamole/unicode.h
 
-noinst_HEADERS = client-handlers.h palette.h wav_encoder.h \
-	$(am__append_2)
-libguac_la_SOURCES = audio.c client.c client-handlers.c error.c hash.c \
-	instruction.c palette.c plugin.c pool.c protocol.c socket.c \
-	socket-fd.c socket-nest.c timestamp.c unicode.c wav_encoder.c \
-	$(am__append_1)
-lib_LTLIBRARIES = libguac.la
-libguac_la_LDFLAGS = -version-info 5:0:0 @PTHREAD_LIBS@ @CAIRO_LIBS@ @PNG_LIBS@ @DL_LIBS@ @VORBIS_LIBS@
+noinst_HEADERS = client-handlers.h encode-jpeg.h encode-png.h \
+	palette.h raw_encoder.h $(am__append_2)
+libguac_la_SOURCES = audio.c client.c client-handlers.c encode-jpeg.c \
+	encode-png.c error.c hash.c instruction.c palette.c plugin.c \
+	pool.c protocol.c raw_encoder.c socket.c socket-fd.c \
+	socket-nest.c timestamp.c unicode.c $(am__append_1)
+libguac_la_CFLAGS = \
+    -Werror -Wall -pedantic -Iguacamole
+
+libguac_la_LDFLAGS = \
+    -version-info 11:0:0 \
+    @CAIRO_LIBS@         \
+    @JPEG_LIBS@          \
+    @PNG_LIBS@           \
+    @PTHREAD_LIBS@       \
+    @UUID_LIBS@          \
+    @VORBIS_LIBS@        \
+    @WEBP_LIBS@
+
+libguac_la_LIBADD = \
+    @LIBADD_DLOPEN@ 
+
 all: all-am
 
 .SUFFIXES:
@@ -369,6 +477,7 @@ $(top_srcdir)/configure:  $(am__configure_deps)
 $(ACLOCAL_M4):  $(am__aclocal_m4_deps)
 	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
 $(am__aclocal_m4_deps):
+
 install-libLTLIBRARIES: $(lib_LTLIBRARIES)
 	@$(NORMAL_INSTALL)
 	@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
@@ -395,14 +504,17 @@ uninstall-libLTLIBRARIES:
 
 clean-libLTLIBRARIES:
 	-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
-	@list='$(lib_LTLIBRARIES)'; for p in $$list; do \
-	  dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
-	  test "$$dir" != "$$p" || dir=.; \
-	  echo "rm -f \"$${dir}/so_locations\""; \
-	  rm -f "$${dir}/so_locations"; \
-	done
+	@list='$(lib_LTLIBRARIES)'; \
+	locs=`for p in $$list; do echo $$p; done | \
+	      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+	      sort -u`; \
+	test -z "$$locs" || { \
+	  echo rm -f $${locs}; \
+	  rm -f $${locs}; \
+	}
+
 libguac.la: $(libguac_la_OBJECTS) $(libguac_la_DEPENDENCIES) $(EXTRA_libguac_la_DEPENDENCIES) 
-	$(libguac_la_LINK) -rpath $(libdir) $(libguac_la_OBJECTS) $(libguac_la_LIBADD) $(LIBS)
+	$(AM_V_CCLD)$(libguac_la_LINK) -rpath $(libdir) $(libguac_la_OBJECTS) $(libguac_la_LIBADD) $(LIBS)
 
 mostlyclean-compile:
 	-rm -f *.$(OBJEXT)
@@ -410,44 +522,182 @@ mostlyclean-compile:
 distclean-compile:
 	-rm -f *.tab.c
 
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/audio.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/client-handlers.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/client.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/error.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/hash.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/instruction.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/ogg_encoder.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/palette.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/plugin.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/pool.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/protocol.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/socket-fd.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/socket-nest.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/socket.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/timestamp.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/unicode.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/wav_encoder.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_la-audio.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_la-client-handlers.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_la-client.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_la-encode-jpeg.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_la-encode-png.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_la-encode-webp.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_la-error.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_la-hash.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_la-instruction.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_la-palette.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_la-plugin.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_la-pool.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_la-protocol.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_la-raw_encoder.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_la-socket-fd.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_la-socket-nest.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_la-socket.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_la-timestamp.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_la-unicode.Plo at am__quote@
 
 .c.o:
- at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+ at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(COMPILE) -c $<
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(COMPILE) -c -o $@ $<
 
 .c.obj:
- at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+ at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(COMPILE) -c `$(CYGPATH_W) '$<'`
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
 
 .c.lo:
- at am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+ at am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+libguac_la-audio.lo: audio.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -MT libguac_la-audio.lo -MD -MP -MF $(DEPDIR)/libguac_la-audio.Tpo -c -o libguac_la-audio.lo `test -f 'audio.c' || echo '$(srcdir)/'`audio.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_la-audio.Tpo $(DEPDIR)/libguac_la-audio.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='audio.c' object='libguac_la-audio.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -c -o libguac_la-audio.lo `test -f 'audio.c' || echo '$(srcdir)/'`audio.c
+
+libguac_la-client.lo: client.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -MT libguac_la-client.lo -MD -MP -MF $(DEPDIR)/libguac_la-client.Tpo -c -o libguac_la-client.lo `test -f 'client.c' || echo '$(srcdir)/'`client.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_la-client.Tpo $(DEPDIR)/libguac_la-client.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='client.c' object='libguac_la-client.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -c -o libguac_la-client.lo `test -f 'client.c' || echo '$(srcdir)/'`client.c
+
+libguac_la-client-handlers.lo: client-handlers.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -MT libguac_la-client-handlers.lo -MD -MP -MF $(DEPDIR)/libguac_la-client-handlers.Tpo -c -o libguac_la-client-handlers.lo `test -f 'client-handlers.c' || echo '$(srcdir)/'`client-handlers.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_la-client-handlers.Tpo $(DEPDIR)/libguac_la-client-handlers.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='client-handlers.c' object='libguac_la-client-handlers.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -c -o libguac_la-client-handlers.lo `test -f 'client-handlers.c' || echo '$(srcdir)/'`client-handlers.c
+
+libguac_la-encode-jpeg.lo: encode-jpeg.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -MT libguac_la-encode-jpeg.lo -MD -MP -MF $(DEPDIR)/libguac_la-encode-jpeg.Tpo -c -o libguac_la-encode-jpeg.lo `test -f 'encode-jpeg.c' || echo '$(srcdir)/'`encode-jpeg.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_la-encode-jpeg.Tpo $(DEPDIR)/libguac_la-encode-jpeg.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='encode-jpeg.c' object='libguac_la-encode-jpeg.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -c -o libguac_la-encode-jpeg.lo `test -f 'encode-jpeg.c' || echo '$(srcdir)/'`encode-jpeg.c
+
+libguac_la-encode-png.lo: encode-png.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -MT libguac_la-encode-png.lo -MD -MP -MF $(DEPDIR)/libguac_la-encode-png.Tpo -c -o libguac_la-encode-png.lo `test -f 'encode-png.c' || echo '$(srcdir)/'`encode-png.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_la-encode-png.Tpo $(DEPDIR)/libguac_la-encode-png.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='encode-png.c' object='libguac_la-encode-png.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -c -o libguac_la-encode-png.lo `test -f 'encode-png.c' || echo '$(srcdir)/'`encode-png.c
+
+libguac_la-error.lo: error.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -MT libguac_la-error.lo -MD -MP -MF $(DEPDIR)/libguac_la-error.Tpo -c -o libguac_la-error.lo `test -f 'error.c' || echo '$(srcdir)/'`error.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_la-error.Tpo $(DEPDIR)/libguac_la-error.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='error.c' object='libguac_la-error.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -c -o libguac_la-error.lo `test -f 'error.c' || echo '$(srcdir)/'`error.c
+
+libguac_la-hash.lo: hash.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -MT libguac_la-hash.lo -MD -MP -MF $(DEPDIR)/libguac_la-hash.Tpo -c -o libguac_la-hash.lo `test -f 'hash.c' || echo '$(srcdir)/'`hash.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_la-hash.Tpo $(DEPDIR)/libguac_la-hash.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='hash.c' object='libguac_la-hash.lo' libtool=yes @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -c -o libguac_la-hash.lo `test -f 'hash.c' || echo '$(srcdir)/'`hash.c
+
+libguac_la-instruction.lo: instruction.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -MT libguac_la-instruction.lo -MD -MP -MF $(DEPDIR)/libguac_la-instruction.Tpo -c -o libguac_la-instruction.lo `test -f 'instruction.c' || echo '$(srcdir)/'`instruction.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_la-instruction.Tpo $(DEPDIR)/libguac_la-instruction.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='instruction.c' object='libguac_la-instruction.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -c -o libguac_la-instruction.lo `test -f 'instruction.c' || echo '$(srcdir)/'`instruction.c
+
+libguac_la-palette.lo: palette.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -MT libguac_la-palette.lo -MD -MP -MF $(DEPDIR)/libguac_la-palette.Tpo -c -o libguac_la-palette.lo `test -f 'palette.c' || echo '$(srcdir)/'`palette.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_la-palette.Tpo $(DEPDIR)/libguac_la-palette.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='palette.c' object='libguac_la-palette.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -c -o libguac_la-palette.lo `test -f 'palette.c' || echo '$(srcdir)/'`palette.c
+
+libguac_la-plugin.lo: plugin.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -MT libguac_la-plugin.lo -MD -MP -MF $(DEPDIR)/libguac_la-plugin.Tpo -c -o libguac_la-plugin.lo `test -f 'plugin.c' || echo '$(srcdir)/'`plugin.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_la-plugin.Tpo $(DEPDIR)/libguac_la-plugin.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='plugin.c' object='libguac_la-plugin.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -c -o libguac_la-plugin.lo `test -f 'plugin.c' || echo '$(srcdir)/'`plugin.c
+
+libguac_la-pool.lo: pool.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -MT libguac_la-pool.lo -MD -MP -MF $(DEPDIR)/libguac_la-pool.Tpo -c -o libguac_la-pool.lo `test -f 'pool.c' || echo '$(srcdir)/'`pool.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_la-pool.Tpo $(DEPDIR)/libguac_la-pool.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='pool.c' object='libguac_la-pool.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -c -o libguac_la-pool.lo `test -f 'pool.c' || echo '$(srcdir)/'`pool.c
+
+libguac_la-protocol.lo: protocol.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -MT libguac_la-protocol.lo -MD -MP -MF $(DEPDIR)/libguac_la-protocol.Tpo -c -o libguac_la-protocol.lo `test -f 'protocol.c' || echo '$(srcdir)/'`protocol.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_la-protocol.Tpo $(DEPDIR)/libguac_la-protocol.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='protocol.c' object='libguac_la-protocol.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -c -o libguac_la-protocol.lo `test -f 'protocol.c' || echo '$(srcdir)/'`protocol.c
+
+libguac_la-raw_encoder.lo: raw_encoder.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -MT libguac_la-raw_encoder.lo -MD -MP -MF $(DEPDIR)/libguac_la-raw_encoder.Tpo -c -o libguac_la-raw_encoder.lo `test -f 'raw_encoder.c' || echo '$(srcdir)/'`raw_encoder.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_la-raw_encoder.Tpo $(DEPDIR)/libguac_la-raw_encoder.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='raw_encoder.c' object='libguac_la-raw_encoder.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -c -o libguac_la-raw_encoder.lo `test -f 'raw_encoder.c' || echo '$(srcdir)/'`raw_encoder.c
+
+libguac_la-socket.lo: socket.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -MT libguac_la-socket.lo -MD -MP -MF $(DEPDIR)/libguac_la-socket.Tpo -c -o libguac_la-socket.lo `test -f 'socket.c' || echo '$(srcdir)/'`socket.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_la-socket.Tpo $(DEPDIR)/libguac_la-socket.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='socket.c' object='libguac_la-socket.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -c -o libguac_la-socket.lo `test -f 'socket.c' || echo '$(srcdir)/'`socket.c
+
+libguac_la-socket-fd.lo: socket-fd.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -MT libguac_la-socket-fd.lo -MD -MP -MF $(DEPDIR)/libguac_la-socket-fd.Tpo -c -o libguac_la-socket-fd.lo `test -f 'socket-fd.c' || echo '$(srcdir)/'`socket-fd.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_la-socket-fd.Tpo $(DEPDIR)/libguac_la-socket-fd.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='socket-fd.c' object='libguac_la-socket-fd.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -c -o libguac_la-socket-fd.lo `test -f 'socket-fd.c' || echo '$(srcdir)/'`socket-fd.c
+
+libguac_la-socket-nest.lo: socket-nest.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -MT libguac_la-socket-nest.lo -MD -MP -MF $(DEPDIR)/libguac_la-socket-nest.Tpo -c -o libguac_la-socket-nest.lo `test -f 'socket-nest.c' || echo '$(srcdir)/'`socket-nest.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_la-socket-nest.Tpo $(DEPDIR)/libguac_la-socket-nest.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='socket-nest.c' object='libguac_la-socket-nest.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -c -o libguac_la-socket-nest.lo `test -f 'socket-nest.c' || echo '$(srcdir)/'`socket-nest.c
+
+libguac_la-timestamp.lo: timestamp.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -MT libguac_la-timestamp.lo -MD -MP -MF $(DEPDIR)/libguac_la-timestamp.Tpo -c -o libguac_la-timestamp.lo `test -f 'timestamp.c' || echo '$(srcdir)/'`timestamp.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_la-timestamp.Tpo $(DEPDIR)/libguac_la-timestamp.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='timestamp.c' object='libguac_la-timestamp.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -c -o libguac_la-timestamp.lo `test -f 'timestamp.c' || echo '$(srcdir)/'`timestamp.c
+
+libguac_la-unicode.lo: unicode.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -MT libguac_la-unicode.lo -MD -MP -MF $(DEPDIR)/libguac_la-unicode.Tpo -c -o libguac_la-unicode.lo `test -f 'unicode.c' || echo '$(srcdir)/'`unicode.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_la-unicode.Tpo $(DEPDIR)/libguac_la-unicode.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='unicode.c' object='libguac_la-unicode.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -c -o libguac_la-unicode.lo `test -f 'unicode.c' || echo '$(srcdir)/'`unicode.c
+
+libguac_la-encode-webp.lo: encode-webp.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -MT libguac_la-encode-webp.lo -MD -MP -MF $(DEPDIR)/libguac_la-encode-webp.Tpo -c -o libguac_la-encode-webp.lo `test -f 'encode-webp.c' || echo '$(srcdir)/'`encode-webp.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_la-encode-webp.Tpo $(DEPDIR)/libguac_la-encode-webp.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='encode-webp.c' object='libguac_la-encode-webp.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_la_CFLAGS) $(CFLAGS) -c -o libguac_la-encode-webp.lo `test -f 'encode-webp.c' || echo '$(srcdir)/'`encode-webp.c
 
 mostlyclean-libtool:
 	-rm -f *.lo
@@ -476,26 +726,15 @@ uninstall-libguacincHEADERS:
 	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
 	dir='$(DESTDIR)$(libguacincdir)'; $(am__uninstall_files_from_dir)
 
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
-	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
-	unique=`for i in $$list; do \
-	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-	  done | \
-	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
-	      END { if (nonempty) { for (i in files) print i; }; }'`; \
-	mkid -fID $$unique
-tags: TAGS
-
-TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
-		$(TAGS_FILES) $(LISP)
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
 	set x; \
 	here=`pwd`; \
-	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
-	unique=`for i in $$list; do \
-	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-	  done | \
-	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
-	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	$(am__define_uniq_tagged_files); \
 	shift; \
 	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
 	  test -n "$$unique" || unique=$$empty_fix; \
@@ -507,15 +746,11 @@ TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
 	      $$unique; \
 	  fi; \
 	fi
-ctags: CTAGS
-CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
-		$(TAGS_FILES) $(LISP)
-	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
-	unique=`for i in $$list; do \
-	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-	  done | \
-	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
-	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
 	test -z "$(CTAGS_ARGS)$$unique" \
 	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
 	     $$unique
@@ -524,6 +759,21 @@ GTAGS:
 	here=`$(am__cd) $(top_builddir) && pwd` \
 	  && $(am__cd) $(top_srcdir) \
 	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
 
 distclean-tags:
 	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
@@ -668,20 +918,21 @@ uninstall-am: uninstall-libLTLIBRARIES uninstall-libguacincHEADERS
 
 .MAKE: install-am install-strip
 
-.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
-	clean-libLTLIBRARIES clean-libtool ctags distclean \
-	distclean-compile distclean-generic distclean-libtool \
-	distclean-tags distdir dvi dvi-am html html-am info info-am \
-	install install-am install-data install-data-am install-dvi \
-	install-dvi-am install-exec install-exec-am install-html \
-	install-html-am install-info install-info-am \
-	install-libLTLIBRARIES install-libguacincHEADERS install-man \
-	install-pdf install-pdf-am install-ps install-ps-am \
-	install-strip installcheck installcheck-am installdirs \
-	maintainer-clean maintainer-clean-generic mostlyclean \
-	mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
-	pdf pdf-am ps ps-am tags uninstall uninstall-am \
-	uninstall-libLTLIBRARIES uninstall-libguacincHEADERS
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
+	clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \
+	ctags-am distclean distclean-compile distclean-generic \
+	distclean-libtool distclean-tags distdir dvi dvi-am html \
+	html-am info info-am install install-am install-data \
+	install-data-am install-dvi install-dvi-am install-exec \
+	install-exec-am install-html install-html-am install-info \
+	install-info-am install-libLTLIBRARIES \
+	install-libguacincHEADERS install-man install-pdf \
+	install-pdf-am install-ps install-ps-am install-strip \
+	installcheck installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+	tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES \
+	uninstall-libguacincHEADERS
 
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
diff --git a/src/libguac/audio.c b/src/libguac/audio.c
index fb8bcd3..8d340be 100644
--- a/src/libguac/audio.c
+++ b/src/libguac/audio.c
@@ -1,55 +1,39 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <stdlib.h>
-#include <string.h>
-#include <pthread.h>
-#include <guacamole/protocol.h>
+#include "config.h"
+
+#include "raw_encoder.h"
+
+#include <guacamole/audio.h>
 #include <guacamole/client.h>
+#include <guacamole/protocol.h>
 #include <guacamole/stream.h>
-#include <guacamole/audio.h>
-
-#ifdef ENABLE_OGG
-#include "ogg_encoder.h"
-#endif
 
-#include "wav_encoder.h"
+#include <stdlib.h>
+#include <string.h>
 
-guac_audio_stream* guac_audio_stream_alloc(guac_client* client, guac_audio_encoder* encoder) {
+guac_audio_stream* guac_audio_stream_alloc(guac_client* client,
+        guac_audio_encoder* encoder, int rate, int channels, int bps) {
 
     guac_audio_stream* audio;
 
@@ -63,17 +47,15 @@ guac_audio_stream* guac_audio_stream_alloc(guac_client* client, guac_audio_encod
 
             const char* mimetype = client->info.audio_mimetypes[i];
 
-#ifdef ENABLE_OGG
-            /* If Ogg is supported, done. */
-            if (strcmp(mimetype, ogg_encoder->mimetype) == 0) {
-                encoder = ogg_encoder;
+            /* If 16-bit raw audio is supported, done. */
+            if (bps == 16 && strcmp(mimetype, raw16_encoder->mimetype) == 0) {
+                encoder = raw16_encoder;
                 break;
             }
-#endif
 
-            /* If wav is supported, done. */
-            if (strcmp(mimetype, wav_encoder->mimetype) == 0) {
-                encoder = wav_encoder;
+            /* If 8-bit raw audio is supported, done. */
+            if (bps == 8 && strcmp(mimetype, raw8_encoder->mimetype) == 0) {
+                encoder = raw8_encoder;
                 break;
             }
 
@@ -86,129 +68,84 @@ guac_audio_stream* guac_audio_stream_alloc(guac_client* client, guac_audio_encod
     }
 
     /* Allocate stream */
-    audio = (guac_audio_stream*) malloc(sizeof(guac_audio_stream));
+    audio = (guac_audio_stream*) calloc(1, sizeof(guac_audio_stream));
     audio->client = client;
 
-    /* Reset buffer stats */
-    audio->used = 0;
-    audio->length = 0x40000;
-
-    audio->encoded_data_used = 0;
-    audio->encoded_data_length = 0x40000;
-
-    /* Allocate buffers */
-    audio->pcm_data = malloc(audio->length);
-    audio->encoded_data = malloc(audio->encoded_data_length);
-
     /* Assign encoder */
     audio->encoder = encoder;
     audio->stream = guac_client_alloc_stream(client);
 
-    /* Ensure socket within new stream is threadsafe */
-    guac_socket_require_threadsafe(audio->stream->socket);
-
-    return audio;
-}
-
-void guac_audio_stream_begin(guac_audio_stream* audio, int rate, int channels, int bps) {
-
     /* Load PCM properties */
     audio->rate = rate;
     audio->channels = channels;
     audio->bps = bps;
 
-    /* Reset write counter */
-    audio->pcm_bytes_written = 0;
+    /* Call handler, if defined */
+    if (audio->encoder->begin_handler)
+        audio->encoder->begin_handler(audio);
 
-    /* Call handler */
-    audio->encoder->begin_handler(audio);
+    return audio;
 
 }
 
-void guac_audio_stream_end(guac_audio_stream* audio) {
+void guac_audio_stream_reset(guac_audio_stream* audio,
+        guac_audio_encoder* encoder, int rate, int channels, int bps) {
 
-    double duration;
+    /* Do nothing if nothing is changing */
+    if ((encoder == NULL || encoder == audio->encoder)
+            && rate     == audio->rate
+            && channels == audio->channels
+            && bps      == audio->bps) {
+        return;
+    }
 
-    /* Flush stream and finish encoding */
-    guac_audio_stream_flush(audio);
-    audio->encoder->end_handler(audio);
+    /* Free old encoder data */
+    if (audio->encoder->end_handler)
+        audio->encoder->end_handler(audio);
 
-    /* Calculate duration of PCM data */
-    duration = ((double) (audio->pcm_bytes_written * 1000 * 8))
-                / audio->rate / audio->channels / audio->bps;
+    /* Assign new encoder, if changed */
+    if (encoder != NULL)
+        audio->encoder = encoder;
 
-    /* Send audio */
-    guac_protocol_send_audio(audio->stream->socket,
-            0, audio->encoder->mimetype,
-            duration, audio->encoded_data, audio->encoded_data_used);
+    /* Set PCM properties */
+    audio->rate = rate;
+    audio->channels = channels;
+    audio->bps = bps;
 
-    /* Clear data */
-    audio->encoded_data_used = 0;
+    /* Init encoder with new data */
+    if (audio->encoder->begin_handler)
+        audio->encoder->begin_handler(audio);
 
 }
 
 void guac_audio_stream_free(guac_audio_stream* audio) {
-    free(audio->pcm_data);
-    free(audio);
-}
-
-void guac_audio_stream_write_pcm(guac_audio_stream* audio, 
-        const unsigned char* data, int length) {
-
-    /* Update counter */
-    audio->pcm_bytes_written += length;
-
-    /* Resize audio buffer if necessary */
-    if (length > audio->length) {
-
-        /* Resize to double provided length */
-        audio->length = length*2;
-        audio->pcm_data = realloc(audio->pcm_data, audio->length);
-
-    }
-
-    /* Flush if necessary */
-    if (audio->used + length > audio->length)
-        guac_audio_stream_flush(audio);
 
-    /* Append to buffer */
-    memcpy(&(audio->pcm_data[audio->used]), data, length);
-    audio->used += length;
-
-}
-
-void guac_audio_stream_flush(guac_audio_stream* audio) {
-
-    /* If data in buffer */
-    if (audio->used != 0) {
-
-        /* Write data */
-        audio->encoder->write_handler(audio,
-                audio->pcm_data, audio->used);
+    /* Flush stream encoding */
+    guac_audio_stream_flush(audio);
 
-        /* Reset buffer */
-        audio->used = 0;
+    /* Clean up encoder */
+    if (audio->encoder->end_handler)
+        audio->encoder->end_handler(audio);
 
-    }
+    /* Free associated data */
+    free(audio);
 
 }
 
-void guac_audio_stream_write_encoded(guac_audio_stream* audio,
+void guac_audio_stream_write_pcm(guac_audio_stream* audio, 
         const unsigned char* data, int length) {
 
-    /* Resize audio buffer if necessary */
-    if (audio->encoded_data_used + length > audio->encoded_data_length) {
+    /* Write data */
+    if (audio->encoder->write_handler)
+        audio->encoder->write_handler(audio, data, length);
 
-        /* Increase to double concatenated size to accomodate */
-        audio->encoded_data_length = (audio->encoded_data_length + length)*2;
-        audio->encoded_data = realloc(audio->encoded_data,
-                audio->encoded_data_length);
+}
 
-    }
+void guac_audio_stream_flush(guac_audio_stream* audio) {
 
-    /* Append to buffer */
-    memcpy(&(audio->encoded_data[audio->encoded_data_used]), data, length);
-    audio->encoded_data_used += length;
+    /* Flush any buffered data */
+    if (audio->encoder->flush_handler)
+        audio->encoder->flush_handler(audio);
 
 }
 
diff --git a/src/libguac/client-handlers.c b/src/libguac/client-handlers.c
index bc20377..fc965aa 100644
--- a/src/libguac/client-handlers.c
+++ b/src/libguac/client-handlers.c
@@ -1,46 +1,38 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <stdlib.h>
-#include <stdio.h>
+#include "config.h"
 
 #include "client.h"
-#include "protocol.h"
 #include "client-handlers.h"
+#include "instruction.h"
+#include "object.h"
+#include "protocol.h"
+#include "stream.h"
+#include "timestamp.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
 
 /* Guacamole instruction handler map */
 
@@ -51,6 +43,13 @@ __guac_instruction_handler_mapping __guac_instruction_handler_map[] = {
    {"clipboard",  __guac_handle_clipboard},
    {"disconnect", __guac_handle_disconnect},
    {"size",       __guac_handle_size},
+   {"file",       __guac_handle_file},
+   {"pipe",       __guac_handle_pipe},
+   {"ack",        __guac_handle_ack},
+   {"blob",       __guac_handle_blob},
+   {"end",        __guac_handle_end},
+   {"get",        __guac_handle_get},
+   {"put",        __guac_handle_put},
    {NULL,         NULL}
 };
 
@@ -72,7 +71,6 @@ int64_t __guac_parse_int(const char* str) {
 
 }
 
-
 /* Guacamole instruction handlers */
 
 int __guac_handle_sync(guac_client* client, guac_instruction* instruction) {
@@ -107,13 +105,86 @@ int __guac_handle_key(guac_client* client, guac_instruction* instruction) {
     return 0;
 }
 
+static guac_stream* __get_input_stream(guac_client* client, int stream_index) {
+
+    /* Validate stream index */
+    if (stream_index < 0 || stream_index >= GUAC_CLIENT_MAX_STREAMS) {
+
+        guac_stream dummy_stream;
+        dummy_stream.index = stream_index;
+
+        guac_protocol_send_ack(client->socket, &dummy_stream,
+                "Invalid stream index", GUAC_PROTOCOL_STATUS_CLIENT_BAD_REQUEST);
+        return NULL;
+    }
+
+    return &(client->__input_streams[stream_index]);
+
+}
+
+static guac_stream* __get_open_input_stream(guac_client* client, int stream_index) {
+
+    guac_stream* stream = __get_input_stream(client, stream_index);
+
+    /* Fail if no such stream */
+    if (stream == NULL)
+        return NULL;
+
+    /* Validate initialization of stream */
+    if (stream->index == GUAC_CLIENT_CLOSED_STREAM_INDEX) {
+
+        guac_stream dummy_stream;
+        dummy_stream.index = stream_index;
+
+        guac_protocol_send_ack(client->socket, &dummy_stream,
+                "Invalid stream index", GUAC_PROTOCOL_STATUS_CLIENT_BAD_REQUEST);
+        return NULL;
+    }
+
+    return stream;
+
+}
+
+static guac_stream* __init_input_stream(guac_client* client, int stream_index) {
+
+    guac_stream* stream = __get_input_stream(client, stream_index);
+
+    /* Fail if no such stream */
+    if (stream == NULL)
+        return NULL;
+
+    /* Initialize stream */
+    stream->index = stream_index;
+    stream->data = NULL;
+    stream->ack_handler = NULL;
+    stream->blob_handler = NULL;
+    stream->end_handler = NULL;
+
+    return stream;
+
+}
+
 int __guac_handle_clipboard(guac_client* client, guac_instruction* instruction) {
+
+    /* Pull corresponding stream */
+    int stream_index = atoi(instruction->argv[0]);
+    guac_stream* stream = __init_input_stream(client, stream_index);
+    if (stream == NULL)
+        return 0;
+
+    /* If supported, call handler */
     if (client->clipboard_handler)
         return client->clipboard_handler(
             client,
-            instruction->argv[0] /* data */
+            stream,
+            instruction->argv[1] /* mimetype */
         );
+
+    /* Otherwise, abort */
+    guac_protocol_send_ack(client->socket, stream,
+            "Clipboard unsupported", GUAC_PROTOCOL_STATUS_UNSUPPORTED);
     return 0;
+
 }
 
 int __guac_handle_size(guac_client* client, guac_instruction* instruction) {
@@ -126,8 +197,204 @@ int __guac_handle_size(guac_client* client, guac_instruction* instruction) {
     return 0;
 }
 
+int __guac_handle_file(guac_client* client, guac_instruction* instruction) {
+
+    /* Pull corresponding stream */
+    int stream_index = atoi(instruction->argv[0]);
+    guac_stream* stream = __init_input_stream(client, stream_index);
+    if (stream == NULL)
+        return 0;
+
+    /* If supported, call handler */
+    if (client->file_handler)
+        return client->file_handler(
+            client,
+            stream,
+            instruction->argv[1], /* mimetype */
+            instruction->argv[2]  /* filename */
+        );
+
+    /* Otherwise, abort */
+    guac_protocol_send_ack(client->socket, stream,
+            "File transfer unsupported", GUAC_PROTOCOL_STATUS_UNSUPPORTED);
+    return 0;
+}
+
+int __guac_handle_pipe(guac_client* client, guac_instruction* instruction) {
+
+    /* Pull corresponding stream */
+    int stream_index = atoi(instruction->argv[0]);
+    guac_stream* stream = __init_input_stream(client, stream_index);
+    if (stream == NULL)
+        return 0;
+
+    /* If supported, call handler */
+    if (client->pipe_handler)
+        return client->pipe_handler(
+            client,
+            stream,
+            instruction->argv[1], /* mimetype */
+            instruction->argv[2]  /* name */
+        );
+
+    /* Otherwise, abort */
+    guac_protocol_send_ack(client->socket, stream,
+            "Named pipes unsupported", GUAC_PROTOCOL_STATUS_UNSUPPORTED);
+    return 0;
+}
+
+int __guac_handle_ack(guac_client* client, guac_instruction* instruction) {
+
+    guac_stream* stream;
+
+    /* Validate stream index */
+    int stream_index = atoi(instruction->argv[0]);
+    if (stream_index < 0 || stream_index >= GUAC_CLIENT_MAX_STREAMS)
+        return 0;
+
+    stream = &(client->__output_streams[stream_index]);
+
+    /* Validate initialization of stream */
+    if (stream->index == GUAC_CLIENT_CLOSED_STREAM_INDEX)
+        return 0;
+
+    /* Call stream handler if defined */
+    if (stream->ack_handler)
+        return stream->ack_handler(client, stream, instruction->argv[1],
+                atoi(instruction->argv[2]));
+
+    /* Fall back to global handler if defined */
+    if (client->ack_handler)
+        return client->ack_handler(client, stream, instruction->argv[1],
+                atoi(instruction->argv[2]));
+
+    return 0;
+}
+
+int __guac_handle_blob(guac_client* client, guac_instruction* instruction) {
+
+    int stream_index = atoi(instruction->argv[0]);
+    guac_stream* stream = __get_open_input_stream(client, stream_index);
+
+    /* Fail if no such stream */
+    if (stream == NULL)
+        return 0;
+
+    /* Call stream handler if defined */
+    if (stream->blob_handler) {
+        int length = guac_protocol_decode_base64(instruction->argv[1]);
+        return stream->blob_handler(client, stream, instruction->argv[1],
+            length);
+    }
+
+    /* Fall back to global handler if defined */
+    if (client->blob_handler) {
+        int length = guac_protocol_decode_base64(instruction->argv[1]);
+        return client->blob_handler(client, stream, instruction->argv[1],
+            length);
+    }
+
+    guac_protocol_send_ack(client->socket, stream,
+            "File transfer unsupported", GUAC_PROTOCOL_STATUS_UNSUPPORTED);
+    return 0;
+}
+
+int __guac_handle_end(guac_client* client, guac_instruction* instruction) {
+
+    int result = 0;
+    int stream_index = atoi(instruction->argv[0]);
+    guac_stream* stream = __get_open_input_stream(client, stream_index);
+
+    /* Fail if no such stream */
+    if (stream == NULL)
+        return 0;
+
+    /* Call stream handler if defined */
+    if (stream->end_handler)
+        result = stream->end_handler(client, stream);
+
+    /* Fall back to global handler if defined */
+    if (client->end_handler)
+        result = client->end_handler(client, stream);
+
+    /* Mark stream as closed */
+    stream->index = GUAC_CLIENT_CLOSED_STREAM_INDEX;
+    return result;
+}
+
+int __guac_handle_get(guac_client* client, guac_instruction* instruction) {
+
+    guac_object* object;
+
+    /* Validate object index */
+    int object_index = atoi(instruction->argv[0]);
+    if (object_index < 0 || object_index >= GUAC_CLIENT_MAX_OBJECTS)
+        return 0;
+
+    object = &(client->__objects[object_index]);
+
+    /* Validate initialization of object */
+    if (object->index == GUAC_CLIENT_UNDEFINED_OBJECT_INDEX)
+        return 0;
+
+    /* Call object handler if defined */
+    if (object->get_handler)
+        return object->get_handler(client, object,
+            instruction->argv[1] /* name */
+        );
+
+    /* Fall back to global handler if defined */
+    if (client->get_handler)
+        return client->get_handler(client, object,
+            instruction->argv[1] /* name */
+        );
+
+    return 0;
+}
+
+int __guac_handle_put(guac_client* client, guac_instruction* instruction) {
+
+    guac_object* object;
+
+    /* Validate object index */
+    int object_index = atoi(instruction->argv[0]);
+    if (object_index < 0 || object_index >= GUAC_CLIENT_MAX_OBJECTS)
+        return 0;
+
+    object = &(client->__objects[object_index]);
+
+    /* Validate initialization of object */
+    if (object->index == GUAC_CLIENT_UNDEFINED_OBJECT_INDEX)
+        return 0;
+
+    /* Pull corresponding stream */
+    int stream_index = atoi(instruction->argv[1]);
+    guac_stream* stream = __init_input_stream(client, stream_index);
+    if (stream == NULL)
+        return 0;
+
+    /* Call object handler if defined */
+    if (object->put_handler)
+        return object->put_handler(client, object, stream,
+            instruction->argv[2], /* mimetype */
+            instruction->argv[3]  /* name */
+        );
+
+    /* Fall back to global handler if defined */
+    if (client->put_handler)
+        return client->put_handler(client, object, stream,
+            instruction->argv[2], /* mimetype */
+            instruction->argv[3]  /* name */
+        );
+
+    /* Otherwise, abort */
+    guac_protocol_send_ack(client->socket, stream,
+            "Object write unsupported", GUAC_PROTOCOL_STATUS_UNSUPPORTED);
+    return 0;
+}
+
 int __guac_handle_disconnect(guac_client* client, guac_instruction* instruction) {
-    /* Return error code to force disconnect */
-    return -1;
+    guac_client_stop(client);
+    return 0;
 }
 
diff --git a/src/libguac/client-handlers.h b/src/libguac/client-handlers.h
index 24f9c63..f72a738 100644
--- a/src/libguac/client-handlers.h
+++ b/src/libguac/client-handlers.h
@@ -1,46 +1,29 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Original Code is libguac.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef _GUAC_CLIENT_HANDLERS__H
 #define _GUAC_CLIENT_HANDLERS__H
 
-#include "client.h"
-#include "instruction.h"
-
 /**
  * Provides initial handler functions and a lookup structure for automatically
  * handling client instructions. This is used only internally within libguac,
@@ -49,6 +32,11 @@
  * @file client-handlers.h
  */
 
+#include "config.h"
+
+#include "client.h"
+#include "instruction.h"
+
 /**
  * Internal handler for Guacamole instructions.
  */
@@ -100,6 +88,55 @@ int __guac_handle_key(guac_client* client, guac_instruction* instruction);
 int __guac_handle_clipboard(guac_client* client, guac_instruction* instruction);
 
 /**
+ * Internal initial handler for the file instruction. When a file instruction
+ * is received, this handler will be called. The client's file handler will
+ * be invoked if defined.
+ */
+int __guac_handle_file(guac_client* client, guac_instruction* instruction);
+
+/**
+ * Internal initial handler for the pipe instruction. When a pipe instruction
+ * is received, this handler will be called. The client's pipe handler will
+ * be invoked if defined.
+ */
+int __guac_handle_pipe(guac_client* client, guac_instruction* instruction);
+
+/**
+ * Internal initial handler for the ack instruction. When a ack instruction
+ * is received, this handler will be called. The client's ack handler will
+ * be invoked if defined.
+ */
+int __guac_handle_ack(guac_client* client, guac_instruction* instruction);
+
+/**
+ * Internal initial handler for the blob instruction. When a blob instruction
+ * is received, this handler will be called. The client's blob handler will
+ * be invoked if defined.
+ */
+int __guac_handle_blob(guac_client* client, guac_instruction* instruction);
+
+/**
+ * Internal initial handler for the end instruction. When a end instruction
+ * is received, this handler will be called. The client's end handler will
+ * be invoked if defined.
+ */
+int __guac_handle_end(guac_client* client, guac_instruction* instruction);
+
+/**
+ * Internal initial handler for the get instruction. When a get instruction
+ * is received, this handler will be called. The client's get handler will
+ * be invoked if defined.
+ */
+int __guac_handle_get(guac_client* client, guac_instruction* instruction);
+
+/**
+ * Internal initial handler for the put instruction. When a put instruction
+ * is received, this handler will be called. The client's put handler will
+ * be invoked if defined.
+ */
+int __guac_handle_put(guac_client* client, guac_instruction* instruction);
+
+/**
  * Internal initial handler for the size instruction. When a size instruction
  * is received, this handler will be called. The client's size handler will
  * be invoked if defined.
diff --git a/src/libguac/client.c b/src/libguac/client.c
index 62b6088..949bc26 100644
--- a/src/libguac/client.c
+++ b/src/libguac/client.c
@@ -1,53 +1,55 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include "config.h"
 
 #include "client.h"
 #include "client-handlers.h"
+#include "encode-jpeg.h"
+#include "encode-png.h"
 #include "error.h"
+#include "instruction.h"
 #include "layer.h"
-#include "plugin.h"
+#include "object.h"
 #include "pool.h"
 #include "protocol.h"
 #include "socket.h"
-#include "time.h"
+#include "stream.h"
+#include "timestamp.h"
+
+#ifdef ENABLE_WEBP
+#include "encode-webp.h"
+#endif
+
+#ifdef HAVE_OSSP_UUID_H
+#include <ossp/uuid.h>
+#else
+#include <uuid.h>
+#endif
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 
 guac_layer __GUAC_DEFAULT_LAYER = {
     .index = 0
@@ -97,15 +99,23 @@ void guac_client_free_layer(guac_client* client, guac_layer* layer) {
 
 guac_stream* guac_client_alloc_stream(guac_client* client) {
 
-    /* Init new stream */
-    guac_stream* allocd_stream = malloc(sizeof(guac_stream));
-    allocd_stream->index = guac_pool_next_int(client->__stream_pool);
+    guac_stream* allocd_stream;
+    int stream_index;
+
+    /* Refuse to allocate beyond maximum */
+    if (client->__stream_pool->active == GUAC_CLIENT_MAX_STREAMS)
+        return NULL;
+
+    /* Allocate stream */
+    stream_index = guac_pool_next_int(client->__stream_pool);
 
-    /* Nest socket */
-    allocd_stream->socket = guac_socket_nest(
-        client->socket,
-        allocd_stream->index
-    );
+    /* Initialize stream */
+    allocd_stream = &(client->__output_streams[stream_index]);
+    allocd_stream->index = stream_index;
+    allocd_stream->data = NULL;
+    allocd_stream->ack_handler = NULL;
+    allocd_stream->blob_handler = NULL;
+    allocd_stream->end_handler = NULL;
 
     return allocd_stream;
 
@@ -114,18 +124,108 @@ guac_stream* guac_client_alloc_stream(guac_client* client) {
 void guac_client_free_stream(guac_client* client, guac_stream* stream) {
 
     /* Release index to pool */
-    guac_pool_free_int(client->__stream_pool, stream->index - 1);
-    
-    /* Release socket */
-    guac_socket_free(stream->socket);
+    guac_pool_free_int(client->__stream_pool, stream->index);
+
+    /* Mark stream as closed */
+    stream->index = GUAC_CLIENT_CLOSED_STREAM_INDEX;
+
+}
+
+guac_object* guac_client_alloc_object(guac_client* client) {
+
+    guac_object* allocd_object;
+    int object_index;
+
+    /* Refuse to allocate beyond maximum */
+    if (client->__object_pool->active == GUAC_CLIENT_MAX_OBJECTS)
+        return NULL;
+
+    /* Allocate object */
+    object_index = guac_pool_next_int(client->__object_pool);
+
+    /* Initialize object */
+    allocd_object = &(client->__objects[object_index]);
+    allocd_object->index = object_index;
+    allocd_object->data = NULL;
+    allocd_object->get_handler = NULL;
+    allocd_object->put_handler = NULL;
+
+    return allocd_object;
+
+}
+
+void guac_client_free_object(guac_client* client, guac_object* object) {
+
+    /* Release index to pool */
+    guac_pool_free_int(client->__object_pool, object->index);
+
+    /* Mark object as undefined */
+    object->index = GUAC_CLIENT_UNDEFINED_OBJECT_INDEX;
+
+}
+
+/**
+ * Returns a newly allocated string containing a guaranteed-unique connection
+ * identifier string which is 37 characters long and begins with a '$'
+ * character.  If an error occurs, NULL is returned, and no memory is
+ * allocated.
+ */
+static char* __guac_generate_connection_id() {
+
+    char* buffer;
+    char* identifier;
+    size_t identifier_length;
+
+    uuid_t* uuid;
+
+    /* Attempt to create UUID object */
+    if (uuid_create(&uuid) != UUID_RC_OK) {
+        guac_error = GUAC_STATUS_NO_MEMORY;
+        guac_error_message = "Could not allocate memory for UUID";
+        return NULL;
+    }
+
+    /* Generate random UUID */
+    if (uuid_make(uuid, UUID_MAKE_V4) != UUID_RC_OK) {
+        uuid_destroy(uuid);
+        guac_error = GUAC_STATUS_NO_MEMORY;
+        guac_error_message = "UUID generation failed";
+        return NULL;
+    }
+
+    /* Allocate buffer for future formatted ID */
+    buffer = malloc(UUID_LEN_STR + 2);
+    if (buffer == NULL) {
+        uuid_destroy(uuid);
+        guac_error = GUAC_STATUS_NO_MEMORY;
+        guac_error_message = "Could not allocate memory for connection ID";
+        return NULL;
+    }
+
+    identifier = &(buffer[1]);
+    identifier_length = UUID_LEN_STR + 1;
 
-    /* Free stream */
-    free(stream);
+    /* Build connection ID from UUID */
+    if (uuid_export(uuid, UUID_FMT_STR, &identifier, &identifier_length) != UUID_RC_OK) {
+        free(buffer);
+        uuid_destroy(uuid);
+        guac_error = GUAC_STATUS_INTERNAL_ERROR;
+        guac_error_message = "Conversion of UUID to connection ID failed";
+        return NULL;
+    }
+
+    uuid_destroy(uuid);
+
+    buffer[0] = '$';
+    buffer[UUID_LEN_STR + 1] = '\0';
+    return buffer;
 
 }
 
 guac_client* guac_client_alloc() {
 
+    int i;
+
     /* Allocate new client */
     guac_client* client = malloc(sizeof(guac_client));
     if (client == NULL) {
@@ -142,6 +242,13 @@ guac_client* guac_client_alloc() {
 
     client->state = GUAC_CLIENT_RUNNING;
 
+    /* Generate ID */
+    client->connection_id = __guac_generate_connection_id();
+    if (client->connection_id == NULL) {
+        free(client);
+        return NULL;
+    }
+
     /* Allocate buffer and layer pools */
     client->__buffer_pool = guac_pool_alloc(GUAC_BUFFER_POOL_INITIAL_SIZE);
     client->__layer_pool = guac_pool_alloc(GUAC_BUFFER_POOL_INITIAL_SIZE);
@@ -149,6 +256,23 @@ guac_client* guac_client_alloc() {
     /* Allocate stream pool */
     client->__stream_pool = guac_pool_alloc(0);
 
+    /* Initialize streams */
+    client->__input_streams = malloc(sizeof(guac_stream) * GUAC_CLIENT_MAX_STREAMS);
+    client->__output_streams = malloc(sizeof(guac_stream) * GUAC_CLIENT_MAX_STREAMS);
+
+    for (i=0; i<GUAC_CLIENT_MAX_STREAMS; i++) {
+        client->__input_streams[i].index = GUAC_CLIENT_CLOSED_STREAM_INDEX;
+        client->__output_streams[i].index = GUAC_CLIENT_CLOSED_STREAM_INDEX;
+    }
+
+    /* Allocate object pool */
+    client->__object_pool = guac_pool_alloc(0);
+
+    /* Initialize objects */
+    client->__objects = malloc(sizeof(guac_object) * GUAC_CLIENT_MAX_OBJECTS);
+    for (i=0; i<GUAC_CLIENT_MAX_OBJECTS; i++)
+        client->__objects[i].index = GUAC_CLIENT_UNDEFINED_OBJECT_INDEX;
+
     return client;
 
 }
@@ -166,9 +290,19 @@ void guac_client_free(guac_client* client) {
     guac_pool_free(client->__buffer_pool);
     guac_pool_free(client->__layer_pool);
 
+    /* Free streams */
+    free(client->__input_streams);
+    free(client->__output_streams);
+
     /* Free stream pool */
     guac_pool_free(client->__stream_pool);
 
+    /* Free objects */
+    free(client->__objects);
+
+    /* Free object pool */
+    guac_pool_free(client->__object_pool);
+
     free(client);
 }
 
@@ -190,47 +324,153 @@ int guac_client_handle_instruction(guac_client* client, guac_instruction* instru
 
 }
 
-void vguac_client_log_info(guac_client* client, const char* format,
-        va_list ap) {
+void vguac_client_log(guac_client* client, guac_client_log_level level,
+        const char* format, va_list ap) {
 
     /* Call handler if defined */
-    if (client->log_info_handler != NULL)
-        client->log_info_handler(client, format, ap);
+    if (client->log_handler != NULL)
+        client->log_handler(client, level, format, ap);
 
 }
 
-void vguac_client_log_error(guac_client* client, const char* format,
-        va_list ap) {
+void guac_client_log(guac_client* client, guac_client_log_level level,
+        const char* format, ...) {
 
-    /* Call handler if defined */
-    if (client->log_error_handler != NULL)
-        client->log_error_handler(client, format, ap);
+    va_list args;
+    va_start(args, format);
+
+    vguac_client_log(client, level, format, args);
+
+    va_end(args);
+
+}
 
+void guac_client_stop(guac_client* client) {
+    client->state = GUAC_CLIENT_STOPPING;
 }
 
-void guac_client_log_info(guac_client* client, const char* format, ...) {
+void vguac_client_abort(guac_client* client, guac_protocol_status status,
+        const char* format, va_list ap) {
 
-    va_list args;
-    va_start(args, format);
+    /* Only relevant if client is running */
+    if (client->state == GUAC_CLIENT_RUNNING) {
 
-    vguac_client_log_info(client, format, args);
+        /* Log detail of error */
+        vguac_client_log(client, GUAC_LOG_ERROR, format, ap);
 
-    va_end(args);
+        /* Send error immediately, limit information given */
+        guac_protocol_send_error(client->socket, "Aborted. See logs.", status);
+        guac_socket_flush(client->socket);
+
+        /* Stop client */
+        guac_client_stop(client);
+
+    }
 
 }
 
-void guac_client_log_error(guac_client* client, const char* format, ...) {
+void guac_client_abort(guac_client* client, guac_protocol_status status,
+        const char* format, ...) {
 
     va_list args;
     va_start(args, format);
 
-    vguac_client_log_error(client, format, args);
+    vguac_client_abort(client, status, format, args);
 
     va_end(args);
 
 }
 
-void guac_client_stop(guac_client* client) {
-    client->state = GUAC_CLIENT_STOPPING;
+void guac_client_stream_png(guac_client* client, guac_socket* socket,
+        guac_composite_mode mode, const guac_layer* layer, int x, int y,
+        cairo_surface_t* surface) {
+
+    /* Allocate new stream for image */
+    guac_stream* stream = guac_client_alloc_stream(client);
+
+    /* Declare stream as containing image data */
+    guac_protocol_send_img(socket, stream, mode, layer, "image/png", x, y);
+
+    /* Write PNG data */
+    guac_png_write(socket, stream, surface);
+
+    /* Terminate stream */
+    guac_protocol_send_end(socket, stream);
+
+    /* Free allocated stream */
+    guac_client_free_stream(client, stream);
+
+}
+
+void guac_client_stream_jpeg(guac_client* client, guac_socket* socket,
+        guac_composite_mode mode, const guac_layer* layer, int x, int y,
+        cairo_surface_t* surface, int quality) {
+
+    /* Allocate new stream for image */
+    guac_stream* stream = guac_client_alloc_stream(client);
+
+    /* Declare stream as containing image data */
+    guac_protocol_send_img(socket, stream, mode, layer, "image/jpeg", x, y);
+
+    /* Write JPEG data */
+    guac_jpeg_write(socket, stream, surface, quality);
+
+    /* Terminate stream */
+    guac_protocol_send_end(socket, stream);
+
+    /* Free allocated stream */
+    guac_client_free_stream(client, stream);
+
+}
+
+void guac_client_stream_webp(guac_client* client, guac_socket* socket,
+        guac_composite_mode mode, const guac_layer* layer, int x, int y,
+        cairo_surface_t* surface, int quality, int lossless) {
+
+#ifdef ENABLE_WEBP
+    /* Allocate new stream for image */
+    guac_stream* stream = guac_client_alloc_stream(client);
+
+    /* Declare stream as containing image data */
+    guac_protocol_send_img(socket, stream, mode, layer, "image/webp", x, y);
+
+    /* Write WebP data */
+    guac_webp_write(socket, stream, surface, quality, lossless);
+
+    /* Terminate stream */
+    guac_protocol_send_end(socket, stream);
+
+    /* Free allocated stream */
+    guac_client_free_stream(client, stream);
+#else
+    /* Do nothing if WebP support is not built in */
+#endif
+
+}
+
+int guac_client_supports_webp(guac_client* client) {
+
+#ifdef ENABLE_WEBP
+    char** mimetype = client->info.image_mimetypes;
+
+    /* Search for WebP mimetype in list of supported image mimetypes */
+    while (*mimetype != NULL) {
+
+        /* If WebP mimetype found, no need to search further */
+        if (strcmp(*mimetype, "image/webp") == 0)
+            return 1;
+
+        /* Next mimetype */
+        mimetype++;
+
+    }
+
+    /* Client does not support WebP */
+    return 0;
+#else
+    /* Support for WebP is completely absent */
+    return 0;
+#endif
+
 }
 
diff --git a/src/libguac/encode-jpeg.c b/src/libguac/encode-jpeg.c
new file mode 100644
index 0000000..83f6cd8
--- /dev/null
+++ b/src/libguac/encode-jpeg.c
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "encode-jpeg.h"
+#include "error.h"
+#include "palette.h"
+#include "protocol.h"
+#include "stream.h"
+
+#include <cairo/cairo.h>
+#include <jpeglib.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * Extended version of the standard libjpeg jpeg_destination_mgr struct, which
+ * provides access to the pointers to the output buffer and size. The values
+ * of this structure will be initialized by jpeg_guac_dest().
+ */
+typedef struct guac_jpeg_destination_mgr {
+
+    /**
+     * Original jpeg_destination_mgr structure. This MUST be the first member
+     * for guac_jpeg_destination_mgr to be usable as a jpeg_destination_mgr.
+     */
+    struct jpeg_destination_mgr parent;
+
+    /**
+     * The socket over which all JPEG blobs will be written.
+     */
+    guac_socket* socket;
+
+    /**
+     * The Guacamole stream to associate with each JPEG blob.
+     */
+    guac_stream* stream;
+
+    /**
+     * The output buffer.
+     */
+    unsigned char buffer[6048];
+
+} guac_jpeg_destination_mgr;
+
+/**
+ * Initializes the destination structure of the given compression structure.
+ *
+ * @param cinfo
+ *     The compression structure whose destination structure should be
+ *     initialized.
+ */
+static void guac_jpeg_init_destination(j_compress_ptr cinfo) {
+
+    guac_jpeg_destination_mgr* dest = (guac_jpeg_destination_mgr*) cinfo->dest;
+
+    /* Init parent destination state */
+    dest->parent.next_output_byte = dest->buffer;
+    dest->parent.free_in_buffer   = sizeof(dest->buffer);
+
+}
+
+/**
+ * Flushes the current output buffer associated with the given compression
+ * structure, as the current output buffer is full.
+ *
+ * @param cinfo
+ *     The compression structure whose output buffer should be flushed.
+ * 
+ * @return
+ *     TRUE, always, indicating that space is now available. FALSE is returned
+ *     only by applications that may need additional time to empty the buffer.
+ */
+static boolean guac_jpeg_empty_output_buffer(j_compress_ptr cinfo) {
+
+    guac_jpeg_destination_mgr* dest = (guac_jpeg_destination_mgr*) cinfo->dest;
+
+    /* Write blob */
+    guac_protocol_send_blob(dest->socket, dest->stream,
+            dest->buffer, sizeof(dest->buffer));
+
+    /* Update destination offset */
+    dest->parent.next_output_byte = dest->buffer;
+    dest->parent.free_in_buffer = sizeof(dest->buffer);
+
+    return TRUE;
+
+}
+
+/**
+ * Flushes the final blob of JPEG data, if any, as JPEG compression is now
+ * complete.
+ *
+ * @param cinfo
+ *     The compression structure associated with the now-complete JPEG
+ *     compression operation.
+ */
+static void guac_jpeg_term_destination(j_compress_ptr cinfo) {
+
+    guac_jpeg_destination_mgr* dest = (guac_jpeg_destination_mgr*) cinfo->dest;
+
+    /* Write final blob, if any */
+    if (dest->parent.free_in_buffer != sizeof(dest->buffer))
+        guac_protocol_send_blob(dest->socket, dest->stream, dest->buffer,
+                sizeof(dest->buffer) - dest->parent.free_in_buffer);
+
+}
+
+/**
+ * Configures the given compression structure to use the given Guacamole stream
+ * for JPEG output.
+ *
+ * @param cinfo
+ *     The libjpeg compression structure to configure.
+ *
+ * @param socket
+ *     The Guacamole socket to use when sending blob instructions.
+ *
+ * @param stream
+ *     The stream over which JPEG-encoded blobs of image data should be sent.
+ */
+static void jpeg_guac_dest(j_compress_ptr cinfo, guac_socket* socket,
+        guac_stream* stream) {
+
+    guac_jpeg_destination_mgr* dest;
+
+    /* Allocate dest from pool if not already allocated */
+    if (cinfo->dest == NULL)
+        cinfo->dest = (struct jpeg_destination_mgr*)
+            (cinfo->mem->alloc_small)((j_common_ptr) cinfo, JPOOL_PERMANENT,
+                    sizeof(guac_jpeg_destination_mgr));
+
+    /* Pull possibly-new destination struct from cinfo */
+    dest = (guac_jpeg_destination_mgr*) cinfo->dest;
+
+    /* Associate destination handlers */
+    dest->parent.init_destination    = guac_jpeg_init_destination;
+    dest->parent.empty_output_buffer = guac_jpeg_empty_output_buffer;
+    dest->parent.term_destination    = guac_jpeg_term_destination;
+
+    /* Store Guacamole-specific objects */
+    dest->socket = socket;
+    dest->stream = stream;
+
+}
+
+int guac_jpeg_write(guac_socket* socket, guac_stream* stream,
+        cairo_surface_t* surface, int quality) {
+
+    /* Get image surface properties and data */
+    cairo_format_t format = cairo_image_surface_get_format(surface);
+
+    if (format != CAIRO_FORMAT_RGB24) {
+        guac_error = GUAC_STATUS_INTERNAL_ERROR;
+        guac_error_message =
+            "Invalid Cairo image format. Unable to create JPEG.";
+        return -1;
+    }
+
+    int width = cairo_image_surface_get_width(surface);
+    int height = cairo_image_surface_get_height(surface);
+    int stride = cairo_image_surface_get_stride(surface);
+    unsigned char* data = cairo_image_surface_get_data(surface);
+
+    /* Flush pending operations to surface */
+    cairo_surface_flush(surface);
+
+    /* Prepare JPEG bits */
+    struct jpeg_compress_struct cinfo;
+    struct jpeg_error_mgr jerr;
+    cinfo.err = jpeg_std_error(&jerr);
+    jpeg_create_compress(&cinfo);
+
+    /* Write JPEG directly to given stream */
+    jpeg_guac_dest(&cinfo, socket, stream);
+
+    cinfo.image_width = width; /* image width and height, in pixels */
+    cinfo.image_height = height;
+    cinfo.arith_code = TRUE;
+
+#ifdef JCS_EXTENSIONS
+    /* The Turbo JPEG extentions allows us to use the Cairo surface
+     * (BGRx) as input without converting it */
+    cinfo.input_components = 4;
+    cinfo.in_color_space = JCS_EXT_BGRX;
+#else
+    /* Standard JPEG supports RGB as input so we will have to convert
+     * the contents of the Cairo surface from (BGRx) to RGB */
+    cinfo.input_components = 3;
+    cinfo.in_color_space = JCS_RGB;
+
+    /* Create a buffer for the write scan line which is where we will
+     * put the converted pixels (BGRx -> RGB) */
+    int write_stride = cinfo.image_width * cinfo.input_components;
+    unsigned char *scanline_data = malloc(write_stride);
+    memset(scanline_data, 0, write_stride);
+#endif
+
+    /* Initialize the JPEG compressor */
+    jpeg_set_defaults(&cinfo);
+    jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
+    jpeg_start_compress(&cinfo, TRUE);
+
+    JSAMPROW row_pointer[1]; /* pointer to a single row */
+
+    /* Write scanlines to be used in JPEG compression */
+    while (cinfo.next_scanline < cinfo.image_height) {
+
+        int row_offset = stride * cinfo.next_scanline;
+
+#ifdef JCS_EXTENSIONS
+        /* In Turbo JPEG we can use the raw BGRx scanline  */
+        row_pointer[0] = &data[row_offset];
+#else
+        /* For standard JPEG libraries we have to convert the
+         * scanline from 24 bit (4 byte) BGRx to 24 bit (3 byte) RGB */
+        unsigned char *inptr = data + row_offset;
+        unsigned char *outptr = scanline_data;
+
+        for (int x = 0; x < width; ++x) {
+
+            outptr[2] = *inptr++; /* B */
+            outptr[1] = *inptr++; /* G */
+            outptr[0] = *inptr++; /* R */
+            inptr++; /* skip the upper byte (x/A) */
+            outptr += 3;
+
+        }
+
+        row_pointer[0] = scanline_data;
+#endif
+
+        jpeg_write_scanlines(&cinfo, row_pointer, 1);
+    }
+
+#ifndef JCS_EXTENSIONS
+    free(scanline_data);
+#endif
+
+    /* Finalize compression */
+    jpeg_finish_compress(&cinfo);
+
+    /* Clean up */
+    jpeg_destroy_compress(&cinfo);
+    return 0;
+
+}
+
diff --git a/src/libguac/encode-jpeg.h b/src/libguac/encode-jpeg.h
new file mode 100644
index 0000000..204d127
--- /dev/null
+++ b/src/libguac/encode-jpeg.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GUAC_ENCODE_JPEG_H
+#define GUAC_ENCODE_JPEG_H
+
+#include "config.h"
+
+#include "socket.h"
+#include "stream.h"
+
+#include <cairo/cairo.h>
+
+/**
+ * Encodes the given surface as a JPEG, and sends the resulting data over the
+ * given stream and socket as blobs.
+ *
+ * @param socket
+ *     The socket to send JPEG blobs over.
+ *
+ * @param stream
+ *     The stream to associate with each blob.
+ *
+ * @param surface
+ *     The Cairo surface to write to the given stream and socket as JPEG blobs.
+ *
+ * @param quality
+ *     JPEG image quality.
+ * 
+ * @return
+ *     Zero if the encoding operation is successful, non-zero otherwise.
+ */
+int guac_jpeg_write(guac_socket* socket, guac_stream* stream,
+        cairo_surface_t* surface, int quality);
+
+#endif
+
diff --git a/src/libguac/encode-png.c b/src/libguac/encode-png.c
new file mode 100644
index 0000000..516c1f6
--- /dev/null
+++ b/src/libguac/encode-png.c
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "encode-png.h"
+#include "error.h"
+#include "palette.h"
+#include "protocol.h"
+#include "stream.h"
+
+#include <png.h>
+#include <cairo/cairo.h>
+
+#ifdef HAVE_PNGSTRUCT_H
+#include <pngstruct.h>
+#endif
+
+#include <inttypes.h>
+#include <setjmp.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * Data describing the current write state of PNG data.
+ */
+typedef struct guac_png_write_state {
+
+    /**
+     * The socket over which all PNG blobs will be written.
+     */
+    guac_socket* socket;
+
+    /**
+     * The Guacamole stream to associate with each PNG blob.
+     */
+    guac_stream* stream;
+
+    /**
+     * Buffer of pending PNG data.
+     */
+    char buffer[6048];
+
+    /**
+     * The number of bytes currently stored in the buffer.
+     */
+    int buffer_size;
+
+} guac_png_write_state;
+
+/**
+ * Writes the contents of the PNG write state as a blob to its associated
+ * socket.
+ *
+ * @param write_state
+ *     The write state to flush.
+ */
+static void guac_png_flush_data(guac_png_write_state* write_state) {
+
+    /* Send blob */
+    guac_protocol_send_blob(write_state->socket, write_state->stream,
+            write_state->buffer, write_state->buffer_size);
+
+    /* Clear buffer */
+    write_state->buffer_size = 0;
+
+}
+
+/**
+ * Appends the given PNG data to the internal buffer of the given write state,
+ * automatically flushing the write state as necessary.
+ * socket.
+ *
+ * @param write_state
+ *     The write state to append the given data to.
+ *
+ * @param data
+ *     The PNG data to append.
+ *
+ * @param length
+ *     The size of the given PNG data, in bytes.
+ */
+static void guac_png_write_data(guac_png_write_state* write_state,
+        const void* data, int length) {
+
+    const unsigned char* current = data;
+
+    /* Append all data given */
+    while (length > 0) {
+
+        /* Calculate space remaining */
+        int remaining = sizeof(write_state->buffer) - write_state->buffer_size;
+
+        /* If no space remains, flush buffer to make room */
+        if (remaining == 0) {
+            guac_png_flush_data(write_state);
+            remaining = sizeof(write_state->buffer);
+        }
+
+        /* Calculate size of next block of data to append */
+        int block_size = remaining;
+        if (block_size > length)
+            block_size = length;
+
+        /* Append block */
+        memcpy(write_state->buffer + write_state->buffer_size,
+                current, block_size);
+
+        /* Next block */
+        current += block_size;
+        write_state->buffer_size += block_size;
+        length -= block_size;
+
+    }
+
+}
+
+/**
+ * Writes the given buffer of PNG data to the buffer of the given write state,
+ * flushing that buffer to blob instructions if necessary. This handler is
+ * called by Cairo when writing PNG data via
+ * cairo_surface_write_to_png_stream().
+ *
+ * @param closure
+ *     Pointer to arbitrary data passed to cairo_surface_write_to_png_stream().
+ *     In the case of this handler, this data will be the guac_png_write_state.
+ *
+ * @param data
+ *     The buffer of PNG data to write.
+ * 
+ * @param length
+ *     The size of the given buffer, in bytes.
+ *
+ * @return
+ *     A Cairo status code indicating whether the write operation succeeded.
+ *     In the case of this handler, this will always be CAIRO_STATUS_SUCCESS.
+ */
+static cairo_status_t guac_png_cairo_write_handler(void* closure,
+        const unsigned char* data, unsigned int length) {
+
+    guac_png_write_state* write_state = (guac_png_write_state*) closure;
+
+    /* Append data to buffer, writing as necessary */
+    guac_png_write_data(write_state, data, length);
+
+    return CAIRO_STATUS_SUCCESS;
+
+}
+
+/**
+ * Implementation of guac_png_write() which uses Cairo's own PNG encoder to
+ * write PNG data, rather than using libpng directly.
+ *
+ * @param socket
+ *     The socket to send PNG blobs over.
+ *
+ * @param stream
+ *     The stream to associate with each blob.
+ *
+ * @param surface
+ *     The Cairo surface to write to the given stream and socket as PNG blobs.
+ *
+ * @return
+ *     Zero if the encoding operation is successful, non-zero otherwise.
+ */
+static int guac_png_cairo_write(guac_socket* socket, guac_stream* stream,
+        cairo_surface_t* surface) {
+
+    guac_png_write_state write_state;
+
+    /* Init write state */
+    write_state.socket = socket;
+    write_state.stream = stream;
+    write_state.buffer_size = 0;
+
+    /* Write surface as PNG */
+    if (cairo_surface_write_to_png_stream(surface,
+                guac_png_cairo_write_handler,
+                &write_state) != CAIRO_STATUS_SUCCESS) {
+        guac_error = GUAC_STATUS_INTERNAL_ERROR;
+        guac_error_message = "Cairo PNG backend failed";
+        return -1;
+    }
+
+    /* Flush remaining PNG data */
+    guac_png_flush_data(&write_state);
+    return 0;
+
+}
+
+/**
+ * Writes the given buffer of PNG data to the buffer of the given write state,
+ * flushing that buffer to blob instructions if necessary. This handler is
+ * called by libpng when writing PNG data via png_write_png().
+ *
+ * @param png
+ *     The PNG compression state structure associated with the write operation.
+ *     The pointer to arbitrary data will have been set to the
+ *     guac_png_write_state by png_set_write_fn(), and will be accessible via
+ *     png->io_ptr or png_get_io_ptr(png), depending on the version of libpng.
+ *
+ * @param data
+ *     The buffer of PNG data to write.
+ * 
+ * @param length
+ *     The size of the given buffer, in bytes.
+ */
+static void guac_png_write_handler(png_structp png, png_bytep data,
+        png_size_t length) {
+
+    /* Get png buffer structure */
+    guac_png_write_state* write_state;
+#ifdef HAVE_PNG_GET_IO_PTR
+    write_state = (guac_png_write_state*) png_get_io_ptr(png);
+#else
+    write_state = (guac_png_write_state*) png->io_ptr;
+#endif
+
+    /* Append data to buffer, writing as necessary */
+    guac_png_write_data(write_state, data, length);
+
+}
+
+/**
+ * Flushes any PNG data within the buffer of the given write state as a blob
+ * instruction. If no data is within the buffer, this function has no effect.
+ * This handler is called by libpng when it has finished writing PNG data via
+ * png_write_png().
+ *
+ * @param png
+ *     The PNG compression state structure associated with the write operation.
+ *     The pointer to arbitrary data will have been set to the
+ *     guac_png_write_state by png_set_write_fn(), and will be accessible via
+ *     png->io_ptr or png_get_io_ptr(png), depending on the version of libpng.
+ */
+static void guac_png_flush_handler(png_structp png) {
+
+    /* Get png buffer structure */
+    guac_png_write_state* write_state;
+#ifdef HAVE_PNG_GET_IO_PTR
+    write_state = (guac_png_write_state*) png_get_io_ptr(png);
+#else
+    write_state = (guac_png_write_state*) png->io_ptr;
+#endif
+
+    /* Flush buffer */
+    guac_png_flush_data(write_state);
+
+}
+
+int guac_png_write(guac_socket* socket, guac_stream* stream,
+        cairo_surface_t* surface) {
+
+    png_structp png;
+    png_infop png_info;
+    png_byte** png_rows;
+    int bpp;
+
+    int x, y;
+
+    guac_png_write_state write_state;
+
+    /* Get image surface properties and data */
+    cairo_format_t format = cairo_image_surface_get_format(surface);
+    int width = cairo_image_surface_get_width(surface);
+    int height = cairo_image_surface_get_height(surface);
+    int stride = cairo_image_surface_get_stride(surface);
+    unsigned char* data = cairo_image_surface_get_data(surface);
+
+    /* If not RGB24, use Cairo PNG writer */
+    if (format != CAIRO_FORMAT_RGB24 || data == NULL)
+        return guac_png_cairo_write(socket, stream, surface);
+
+    /* Flush pending operations to surface */
+    cairo_surface_flush(surface);
+
+    /* Attempt to build palette */
+    guac_palette* palette = guac_palette_alloc(surface);
+
+    /* If not possible, resort to Cairo PNG writer */
+    if (palette == NULL)
+        return guac_png_cairo_write(socket, stream, surface);
+
+    /* Calculate BPP from palette size */
+    if      (palette->size <= 2)  bpp = 1;
+    else if (palette->size <= 4)  bpp = 2;
+    else if (palette->size <= 16) bpp = 4;
+    else                          bpp = 8;
+
+    /* Set up PNG writer */
+    png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+    if (!png) {
+        guac_error = GUAC_STATUS_INTERNAL_ERROR;
+        guac_error_message = "libpng failed to create write structure";
+        return -1;
+    }
+
+    png_info = png_create_info_struct(png);
+    if (!png_info) {
+        png_destroy_write_struct(&png, NULL);
+        guac_error = GUAC_STATUS_INTERNAL_ERROR;
+        guac_error_message = "libpng failed to create info structure";
+        return -1;
+    }
+
+    /* Set error handler */
+    if (setjmp(png_jmpbuf(png))) {
+        png_destroy_write_struct(&png, &png_info);
+        guac_error = GUAC_STATUS_IO_ERROR;
+        guac_error_message = "libpng output error";
+        return -1;
+    }
+
+    /* Init write state */
+    write_state.socket = socket;
+    write_state.stream = stream;
+    write_state.buffer_size = 0;
+
+    /* Set up writer */
+    png_set_write_fn(png, &write_state,
+            guac_png_write_handler,
+            guac_png_flush_handler);
+
+    /* Copy data from surface into PNG data */
+    png_rows = (png_byte**) malloc(sizeof(png_byte*) * height);
+    for (y=0; y<height; y++) {
+
+        /* Allocate new PNG row */
+        png_byte* row = (png_byte*) malloc(sizeof(png_byte) * width);
+        png_rows[y] = row;
+
+        /* Copy data from surface into current row */
+        for (x=0; x<width; x++) {
+
+            /* Get pixel color */
+            int color = ((uint32_t*) data)[x] & 0xFFFFFF;
+
+            /* Set index in row */
+            row[x] = guac_palette_find(palette, color);
+
+        }
+
+        /* Advance to next data row */
+        data += stride;
+
+    }
+
+    /* Write image info */
+    png_set_IHDR(
+        png,
+        png_info,
+        width,
+        height,
+        bpp,
+        PNG_COLOR_TYPE_PALETTE,
+        PNG_INTERLACE_NONE,
+        PNG_COMPRESSION_TYPE_DEFAULT,
+        PNG_FILTER_TYPE_DEFAULT
+    );
+
+    /* Write palette */
+    png_set_PLTE(png, png_info, palette->colors, palette->size);
+
+    /* Write image */
+    png_set_rows(png, png_info, png_rows);
+    png_write_png(png, png_info, PNG_TRANSFORM_PACKING, NULL);
+
+    /* Finish write */
+    png_destroy_write_struct(&png, &png_info);
+
+    /* Free palette */
+    guac_palette_free(palette);
+
+    /* Free PNG data */
+    for (y=0; y<height; y++)
+        free(png_rows[y]);
+    free(png_rows);
+
+    /* Ensure all data is written */
+    guac_png_flush_data(&write_state);
+    return 0;
+
+}
+
diff --git a/src/libguac/encode-png.h b/src/libguac/encode-png.h
new file mode 100644
index 0000000..49c00bf
--- /dev/null
+++ b/src/libguac/encode-png.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GUAC_ENCODE_PNG_H
+#define GUAC_ENCODE_PNG_H
+
+#include "config.h"
+
+#include "socket.h"
+#include "stream.h"
+
+#include <cairo/cairo.h>
+
+/**
+ * Encodes the given surface as a PNG, and sends the resulting data over the
+ * given stream and socket as blobs.
+ *
+ * @param socket
+ *     The socket to send PNG blobs over.
+ *
+ * @param stream
+ *     The stream to associate with each blob.
+ *
+ * @param surface
+ *     The Cairo surface to write to the given stream and socket as PNG blobs.
+ *
+ * @return
+ *     Zero if the encoding operation is successful, non-zero otherwise.
+ */
+int guac_png_write(guac_socket* socket, guac_stream* stream,
+        cairo_surface_t* surface);
+
+#endif
+
diff --git a/src/libguac/encode-webp.c b/src/libguac/encode-webp.c
new file mode 100644
index 0000000..ea518a4
--- /dev/null
+++ b/src/libguac/encode-webp.c
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "encode-webp.h"
+#include "error.h"
+#include "palette.h"
+#include "protocol.h"
+#include "stream.h"
+
+#include <cairo/cairo.h>
+#include <webp/encode.h>
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * Structure which describes the current state of the WebP image writer.
+ */
+typedef struct guac_webp_stream_writer {
+
+    /**
+     * The socket over which all WebP blobs will be written.
+     */
+    guac_socket* socket;
+
+    /**
+     * The Guacamole stream to associate with each WebP blob.
+     */
+    guac_stream* stream;
+
+    /**
+     * Buffer of pending WebP data.
+     */
+    char buffer[6048];
+
+    /**
+     * The number of bytes currently stored in the buffer.
+     */
+    int buffer_size;
+
+} guac_webp_stream_writer;
+
+/**
+ * Writes the contents of the WebP stream writer as a blob to its associated
+ * socket.
+ *
+ * @param writer
+ *     The writer structure to flush.
+ */
+static void guac_webp_flush_data(guac_webp_stream_writer* writer) {
+
+    /* Send blob */
+    guac_protocol_send_blob(writer->socket, writer->stream,
+            writer->buffer, writer->buffer_size);
+
+    /* Clear buffer */
+    writer->buffer_size = 0;
+
+}
+
+/**
+ * Configures the given stream writer object to use the given Guacamole stream
+ * object for WebP output.
+ *
+ * @param writer
+ *     The Guacamole WebP stream writer structure to configure.
+ *
+ * @param socket
+ *     The Guacamole socket to use when sending blob instructions.
+ *
+ * @param stream
+ *     The stream over which WebP-encoded blobs of image data should be sent.
+ */
+static void guac_webp_stream_writer_init(guac_webp_stream_writer* writer,
+        guac_socket* socket, guac_stream* stream) {
+
+    writer->buffer_size = 0;
+
+    /* Store Guacamole-specific objects */
+    writer->socket = socket;
+    writer->stream = stream;
+
+}
+
+/**
+ * WebP output function which appends the given WebP data to the internal
+ * buffer of the Guacamole stream writer structure, automatically flushing the
+ * writer as necessary.
+ *
+ * @param data
+ *     The segment of data to write.
+ *
+ * @param data_size
+ *     The size of segment of data to write.
+ *
+ * @param picture
+ *     The WebP picture associated with this write operation. Provides access to
+ *     picture->custom_ptr which contains the Guacamole stream writer structure.
+ *
+ * @return
+ *     Non-zero if writing was successful, zero on failure.
+ */
+static int guac_webp_stream_write(const uint8_t* data, size_t data_size,
+        const WebPPicture* picture) {
+
+    guac_webp_stream_writer* const writer =
+        (guac_webp_stream_writer*) picture->custom_ptr;
+    assert(writer != NULL);
+
+    const unsigned char* current = data;
+    int length = data_size;
+
+    /* Append all data given */
+    while (length > 0) {
+
+        /* Calculate space remaining */
+        int remaining = sizeof(writer->buffer) - writer->buffer_size;
+
+        /* If no space remains, flush buffer to make room */
+        if (remaining == 0) {
+            guac_webp_flush_data(writer);
+            remaining = sizeof(writer->buffer);
+        }
+
+        /* Calculate size of next block of data to append */
+        int block_size = remaining;
+        if (block_size > length)
+            block_size = length;
+
+        /* Append block */
+        memcpy(writer->buffer + writer->buffer_size,
+               current, block_size);
+
+        /* Next block */
+        current += block_size;
+        writer->buffer_size += block_size;
+        length -= block_size;
+
+    }
+
+    return 1;
+}
+
+int guac_webp_write(guac_socket* socket, guac_stream* stream,
+        cairo_surface_t* surface, int quality, int lossless) {
+
+    guac_webp_stream_writer writer;
+    WebPPicture picture;
+    uint32_t* argb_output;
+
+    int x, y;
+
+    int width = cairo_image_surface_get_width(surface);
+    int height = cairo_image_surface_get_height(surface);
+    int stride = cairo_image_surface_get_stride(surface);
+    cairo_format_t format = cairo_image_surface_get_format(surface);
+    unsigned char* data = cairo_image_surface_get_data(surface);
+
+    if (format != CAIRO_FORMAT_RGB24 && format != CAIRO_FORMAT_ARGB32) {
+        guac_error = GUAC_STATUS_INTERNAL_ERROR;
+        guac_error_message = "Invalid Cairo image format. Unable to create WebP.";
+        return -1;
+    }
+
+    /* Flush pending operations to surface */
+    cairo_surface_flush(surface);
+
+    /* Configure WebP compression bits */
+    WebPConfig config;
+    if (!WebPConfigPreset(&config, WEBP_PRESET_DEFAULT, quality))
+        return -1;
+
+    /* Add additional tuning */
+    config.lossless = lossless;
+    config.quality = quality;
+    config.thread_level = 1; /* Multi threaded */
+    config.method = 2; /* Compression method (0=fast/larger, 6=slow/smaller) */
+
+    /* Validate configuration */
+    WebPValidateConfig(&config);
+
+    /* Set up WebP picture */
+    WebPPictureInit(&picture);
+    picture.use_argb = 1;
+    picture.width = width;
+    picture.height = height;
+
+    /* Allocate and init writer */
+    WebPPictureAlloc(&picture);
+    picture.writer = guac_webp_stream_write;
+    picture.custom_ptr = &writer;
+    guac_webp_stream_writer_init(&writer, socket, stream);
+
+    /* Copy image data into WebP picture */
+    argb_output = picture.argb;
+    for (y = 0; y < height; y++) {
+
+        /* Get pixels at start of each row */
+        uint32_t* src = (uint32_t*) data;
+        uint32_t* dst = argb_output;
+
+        /* For each pixel in row */
+        for (x = 0; x < width; x++) {
+
+            /* Pull pixel data, removing alpha channel if necessary */
+            uint32_t src_pixel = *src;
+            if (format != CAIRO_FORMAT_ARGB32)
+                src_pixel |= 0xFF000000;
+
+            /* Store converted pixel data */
+            *dst = src_pixel;
+
+            /* Next pixel */
+            src++;
+            dst++;
+
+        }
+
+        /* Next row */
+        data += stride;
+        argb_output += picture.argb_stride;
+
+    }
+
+    /* Encode image */
+    WebPEncode(&config, &picture);
+
+    /* Free picture */
+    WebPPictureFree(&picture);
+
+    /* Ensure all data is written */
+    guac_webp_flush_data(&writer);
+
+    return 0;
+
+}
+
diff --git a/src/libguac/encode-webp.h b/src/libguac/encode-webp.h
new file mode 100644
index 0000000..a83bb3b
--- /dev/null
+++ b/src/libguac/encode-webp.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GUAC_ENCODE_WEBP_H
+#define GUAC_ENCODE_WEBP_H
+
+#include "config.h"
+
+#include "socket.h"
+#include "stream.h"
+
+#include <cairo/cairo.h>
+
+/**
+ * Encodes the given surface as a WebP, and sends the resulting data over the
+ * given stream and socket as blobs.
+ *
+ * @param socket
+ *     The socket to send WebP blobs over.
+ *
+ * @param stream
+ *     The stream to associate with each blob.
+ *
+ * @param surface
+ *     The Cairo surface to write to the given stream and socket as PNG blobs.
+ *
+ * @param quality
+ *     The WebP image quality to use. For lossy images, larger values indicate
+ *     improving quality at the expense of larger file size. For lossless
+ *     images, this dictates the quality of compression, with larger values
+ *     producing smaller files at the expense of speed.
+ *
+ * @param lossless
+ *     Zero for a lossy image, non-zero for lossless.
+ *
+ * @return
+ *     Zero if the encoding operation is successful, non-zero otherwise.
+ */
+int guac_webp_write(guac_socket* socket, guac_stream* stream,
+        cairo_surface_t* surface, int quality, int lossless);
+
+#endif
diff --git a/src/libguac/error.c b/src/libguac/error.c
index 869259f..b6a9377 100644
--- a/src/libguac/error.c
+++ b/src/libguac/error.c
@@ -1,61 +1,65 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "error.h"
 
-#include <stdlib.h>
 #include <errno.h>
+#include <stdlib.h>
 #include <string.h>
 
 #ifdef HAVE_LIBPTHREAD
 #include <pthread.h>
 #endif
 
-#include "error.h"
-
-/* Error strings */
-
-const char* __GUAC_STATUS_SUCCESS_STR        = "Success";
-const char* __GUAC_STATUS_NO_MEMORY_STR      = "Insufficient memory";
-const char* __GUAC_STATUS_NO_INPUT_STR       = "End of input stream";
-const char* __GUAC_STATUS_INPUT_TIMEOUT_STR  = "Read timeout";
-const char* __GUAC_STATUS_OUTPUT_ERROR_STR   = "Output error";
-const char* __GUAC_STATUS_BAD_ARGUMENT_STR   = "Invalid argument";
-const char* __GUAC_STATUS_BAD_STATE_STR      = "Illegal state";
-const char* __GUAC_STATUS_INVALID_STATUS_STR = "UNKNOWN STATUS CODE";
-
+/*
+ * Error strings
+ */
+
+const char* __GUAC_STATUS_SUCCESS_STR           = "Success";
+const char* __GUAC_STATUS_NO_MEMORY_STR         = "Insufficient memory";
+const char* __GUAC_STATUS_CLOSED_STR            = "Closed";
+const char* __GUAC_STATUS_TIMEOUT_STR           = "Timed out";
+const char* __GUAC_STATUS_IO_ERROR_STR          = "Input/output error";
+const char* __GUAC_STATUS_INVALID_ARGUMENT_STR  = "Invalid argument";
+const char* __GUAC_STATUS_INTERNAL_ERROR_STR    = "Internal error";
+const char* __GUAC_STATUS_UNKNOWN_STATUS_STR    = "UNKNOWN STATUS CODE";
+const char* __GUAC_STATUS_NO_SPACE_STR          = "Insufficient space";
+const char* __GUAC_STATUS_INPUT_TOO_LARGE_STR   = "Input too large";
+const char* __GUAC_STATUS_RESULT_TOO_LARGE_STR  = "Result too large";
+const char* __GUAC_STATUS_PERMISSION_DENIED_STR = "Permission denied";
+const char* __GUAC_STATUS_BUSY_STR              = "Resource busy";
+const char* __GUAC_STATUS_NOT_AVAILABLE_STR     = "Resource not available";
+const char* __GUAC_STATUS_NOT_SUPPORTED_STR     = "Not supported";
+const char* __GUAC_STATUS_NOT_INPLEMENTED_STR   = "Not implemented";
+const char* __GUAC_STATUS_TRY_AGAIN_STR         = "Temporary failure";
+const char* __GUAC_STATUS_PROTOCOL_ERROR_STR    = "Protocol violation";
+const char* __GUAC_STATUS_NOT_FOUND_STR         = "Not found";
+const char* __GUAC_STATUS_CANCELED_STR          = "Canceled";
+const char* __GUAC_STATUS_OUT_OF_RANGE_STR      = "Value out of range";
+const char* __GUAC_STATUS_REFUSED_STR           = "Operation refused";
+const char* __GUAC_STATUS_TOO_MANY_STR          = "Insufficient resources";
+const char* __GUAC_STATUS_WOULD_BLOCK_STR       = "Operation would block";
 
 const char* guac_status_string(guac_status status) {
 
@@ -69,32 +73,97 @@ const char* guac_status_string(guac_status status) {
 		case GUAC_STATUS_NO_MEMORY:
             return __GUAC_STATUS_NO_MEMORY_STR;
 
-        /* End of input */
-		case GUAC_STATUS_NO_INPUT:
-            return __GUAC_STATUS_NO_INPUT_STR;
+        /* End of stream */
+		case GUAC_STATUS_CLOSED:
+            return __GUAC_STATUS_CLOSED_STR;
 
-        /* Input timeout */
-		case GUAC_STATUS_INPUT_TIMEOUT:
-            return __GUAC_STATUS_INPUT_TIMEOUT_STR;
+        /* Timeout */
+		case GUAC_STATUS_TIMEOUT:
+            return __GUAC_STATUS_TIMEOUT_STR;
 
         /* Further information in errno */
 		case GUAC_STATUS_SEE_ERRNO:
             return strerror(errno);
 
-        /* Output error */
-		case GUAC_STATUS_OUTPUT_ERROR:
-            return __GUAC_STATUS_OUTPUT_ERROR_STR;
+        /* Input/output error */
+		case GUAC_STATUS_IO_ERROR:
+            return __GUAC_STATUS_IO_ERROR_STR;
 
         /* Invalid argument */
-		case GUAC_STATUS_BAD_ARGUMENT:
-            return __GUAC_STATUS_BAD_ARGUMENT_STR;
+		case GUAC_STATUS_INVALID_ARGUMENT:
+            return __GUAC_STATUS_INVALID_ARGUMENT_STR;
+
+        /* Internal error */
+		case GUAC_STATUS_INTERNAL_ERROR:
+            return __GUAC_STATUS_INTERNAL_ERROR_STR;
+
+        /* Out of space */
+		case GUAC_STATUS_NO_SPACE:
+            return __GUAC_STATUS_NO_SPACE_STR;
+
+        /* Input too large */
+        case GUAC_STATUS_INPUT_TOO_LARGE:
+            return __GUAC_STATUS_INPUT_TOO_LARGE_STR;
+
+        /* Result too large */
+        case GUAC_STATUS_RESULT_TOO_LARGE:
+            return __GUAC_STATUS_RESULT_TOO_LARGE_STR;
+
+        /* Permission denied */
+        case GUAC_STATUS_PERMISSION_DENIED:
+            return __GUAC_STATUS_PERMISSION_DENIED_STR;
+
+        /* Resource is busy */
+        case GUAC_STATUS_BUSY:
+            return __GUAC_STATUS_BUSY_STR;
+
+        /* Resource not available */
+        case GUAC_STATUS_NOT_AVAILABLE:
+            return __GUAC_STATUS_NOT_AVAILABLE_STR;
+
+        /* Not supported */
+        case GUAC_STATUS_NOT_SUPPORTED:
+            return __GUAC_STATUS_NOT_SUPPORTED_STR;
+
+        /* Not implemented */
+        case GUAC_STATUS_NOT_INPLEMENTED:
+            return __GUAC_STATUS_NOT_INPLEMENTED_STR;
+
+        /* Temporary failure */
+        case GUAC_STATUS_TRY_AGAIN:
+            return __GUAC_STATUS_TRY_AGAIN_STR;
+
+        /* Guacamole protocol error */
+        case GUAC_STATUS_PROTOCOL_ERROR:
+            return __GUAC_STATUS_PROTOCOL_ERROR_STR;
+
+        /* Resource not found */
+        case GUAC_STATUS_NOT_FOUND:
+            return __GUAC_STATUS_NOT_FOUND_STR;
+
+        /* Operation canceled */
+        case GUAC_STATUS_CANCELED:
+            return __GUAC_STATUS_CANCELED_STR;
+
+        /* Value out of range */
+        case GUAC_STATUS_OUT_OF_RANGE:
+            return __GUAC_STATUS_OUT_OF_RANGE_STR;
+
+        /* Operation refused */
+        case GUAC_STATUS_REFUSED:
+            return __GUAC_STATUS_REFUSED_STR;
+
+        /* Too many resource in use */
+        case GUAC_STATUS_TOO_MANY:
+            return __GUAC_STATUS_TOO_MANY_STR;
 
-        /* Illegal state */
-		case GUAC_STATUS_BAD_STATE:
-            return __GUAC_STATUS_BAD_STATE_STR;
+        /* Operation would block */
+        case GUAC_STATUS_WOULD_BLOCK:
+            return __GUAC_STATUS_WOULD_BLOCK_STR;
 
+        /* Unknown status code */
         default:
-            return __GUAC_STATUS_INVALID_STATUS_STR;
+            return __GUAC_STATUS_UNKNOWN_STATUS_STR;
 
     }
 
diff --git a/src/libguac/guacamole/audio-fntypes.h b/src/libguac/guacamole/audio-fntypes.h
new file mode 100644
index 0000000..7880eac
--- /dev/null
+++ b/src/libguac/guacamole/audio-fntypes.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef __GUAC_AUDIO_FNTYPES_H
+#define __GUAC_AUDIO_FNTYPES_H
+
+/**
+ * Function type definitions related to simple streaming audio.
+ *
+ * @file audio-fntypes.h
+ */
+
+#include "audio-types.h"
+
+/**
+ * Handler which is called when the audio stream is opened.
+ */
+typedef void guac_audio_encoder_begin_handler(guac_audio_stream* audio);
+
+/**
+ * Handler which is called when the audio stream needs to be flushed.
+ */
+typedef void guac_audio_encoder_flush_handler(guac_audio_stream* audio);
+
+/**
+ * Handler which is called when the audio stream is closed.
+ */
+typedef void guac_audio_encoder_end_handler(guac_audio_stream* audio);
+
+/**
+ * Handler which is called when PCM data is written to the audio stream.
+ */
+typedef void guac_audio_encoder_write_handler(guac_audio_stream* audio,
+        const unsigned char* pcm_data, int length);
+
+#endif
+
diff --git a/src/libguac/guacamole/audio-types.h b/src/libguac/guacamole/audio-types.h
new file mode 100644
index 0000000..de63c57
--- /dev/null
+++ b/src/libguac/guacamole/audio-types.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef __GUAC_AUDIO_TYPES_H
+#define __GUAC_AUDIO_TYPES_H
+
+/**
+ * Type definitions related to simple streaming audio.
+ *
+ * @file audio-types.h
+ */
+
+/**
+ * Basic audio stream. PCM data is added to the stream. When the stream is
+ * flushed, a write handler receives PCM data packets and, presumably, streams
+ * them to the guac_stream provided.
+ */
+typedef struct guac_audio_stream guac_audio_stream;
+
+/**
+ * Arbitrary audio codec encoder.
+ */
+typedef struct guac_audio_encoder guac_audio_encoder;
+
+#endif
+
diff --git a/src/libguac/guacamole/audio.h b/src/libguac/guacamole/audio.h
index a243fa5..6b6696f 100644
--- a/src/libguac/guacamole/audio.h
+++ b/src/libguac/guacamole/audio.h
@@ -1,74 +1,41 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2015 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef __GUAC_AUDIO_H
 #define __GUAC_AUDIO_H
 
-#include <guacamole/client.h>
-#include <guacamole/stream.h>
-
 /**
  * Provides functions and structures used for providing simple streaming audio.
  *
  * @file audio.h
  */
 
-typedef struct guac_audio_stream guac_audio_stream;
-
-/**
- * Handler which is called when the audio stream is opened.
- */
-typedef void guac_audio_encoder_begin_handler(guac_audio_stream* audio);
-
-/**
- * Handler which is called when the audio stream is closed.
- */
-typedef void guac_audio_encoder_end_handler(guac_audio_stream* audio);
-
-/**
- * Handler which is called when the audio stream is flushed.
- */
-typedef void guac_audio_encoder_write_handler(guac_audio_stream* audio,
-        const unsigned char* pcm_data, int length);
+#include "audio-fntypes.h"
+#include "audio-types.h"
+#include "client-types.h"
+#include "stream-types.h"
 
-/**
- * Arbitrary audio codec encoder.
- */
-typedef struct guac_audio_encoder {
+struct guac_audio_encoder {
 
     /**
      * The mimetype of the audio data encoded by this audio
@@ -77,62 +44,32 @@ typedef struct guac_audio_encoder {
     const char* mimetype;
 
     /**
-     * Handler which will be called when the audio stream is opened.
+     * Handler which will be called when the audio stream is first created.
      */
     guac_audio_encoder_begin_handler* begin_handler;
 
     /**
-     * Handler which will be called when the audio stream is flushed.
+     * Handler which will be called when PCM data is written to the audio
+     * stream for encoding.
      */
     guac_audio_encoder_write_handler* write_handler;
 
     /**
-     * Handler which will be called when the audio stream is closed.
-     */
-    guac_audio_encoder_end_handler* end_handler;
-
-} guac_audio_encoder;
-
-/**
- * Basic audio stream. PCM data is added to the stream. When the stream is
- * flushed, a write handler receives PCM data packets and, presumably, streams
- * them to the guac_stream provided.
- */
-struct guac_audio_stream {
-
-    /**
-     * PCM data buffer, 16-bit samples, 2-channel, 44100 Hz.
-     */
-    unsigned char* pcm_data;
-
-    /**
-     * Number of bytes in buffer.
-     */
-    int used;
-
-    /**
-     * Maximum number of bytes in buffer.
+     * Handler which will be called when the audio stream is flushed.
      */
-    int length;
+    guac_audio_encoder_flush_handler* flush_handler;
 
     /**
-     * Encoded audio data buffer, as written by the encoder.
+     * Handler which will be called when the audio stream is closed.
      */
-    unsigned char* encoded_data;
+    guac_audio_encoder_end_handler* end_handler;
 
-    /**
-     * Number of bytes in the encoded data buffer.
-     */
-    int encoded_data_used;
+};
 
-    /**
-     * Maximum number of bytes in the encoded data buffer.
-     */
-    int encoded_data_length;
+struct guac_audio_stream {
 
     /**
-     * Arbitrary codec encoder. When the PCM buffer is flushed, PCM data will
-     * be sent to this encoder.
+     * Arbitrary codec encoder which will receive raw PCM data.
      */
     guac_audio_encoder* encoder;
 
@@ -164,11 +101,6 @@ struct guac_audio_stream {
     int bps;
 
     /**
-     * The number of PCM bytes written since the audio chunk began.
-     */
-    int pcm_bytes_written;
-
-    /**
      * Encoder-specific state data.
      */
     void* data;
@@ -179,79 +111,97 @@ struct guac_audio_stream {
  * Allocates a new audio stream which encodes audio data using the given
  * encoder. If NULL is specified for the encoder, an appropriate encoder
  * will be selected based on the encoders built into libguac and the level
- * of client support.
+ * of client support. The PCM format specified here (via rate, channels, and
+ * bps) must be the format used for all PCM data provided to the audio stream.
+ * The format may only be changed using guac_audio_stream_reset().
+ *
+ * @param client
+ *     The guac_client for which this audio stream is being allocated.
+ *
+ * @param encoder
+ *     The guac_audio_encoder to use when encoding audio, or NULL if libguac
+ *     should select an appropriate built-in encoder on its own.
+ *
+ * @param rate
+ *     The number of samples per second of PCM data sent to this stream.
  *
- * @param client The guac_client for which this audio stream is being
- *               allocated.
- * @param encoder The guac_audio_encoder to use when encoding audio, or
- *                NULL if libguac should select an appropriate built-in
- *                encoder on its own.
- * @return The newly allocated guac_audio_stream, or NULL if no audio
- *         stream could be allocated due to lack of client support.
+ * @param channels
+ *     The number of audio channels per sample of PCM data. Legal values are
+ *     1 or 2.
+ *
+ * @param bps
+ *     The number of bits per sample per channel for PCM data. Legal values are
+ *     8 or 16.
+ *
+ * @return
+ *     The newly allocated guac_audio_stream, or NULL if no audio stream could
+ *     be allocated due to lack of client support.
  */
 guac_audio_stream* guac_audio_stream_alloc(guac_client* client,
-        guac_audio_encoder* encoder);
+        guac_audio_encoder* encoder, int rate, int channels, int bps);
 
 /**
- * Frees the given audio stream.
+ * Resets the given audio stream, switching to the given encoder, rate,
+ * channels, and bits per sample. If NULL is specified for the encoder, the
+ * encoder is left unchanged. If the encoder, rate, channels, and bits per
+ * sample are all identical to the current settings, this function has no
+ * effect.
  *
- * @param stream The guac_audio_stream to free.
- */
-void guac_audio_stream_free(guac_audio_stream* stream);
-
-/**
- * Begins a new audio packet within the given audio stream. This packet will be
- * built up with repeated writes of PCM data, finally being sent when complete
- * via guac_audio_stream_end().
+ * @param audio
+ *     The guac_audio_stream to reset.
+ *
+ * @param encoder
+ *     The guac_audio_encoder to use when encoding audio, or NULL to leave this
+ *     unchanged.
+ *
+ * @param rate
+ *     The number of samples per second of PCM data sent to this stream.
+ *
+ * @param channels
+ *     The number of audio channels per sample of PCM data. Legal values are
+ *     1 or 2.
  *
- * @param stream The guac_audio_stream which should start a new audio packet.
- * @param rate The audio rate of the packet, in Hz.
- * @param channels The number of audio channels.
- * @param bps The number of bits per audio sample.
+ * @param bps
+ *     The number of bits per sample per channel for PCM data. Legal values are
+ *     8 or 16.
  */
-void guac_audio_stream_begin(guac_audio_stream* stream, int rate, int channels, int bps);
+void guac_audio_stream_reset(guac_audio_stream* audio,
+        guac_audio_encoder* encoder, int rate, int channels, int bps);
 
 /**
- * Ends the current audio packet, writing the finished packet as an audio
- * instruction.
+ * Closes and frees the given audio stream.
  *
- * @param stream The guac_audio_stream whose current audio packet should be
- *               completed and sent.
+ * @param stream
+ *     The guac_audio_stream to free.
  */
-void guac_audio_stream_end(guac_audio_stream* stream);
+void guac_audio_stream_free(guac_audio_stream* stream);
 
 /**
  * Writes PCM data to the given audio stream. This PCM data will be
- * automatically encoded by the audio encoder associated with this stream. This
- * function must only be called after an audio packet has been started with
- * guac_audio_stream_begin().
+ * automatically encoded by the audio encoder associated with this stream. The
+ * PCM data must be 2-channel, 44100 Hz, with signed 16-bit samples.
+ *
+ * @param stream
+ *     The guac_audio_stream to write PCM data through.
  *
- * @param stream The guac_audio_stream to write PCM data through.
- * @param data The PCM data to write.
- * @param length The number of bytes of PCM data provided.
+ * @param data
+ *     The PCM data to write.
+ *
+ * @param length
+ *     The number of bytes of PCM data provided.
  */
 void guac_audio_stream_write_pcm(guac_audio_stream* stream,
         const unsigned char* data, int length);
 
 /**
- * Flushes the given audio stream.
+ * Flushes the underlying audio buffer, if any, ensuring that all audio
+ * previously written via guac_audio_stream_write_pcm() has been encoded and
+ * sent to the client.
  *
- * @param stream The guac_audio_stream to flush.
+ * @param stream
+ *     The guac_audio_stream whose audio buffers should be flushed.
  */
 void guac_audio_stream_flush(guac_audio_stream* stream);
 
-/**
- * Appends arbitrarily-encoded data to the encoded_data buffer within the given
- * audio stream. This data must be encoded in the output format of the encoder
- * used by the stream. This function is mainly for use by encoder
- * implementations.
- *
- * @param audio The guac_audio_stream to write data through.
- * @param data Arbitrary encoded data to write through the audio stream.
- * @param length The number of bytes of data provided.
- */
-void guac_audio_stream_write_encoded(guac_audio_stream* audio,
-        const unsigned char* data, int length);
-
 #endif
 
diff --git a/src/libguac/guacamole/client-constants.h b/src/libguac/guacamole/client-constants.h
new file mode 100644
index 0000000..dfa45db
--- /dev/null
+++ b/src/libguac/guacamole/client-constants.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _GUAC_CLIENT_CONSTANTS_H
+#define _GUAC_CLIENT_CONSTANTS_H
+
+/**
+ * Constants related to the Guacamole client structure, guac_client.
+ *
+ * @file client-constants.h
+ */
+
+/**
+ * The maximum number of inbound streams supported by any one guac_client.
+ */
+#define GUAC_CLIENT_MAX_STREAMS 64
+
+/**
+ * The index of a closed stream.
+ */
+#define GUAC_CLIENT_CLOSED_STREAM_INDEX -1
+
+/**
+ * The maximum number of objects supported by any one guac_client.
+ */
+#define GUAC_CLIENT_MAX_OBJECTS 64
+
+/**
+ * The index of an object which has not been defined.
+ */
+#define GUAC_CLIENT_UNDEFINED_OBJECT_INDEX -1
+
+/**
+ * The stream name reserved for the root of a Guacamole protocol object.
+ */
+#define GUAC_CLIENT_OBJECT_ROOT_NAME "/"
+
+/**
+ * The mimetype of a stream containing a map of available stream names to their
+ * corresponding mimetypes. The root of a Guacamole protocol object is
+ * guaranteed to have this type.
+ */
+#define GUAC_CLIENT_STREAM_INDEX_MIMETYPE "application/vnd.glyptodon.guacamole.stream-index+json"
+
+/**
+ * The flag set in the mouse button mask when the left mouse button is down.
+ */
+#define GUAC_CLIENT_MOUSE_LEFT 0x01
+
+/**
+ * The flag set in the mouse button mask when the middle mouse button is down.
+ */
+#define GUAC_CLIENT_MOUSE_MIDDLE 0x02
+
+/**
+ * The flag set in the mouse button mask when the right mouse button is down.
+ */
+#define GUAC_CLIENT_MOUSE_RIGHT 0x04
+
+/**
+ * The flag set in the mouse button mask when the mouse scrollwheel is scrolled
+ * up. Note that mouse scrollwheels are actually sets of two buttons. One
+ * button is pressed and released for an upward scroll, and the other is
+ * pressed and released for a downward scroll. Some mice may actually implement
+ * these as separate buttons, not a wheel.
+ */
+#define GUAC_CLIENT_MOUSE_SCROLL_UP 0x08
+
+/**
+ * The flag set in the mouse button mask when the mouse scrollwheel is scrolled
+ * down. Note that mouse scrollwheels are actually sets of two buttons. One
+ * button is pressed and released for an upward scroll, and the other is
+ * pressed and released for a downward scroll. Some mice may actually implement
+ * these as separate buttons, not a wheel.
+ */
+#define GUAC_CLIENT_MOUSE_SCROLL_DOWN 0x10
+
+/**
+ * The minimum number of buffers to create before allowing free'd buffers to
+ * be reclaimed. In the case a protocol rapidly creates, uses, and destroys
+ * buffers, this can prevent unnecessary reuse of the same buffer (which
+ * would make draw operations unnecessarily synchronous).
+ */
+#define GUAC_BUFFER_POOL_INITIAL_SIZE 1024
+
+#endif
+
diff --git a/src/libguac/guacamole/client-fntypes.h b/src/libguac/guacamole/client-fntypes.h
new file mode 100644
index 0000000..1a11f35
--- /dev/null
+++ b/src/libguac/guacamole/client-fntypes.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _GUAC_CLIENT_FNTYPES_H
+#define _GUAC_CLIENT_FNTYPES_H
+
+/**
+ * Function type definitions related to the Guacamole client structure,
+ * guac_client.
+ *
+ * @file client-fntypes.h
+ */
+
+#include "client-types.h"
+#include "object-types.h"
+#include "protocol-types.h"
+#include "stream-types.h"
+
+#include <stdarg.h>
+
+/**
+ * Handler for server messages (where "server" refers to the server that
+ * the proxy client is connected to).
+ */
+typedef int guac_client_handle_messages(guac_client* client);
+
+/**
+ * Handler for Guacamole mouse events.
+ */
+typedef int guac_client_mouse_handler(guac_client* client, int x, int y, int button_mask);
+
+/**
+ * Handler for Guacamole key events.
+ */
+typedef int guac_client_key_handler(guac_client* client, int keysym, int pressed);
+
+/**
+ * Handler for Guacamole clipboard events.
+ */
+typedef int guac_client_clipboard_handler(guac_client* client, guac_stream* stream,
+        char* mimetype);
+/**
+ * Handler for Guacamole screen size events.
+ */
+typedef int guac_client_size_handler(guac_client* client,
+        int width, int height);
+
+/**
+ * Handler for Guacamole file transfer events.
+ */
+typedef int guac_client_file_handler(guac_client* client, guac_stream* stream,
+        char* mimetype, char* filename);
+
+/**
+ * Handler for Guacamole pipe events.
+ */
+typedef int guac_client_pipe_handler(guac_client* client, guac_stream* stream,
+        char* mimetype, char* name);
+
+/**
+ * Handler for Guacamole stream blob events.
+ */
+typedef int guac_client_blob_handler(guac_client* client, guac_stream* stream,
+        void* data, int length);
+
+/**
+ * Handler for Guacamole stream ack events.
+ */
+typedef int guac_client_ack_handler(guac_client* client, guac_stream* stream,
+        char* error, guac_protocol_status status);
+
+/**
+ * Handler for Guacamole stream end events.
+ */
+typedef int guac_client_end_handler(guac_client* client, guac_stream* stream);
+
+/**
+ * Handler for Guacamole object get events.
+ */
+typedef int guac_client_get_handler(guac_client* client, guac_object* object,
+        char* name);
+
+/**
+ * Handler for Guacamole object put events.
+ */
+typedef int guac_client_put_handler(guac_client* client, guac_object* object,
+        guac_stream* stream, char* mimetype, char* name);
+
+/**
+ * Handler for Guacamole audio format events.
+ */
+typedef int guac_client_audio_handler(guac_client* client, char* mimetype);
+
+/**
+ * Handler for Guacamole video format events.
+ */
+typedef int guac_client_video_handler(guac_client* client, char* mimetype);
+
+/**
+ * Handler for freeing up any extra data allocated by the client
+ * implementation.
+ */
+typedef int guac_client_free_handler(guac_client* client);
+
+/**
+ * Handler for logging messages
+ */
+typedef void guac_client_log_handler(guac_client* client, guac_client_log_level level, const char* format, va_list args); 
+
+/**
+ * Handler which should initialize the given guac_client.
+ */
+typedef int guac_client_init_handler(guac_client* client, int argc, char** argv);
+
+#endif
+
diff --git a/src/libguac/guacamole/client-types.h b/src/libguac/guacamole/client-types.h
new file mode 100644
index 0000000..ec32b95
--- /dev/null
+++ b/src/libguac/guacamole/client-types.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _GUAC_CLIENT_TYPES_H
+#define _GUAC_CLIENT_TYPES_H
+
+/**
+ * Type definitions related to the Guacamole client structure, guac_client.
+ *
+ * @file client-types.h
+ */
+
+/**
+ * Guacamole proxy client.
+ *
+ * Represents a Guacamole proxy client (the client which communicates to
+ * a server on behalf of Guacamole, on behalf of the web-client).
+ */
+typedef struct guac_client guac_client;
+
+/**
+ * Possible current states of the Guacamole client. Currently, the only
+ * two states are GUAC_CLIENT_RUNNING and GUAC_CLIENT_STOPPING.
+ */
+typedef enum guac_client_state {
+
+    /**
+     * The state of the client from when it has been allocated by the main
+     * daemon until it is killed or disconnected.
+     */
+    GUAC_CLIENT_RUNNING,
+
+    /**
+     * The state of the client when a stop has been requested, signalling the
+     * I/O threads to shutdown.
+     */
+    GUAC_CLIENT_STOPPING
+
+} guac_client_state;
+
+/**
+ * All supported log levels used by the logging subsystem of each Guacamole
+ * client. These log levels correspond to a subset of the log levels defined by
+ * RFC 5424.
+ */
+typedef enum guac_client_log_level {
+
+    /**
+     * Fatal errors.
+     */
+    GUAC_LOG_ERROR = 3,
+
+    /**
+     * Non-fatal conditions that indicate problems.
+     */
+    GUAC_LOG_WARNING = 4,
+
+    /**
+     * Informational messages of general interest to users or administrators.
+     */
+    GUAC_LOG_INFO = 6,
+
+    /**
+     * Informational messages which can be useful for debugging, but are
+     * otherwise not useful to users or administrators.
+     */
+    GUAC_LOG_DEBUG = 7
+
+} guac_client_log_level;
+
+/**
+ * Information exposed by the remote client during the connection handshake
+ * which can be used by a client plugin.
+ */
+typedef struct guac_client_info guac_client_info;
+
+#endif
+
diff --git a/src/libguac/guacamole/client.h b/src/libguac/guacamole/client.h
index 0e2e78e..d777ada 100644
--- a/src/libguac/guacamole/client.h
+++ b/src/libguac/guacamole/client.h
@@ -1,180 +1,49 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
 #ifndef _GUAC_CLIENT_H
 #define _GUAC_CLIENT_H
 
-#include <stdarg.h>
-
-#include "instruction.h"
-#include "layer.h"
-#include "pool.h"
-#include "socket.h"
-#include "stream.h"
-#include "timestamp.h"
-
 /**
- * Provides functions and structures required for defining (and handling) a proxy client.
+ * Functions and structure contents for the Guacamole proxy client.
  *
  * @file client.h
  */
 
-typedef struct guac_client guac_client;
-
-/**
- * Handler for server messages (where "server" refers to the server that
- * the proxy client is connected to).
- */
-typedef int guac_client_handle_messages(guac_client* client);
-
-/**
- * Handler for Guacamole mouse events.
- */
-typedef int guac_client_mouse_handler(guac_client* client, int x, int y, int button_mask);
-
-/**
- * Handler for Guacamole key events.
- */
-typedef int guac_client_key_handler(guac_client* client, int keysym, int pressed);
-
-/**
- * Handler for Guacamole clipboard events.
- */
-typedef int guac_client_clipboard_handler(guac_client* client, char* copied);
-
-/**
- * Handler for Guacamole screen size events.
- */
-typedef int guac_client_size_handler(guac_client* client,
-        int width, int height);
-
-/**
- * Handler for Guacamole audio format events.
- */
-typedef int guac_client_audio_handler(guac_client* client, char* mimetype);
-
-/**
- * Handler for Guacamole video format events.
- */
-typedef int guac_client_video_handler(guac_client* client, char* mimetype);
-
-/**
- * Handler for freeing up any extra data allocated by the client
- * implementation.
- */
-typedef int guac_client_free_handler(guac_client* client);
-
-/**
- * Handler for logging messages
- */
-typedef void guac_client_log_handler(guac_client* client, const char* format, va_list args); 
-
-/**
- * Handler which should initialize the given guac_client.
- */
-typedef int guac_client_init_handler(guac_client* client, int argc, char** argv);
-
-/**
- * The flag set in the mouse button mask when the left mouse button is down.
- */
-#define GUAC_CLIENT_MOUSE_LEFT        0x01
-
-/**
- * The flag set in the mouse button mask when the middle mouse button is down.
- */
-#define GUAC_CLIENT_MOUSE_MIDDLE      0x02
-
-/**
- * The flag set in the mouse button mask when the right mouse button is down.
- */
-#define GUAC_CLIENT_MOUSE_RIGHT       0x04
-
-/**
- * The flag set in the mouse button mask when the mouse scrollwheel is scrolled
- * up. Note that mouse scrollwheels are actually sets of two buttons. One
- * button is pressed and released for an upward scroll, and the other is
- * pressed and released for a downward scroll. Some mice may actually implement
- * these as separate buttons, not a wheel.
- */
-#define GUAC_CLIENT_MOUSE_SCROLL_UP   0x08
-
-/**
- * The flag set in the mouse button mask when the mouse scrollwheel is scrolled
- * down. Note that mouse scrollwheels are actually sets of two buttons. One
- * button is pressed and released for an upward scroll, and the other is
- * pressed and released for a downward scroll. Some mice may actually implement
- * these as separate buttons, not a wheel.
- */
-#define GUAC_CLIENT_MOUSE_SCROLL_DOWN 0x10
-
-/**
- * The minimum number of buffers to create before allowing free'd buffers to
- * be reclaimed. In the case a protocol rapidly creates, uses, and destroys
- * buffers, this can prevent unnecessary reuse of the same buffer (which
- * would make draw operations unnecessarily synchronous).
- */
-#define GUAC_BUFFER_POOL_INITIAL_SIZE 1024
-
-/**
- * Possible current states of the Guacamole client. Currently, the only
- * two states are GUAC_CLIENT_RUNNING and GUAC_CLIENT_STOPPING.
- */
-typedef enum guac_client_state {
-
-    /**
-     * The state of the client from when it has been allocated by the main
-     * daemon until it is killed or disconnected.
-     */
-    GUAC_CLIENT_RUNNING,
+#include "client-fntypes.h"
+#include "client-types.h"
+#include "client-constants.h"
+#include "instruction-types.h"
+#include "layer-types.h"
+#include "object-types.h"
+#include "pool-types.h"
+#include "socket-types.h"
+#include "stream-types.h"
+#include "timestamp-types.h"
 
-    /**
-     * The state of the client when a stop has been requested, signalling the
-     * I/O threads to shutdown.
-     */
-    GUAC_CLIENT_STOPPING
+#include <cairo/cairo.h>
+#include <stdarg.h>
 
-} guac_client_state;
-
-/**
- * Information exposed by the remote client during the connection handshake
- * which can be used by a client plugin.
- */
-typedef struct guac_client_info {
+struct guac_client_info {
 
     /**
      * The number of pixels the remote client requests for the display width.
@@ -196,22 +65,32 @@ typedef struct guac_client_info {
      * NULL-terminated array of client-supported audio mimetypes. If the client
      * does not support audio at all, this will be NULL.
      */
-    const char** audio_mimetypes;
+    char** audio_mimetypes;
 
     /**
      * NULL-terminated array of client-supported video mimetypes. If the client
      * does not support video at all, this will be NULL.
      */
-    const char** video_mimetypes;
+    char** video_mimetypes;
 
-} guac_client_info;
+    /**
+     * NULL-terminated array of client-supported image mimetypes. Though all
+     * supported image mimetypes will be listed here, it can be safely assumed
+     * that all clients will support at least "image/png" and "image/jpeg".
+     */ 
+    char** image_mimetypes;
+
+    /**
+     * The DPI of the physical remote display if configured for the optimal
+     * width/height combination described here. This need not be honored by
+     * a client plugin implementation, but if the underlying protocol of the
+     * client plugin supports dynamic sizing of the screen, honoring the
+     * stated resolution of the display size request is recommended.
+     */
+    int optimal_resolution;
+
+};
 
-/**
- * Guacamole proxy client.
- *
- * Represents a Guacamole proxy client (the client which communicates to
- * a server on behalf of Guacamole, on behalf of the web-client).
- */
 struct guac_client {
 
     /**
@@ -322,14 +201,14 @@ struct guac_client {
      * handler will be called whenever the web-client sets the data of the
      * clipboard.
      *
-     * This handler takes a single string which contains the text which
-     * has been set in the clipboard. This text is already unescaped from
-     * the Guacamole escaped version sent within the clipboard message
-     * in the protocol.
+     * The handler takes a guac_stream, which contains the stream index and
+     * will persist through the duration of the transfer, and the mimetype
+     * of the data being transferred.
      *
      * Example:
      * @code
-     *     int clipboard_handler(guac_client* client, char* copied);
+     *     int clipboard_handler(guac_client* client, guac_stream* stream,
+     *             char* mimetype);
      *
      *     int guac_client_init(guac_client* client, int argc, char** argv) {
      *         client->clipboard_handler = clipboard_handler;
@@ -356,6 +235,100 @@ struct guac_client {
     guac_client_size_handler* size_handler;
 
     /**
+     * Handler for file events sent by the Guacamole web-client.
+     *
+     * The handler takes a guac_stream which contains the stream index and
+     * will persist through the duration of the transfer, the mimetype of
+     * the file being transferred, and the filename.
+     *
+     * Example:
+     * @code
+     *     int file_handler(guac_client* client, guac_stream* stream,
+     *             char* mimetype, char* filename);
+     *
+     *     int guac_client_init(guac_client* client, int argc, char** argv) {
+     *         client->file_handler = file_handler;
+     *     }
+     * @endcode
+     */
+    guac_client_file_handler* file_handler;
+
+    /**
+     * Handler for pipe events sent by the Guacamole web-client.
+     *
+     * The handler takes a guac_stream which contains the stream index and
+     * will persist through the duration of the transfer, the mimetype of
+     * the data being transferred, and the pipe name.
+     *
+     * Example:
+     * @code
+     *     int pipe_handler(guac_client* client, guac_stream* stream,
+     *             char* mimetype, char* name);
+     *
+     *     int guac_client_init(guac_client* client, int argc, char** argv) {
+     *         client->pipe_handler = pipe_handler;
+     *     }
+     * @endcode
+     */
+    guac_client_pipe_handler* pipe_handler;
+
+    /**
+     * Handler for ack events sent by the Guacamole web-client.
+     *
+     * The handler takes a guac_stream which contains the stream index and
+     * will persist through the duration of the transfer, a string containing
+     * the error or status message, and a status code.
+     *
+     * Example:
+     * @code
+     *     int ack_handler(guac_client* client, guac_stream* stream,
+     *             char* error, guac_protocol_status status);
+     *
+     *     int guac_client_init(guac_client* client, int argc, char** argv) {
+     *         client->ack_handler = ack_handler;
+     *     }
+     * @endcode
+     */
+    guac_client_ack_handler* ack_handler;
+
+    /**
+     * Handler for blob events sent by the Guacamole web-client.
+     *
+     * The handler takes a guac_stream which contains the stream index and
+     * will persist through the duration of the transfer, an arbitrary buffer
+     * containing the blob, and the length of the blob.
+     *
+     * Example:
+     * @code
+     *     int blob_handler(guac_client* client, guac_stream* stream,
+     *             void* data, int length);
+     *
+     *     int guac_client_init(guac_client* client, int argc, char** argv) {
+     *         client->blob_handler = blob_handler;
+     *     }
+     * @endcode
+     */
+    guac_client_blob_handler* blob_handler;
+
+    /**
+     * Handler for stream end events sent by the Guacamole web-client.
+     *
+     * The handler takes only a guac_stream which contains the stream index.
+     * This guac_stream will be disposed of immediately after this event is
+     * finished.
+     *
+     * Example:
+     * @code
+     *     int end_handler(guac_client* client, guac_stream* stream);
+     *
+     *     int guac_client_init(guac_client* client, int argc, char** argv) {
+     *         client->end_handler = end_handler;
+     *     }
+     * @endcode
+     */
+    guac_client_end_handler* end_handler;
+
+    /**
      * Handler for freeing data when the client is being unloaded.
      *
      * This handler will be called when the client needs to be unloaded
@@ -379,8 +352,8 @@ struct guac_client {
     guac_client_free_handler* free_handler;
 
     /**
-     * Handler for logging informational messages. This handler will be called
-     * via guac_client_log_info() when the client needs to log information.
+     * Logging handler. This handler will be called via guac_client_log() when
+     * the client needs to log messages of any type.
      *
      * In general, only programs loading the client should implement this
      * handler, as those are the programs that would provide the logging
@@ -391,7 +364,7 @@ struct guac_client {
      *
      * Example:
      * @code
-     *     void log_handler(guac_client* client, const char* format, va_list args);
+     *     void log_handler(guac_client* client, guac_client_log_level level, const char* format, va_list args);
      *
      *     void function_of_daemon() {
      *
@@ -400,32 +373,47 @@ struct guac_client {
      *     }
      * @endcode
      */
-    guac_client_log_handler* log_info_handler;
-
+    guac_client_log_handler* log_handler;
 
     /**
-     * Handler for logging error messages. This handler will be called
-     * via guac_client_log_error() when the client needs to log an error.
+     * Handler for get events sent by the Guacamole web-client.
      *
-     * In general, only programs loading the client should implement this
-     * handler, as those are the programs that would provide the logging
-     * facilities.
-     *
-     * Client implementations should expect these handlers to already be
-     * set.
+     * The handler takes a guac_object, containing the object index which will
+     * persist through the duration of the transfer, and the name of the stream
+     * being requested. It is up to the get handler to create the required body
+     * stream.
      *
      * Example:
      * @code
-     *     void log_handler(guac_client* client, const char* format, va_list args);
+     *     int get_handler(guac_client* client, guac_object* object,
+     *             char* name);
      *
-     *     void function_of_daemon() {
+     *     int guac_client_init(guac_client* client, int argc, char** argv) {
+     *         client->get_handler = get_handler;
+     *     }
+     * @endcode
+     */
+    guac_client_get_handler* get_handler;
+
+    /**
+     * Handler for put events sent by the Guacamole web-client.
      *
-     *         guac_client* client = [pass log_handler to guac_client_plugin_get_client()];
+     * The handler takes a guac_object and guac_stream, which each contain their
+     * respective indices which will persist through the duration of the
+     * transfer, the mimetype of the data being transferred, and the name of
+     * the stream within the object being written to.
+     *
+     * Example:
+     * @code
+     *     int put_handler(guac_client* client, guac_object* object,
+     *             guac_stream* stream, char* mimetype, char* name);
      *
+     *     int guac_client_init(guac_client* client, int argc, char** argv) {
+     *         client->put_handler = put_handler;
      *     }
      * @endcode
      */
-    guac_client_log_handler* log_error_handler;
+    guac_client_put_handler* put_handler;
 
     /**
      * Pool of buffer indices. Buffers are simply layers with negative indices.
@@ -446,6 +434,35 @@ struct guac_client {
      */
     guac_pool* __stream_pool;
 
+    /**
+     * All available output streams (data going to connected client).
+     */
+    guac_stream* __output_streams;
+
+    /**
+     * All available input streams (data coming from connected client).
+     */
+    guac_stream* __input_streams;
+
+    /**
+     * Pool of object indices.
+     */
+    guac_pool* __object_pool;
+
+    /**
+     * All available objects (arbitrary sets of named streams).
+     */
+    guac_object* __objects;
+
+    /**
+     * The unique identifier allocated for the connection, which may
+     * be used within the Guacamole protocol to refer to this connection.
+     * This identifier is guaranteed to be unique from all existing
+     * connections and will not collide with any available protocol
+     * names.
+     */
+    char* connection_id;
+
 };
 
 /**
@@ -469,70 +486,83 @@ void guac_client_free(guac_client* client);
  * initial handler lookup table defined in client-handlers.c. The intial
  * handlers will in turn call the client's handler (if defined).
  *
- * @param client The proxy client whose handlers should be called.
- * @param instruction The instruction to pass to the proxy client via the
- *                    appropriate handler.
+ * @param client
+ *     The proxy client whose handlers should be called.
+ *
+ * @param instruction
+ *     The instruction to pass to the proxy client via the appropriate handler.
+ *
+ * @return
+ *     Non-negative if the instruction was handled successfully, or negative
+ *     if an error occurred.
  */
-int guac_client_handle_instruction(guac_client* client, guac_instruction* instruction);
+int guac_client_handle_instruction(guac_client* client,
+        guac_instruction* instruction);
 
 /**
- * Logs an informational message in the log used by the given client. The
- * logger used will normally be defined by guacd (or whichever program loads
- * the proxy client) by setting the logging handlers of the client when it is
- * loaded.
+ * Writes a message in the log used by the given client. The logger used will
+ * normally be defined by guacd (or whichever program loads the proxy client)
+ * by setting the logging handlers of the client when it is loaded.
  *
- * @param client The proxy client to log an informational message for.
+ * @param client The proxy client logging this message.
+ * @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 guac_client_log_info(guac_client* client, const char* format, ...);
+void guac_client_log(guac_client* client, guac_client_log_level level,
+        const char* format, ...);
 
 /**
- * Logs an error message in the log used by the given client. The logger
- * used will normally be defined by guacd (or whichever program loads the
- * proxy client) by setting the logging handlers of the client when it is
- * loaded.
+ * Writes a message in the log used by the given client. The logger used will
+ * normally be defined by guacd (or whichever program loads the proxy client)
+ * by setting the logging handlers of the client when it is loaded.
  *
- * @param client The proxy client to log an error for.
+ * @param client The proxy client logging this message.
+ * @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.
+ * @param ap The va_list containing the arguments to be used when filling the
+ *           format string for printing.
  */
-void guac_client_log_error(guac_client* client, const char* format, ...);
+void vguac_client_log(guac_client* client, guac_client_log_level level,
+        const char* format, va_list ap);
 
 /**
- * Logs an informational message in the log used by the given client. The
- * logger used will normally be defined by guacd (or whichever program loads
- * the proxy client) by setting the logging handlers of the client when it is
- * loaded.
+ * Signals the given client to stop gracefully. This is a completely
+ * cooperative signal, and can be ignored by the client or the hosting
+ * daemon.
  *
- * @param client The proxy client to log an informational message for.
- * @param format A printf-style format string to log.
- * @param ap The va_list containing the arguments to be used when filling the
- *           format string for printing.
+ * @param client The proxy client to signal to stop.
  */
-void vguac_client_log_info(guac_client* client, const char* format, va_list ap);
+void guac_client_stop(guac_client* client);
 
 /**
- * Logs an error message in the log used by the given client. The logger
- * used will normally be defined by guacd (or whichever program loads the
- * proxy client) by setting the logging handlers of the client when it is
- * loaded.
+ * Signals the given client to stop gracefully, while also signalling via the
+ * Guacamole protocol that an error has occurred. Note that this is a completely
+ * cooperative signal, and can be ignored by the client or the hosting
+ * daemon. The message given will be logged to the system logs.
  *
- * @param client The proxy client to log an error for.
+ * @param client The proxy client to signal to stop.
+ * @param status The status to send over the Guacamole protocol.
  * @param format A printf-style format string to log.
- * @param ap The va_list containing the arguments to be used when filling the
- *           format string for printing.
+ * @param ... Arguments to use when filling the format string for printing.
  */
-void vguac_client_log_error(guac_client* client, const char* format, va_list ap);
+void guac_client_abort(guac_client* client, guac_protocol_status status,
+        const char* format, ...);
 
 /**
- * Signals the given client to stop gracefully. This is a completely
+ * Signals the given client to stop gracefully, while also signalling via the
+ * Guacamole protocol that an error has occurred. Note that this is a completely
  * cooperative signal, and can be ignored by the client or the hosting
- * daemon.
+ * daemon. The message given will be logged to the system logs.
  *
  * @param client The proxy client to signal to stop.
+ * @param status The status to send over the Guacamole protocol.
+ * @param format A printf-style format string to log.
+ * @param ap The va_list containing the arguments to be used when filling the
+ *           format string for printing.
  */
-void guac_client_stop(guac_client* client);
+void vguac_client_abort(guac_client* client, guac_protocol_status status,
+        const char* format, va_list ap);
 
 /**
  * Allocates a new buffer (invisible layer). An arbitrary index is
@@ -588,6 +618,157 @@ guac_stream* guac_client_alloc_stream(guac_client* client);
  */
 void guac_client_free_stream(guac_client* client, guac_stream* stream);
 
+/**
+ * Allocates a new object. An arbitrary index is automatically assigned
+ * if no previously-allocated object is available for use.
+ *
+ * @param client
+ *     The proxy client to allocate the object for.
+ *
+ * @return
+ *     The next available object, or a newly allocated object.
+ */
+guac_object* guac_client_alloc_object(guac_client* client);
+
+/**
+ * Returns the given object to the pool of available objects, such that it
+ * can be reused by any subsequent call to guac_client_alloc_object().
+ *
+ * @param client
+ *     The proxy client to return the object to.
+ *
+ * @param object
+ *     The object to return to the pool of available object.
+ */
+void guac_client_free_object(guac_client* client, guac_object* object);
+
+/**
+ * Streams the image data of the given surface over an image stream ("img"
+ * instruction) as PNG-encoded data. The image stream will be automatically
+ * allocated and freed.
+ *
+ * @param client
+ *     The Guacamole client from which the image stream should be allocated.
+ *
+ * @param socket
+ *     The socket over which instructions associated with the image stream
+ *     should be sent.
+ *
+ * @param mode
+ *     The composite mode to use when rendering the image over the given layer.
+ *
+ * @param layer
+ *     The destination layer.
+ *
+ * @param x
+ *     The X coordinate of the upper-left corner of the destination rectangle
+ *     within the given layer.
+ *
+ * @param y
+ *     The Y coordinate of the upper-left corner of the destination rectangle
+ *     within the given layer.
+ *
+ * @param surface
+ *     A Cairo surface containing the image data to be streamed.
+ */
+void guac_client_stream_png(guac_client* client, guac_socket* socket,
+        guac_composite_mode mode, const guac_layer* layer, int x, int y,
+        cairo_surface_t* surface);
+
+/**
+ * Streams the image data of the given surface over an image stream ("img"
+ * instruction) as JPEG-encoded data at the given quality. The image stream
+ * will be automatically allocated and freed.
+ *
+ * @param client
+ *     The Guacamole client from which the image stream should be allocated.
+ *
+ * @param socket
+ *     The socket over which instructions associated with the image stream
+ *     should be sent.
+ *
+ * @param mode
+ *     The composite mode to use when rendering the image over the given layer.
+ *
+ * @param layer
+ *     The destination layer.
+ *
+ * @param x
+ *     The X coordinate of the upper-left corner of the destination rectangle
+ *     within the given layer.
+ *
+ * @param y
+ *     The Y coordinate of the upper-left corner of the destination rectangle
+ *     within the given layer.
+ *
+ * @param surface
+ *     A Cairo surface containing the image data to be streamed.
+ *
+ * @param quality
+ *     The JPEG image quality, which must be an integer value between 0 and
+ *     100 inclusive.
+ */
+void guac_client_stream_jpeg(guac_client* client, guac_socket* socket,
+        guac_composite_mode mode, const guac_layer* layer, int x, int y,
+        cairo_surface_t* surface, int quality);
+
+/**
+ * Streams the image data of the given surface over an image stream ("img"
+ * instruction) as WebP-encoded data at the given quality. The image stream
+ * will be automatically allocated and freed. If the server does not support
+ * WebP, this function has no effect, so be sure to check the result of
+ * guac_client_supports_webp() prior to calling this function.
+ *
+ * @param client
+ *     The Guacamole client from which the image stream should be allocated.
+ *
+ * @param socket
+ *     The socket over which instructions associated with the image stream
+ *     should be sent.
+ *
+ * @param mode
+ *     The composite mode to use when rendering the image over the given layer.
+ *
+ * @param layer
+ *     The destination layer.
+ *
+ * @param x
+ *     The X coordinate of the upper-left corner of the destination rectangle
+ *     within the given layer.
+ *
+ * @param y
+ *     The Y coordinate of the upper-left corner of the destination rectangle
+ *     within the given layer.
+ *
+ * @param surface
+ *     A Cairo surface containing the image data to be streamed.
+ *
+ * @param quality
+ *     The WebP image quality, which must be an integer value between 0 and 100
+ *     inclusive. For lossy images, larger values indicate improving quality at
+ *     the expense of larger file size. For lossless images, this dictates the
+ *     quality of compression, with larger values producing smaller files at
+ *     the expense of speed.
+ *
+ * @param lossless
+ *     Zero to encode a lossy image, non-zero to encode losslessly.
+ */
+void guac_client_stream_webp(guac_client* client, guac_socket* socket,
+        guac_composite_mode mode, const guac_layer* layer, int x, int y,
+        cairo_surface_t* surface, int quality, int lossless);
+
+/**
+ * Returns whether the given client supports WebP. If the client does not
+ * support WebP, or the server cannot encode WebP images, zero is returned.
+ *
+ * @param client
+ *     The Guacamole client to check for WebP support.
+ *
+ * @return
+ *     Non-zero if the given client claims to support WebP and the server has
+ *     been built with WebP support, zero otherwise.
+ */
+int guac_client_supports_webp(guac_client* client);
 
 /**
  * The default Guacamole client layer, layer 0.
@@ -595,3 +776,4 @@ void guac_client_free_stream(guac_client* client, guac_stream* stream);
 extern const guac_layer* GUAC_DEFAULT_LAYER;
 
 #endif
+
diff --git a/src/libguac/guacamole/error-types.h b/src/libguac/guacamole/error-types.h
new file mode 100644
index 0000000..d7813b4
--- /dev/null
+++ b/src/libguac/guacamole/error-types.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _GUAC_ERROR_TYPES_H
+#define _GUAC_ERROR_TYPES_H
+
+/**
+ * Type definitions related to return values and errors.
+ *
+ * @file error-types.h
+ */
+
+/**
+ * Return codes shared by all Guacamole functions which can fail.
+ */
+typedef enum guac_status {
+
+    /**
+     * No errors occurred and the operation was successful.
+     */
+    GUAC_STATUS_SUCCESS = 0,
+
+    /**
+     * Insufficient memory to complete the operation.
+     */
+    GUAC_STATUS_NO_MEMORY,
+
+    /**
+     * The resource associated with the operation can no longer be used as it
+     * has reached the end of its normal lifecycle.
+     */
+    GUAC_STATUS_CLOSED,
+
+    /**
+     * Time ran out before the operation could complete.
+     */
+    GUAC_STATUS_TIMEOUT,
+
+    /**
+     * An error occurred, and further information about the error is already
+     * stored in errno.
+     */
+    GUAC_STATUS_SEE_ERRNO,
+
+    /**
+     * An I/O error prevented the operation from succeeding.
+     */
+    GUAC_STATUS_IO_ERROR,
+
+    /**
+     * The operation could not be performed because an invalid argument was
+     * given.
+     */
+    GUAC_STATUS_INVALID_ARGUMENT,
+
+    /**
+     * The operation failed due to a bug in the software or a serious system
+     * problem.
+     */
+    GUAC_STATUS_INTERNAL_ERROR,
+
+    /**
+     * Insufficient space remaining to complete the operation.
+     */
+    GUAC_STATUS_NO_SPACE,
+
+    /**
+     * The operation failed because the input provided is too large.
+     */
+    GUAC_STATUS_INPUT_TOO_LARGE,
+
+    /**
+     * The operation failed because the result could not be stored in the
+     * space provided.
+     */
+    GUAC_STATUS_RESULT_TOO_LARGE,
+
+    /**
+     * Permission was denied to perform the operation.
+     */
+    GUAC_STATUS_PERMISSION_DENIED,
+
+    /**
+     * The operation could not be performed because associated resources are
+     * busy.
+     */
+    GUAC_STATUS_BUSY,
+
+    /**
+     * The operation could not be performed because, while the associated
+     * resources do exist, they are not currently available for use.
+     */
+    GUAC_STATUS_NOT_AVAILABLE,
+
+    /**
+     * The requested operation is not supported.
+     */
+    GUAC_STATUS_NOT_SUPPORTED,
+
+    /**
+     * Support for the requested operation is not yet implemented.
+     */
+    GUAC_STATUS_NOT_INPLEMENTED,
+
+    /**
+     * The operation is temporarily unable to be performed, but may succeed if
+     * reattempted.
+     */
+    GUAC_STATUS_TRY_AGAIN,
+
+    /**
+     * A violation of the Guacamole protocol occurred.
+     */
+    GUAC_STATUS_PROTOCOL_ERROR,
+
+    /**
+     * The operation could not be performed because the requested resources do
+     * not exist.
+     */
+    GUAC_STATUS_NOT_FOUND,
+
+    /**
+     * The operation was canceled prior to completion.
+     */
+    GUAC_STATUS_CANCELED,
+
+    /**
+     * The operation could not be performed because input values are outside
+     * the allowed range.
+     */
+    GUAC_STATUS_OUT_OF_RANGE,
+
+    /**
+     * The operation could not be performed because access to an underlying
+     * resource is explicitly not allowed, though not necessarily due to
+     * permissions.
+     */
+    GUAC_STATUS_REFUSED,
+
+    /**
+     * The operation failed because too many resources are already in use.
+     */
+    GUAC_STATUS_TOO_MANY,
+
+    /**
+     * The operation was not performed because it would otherwise block.
+     */
+    GUAC_STATUS_WOULD_BLOCK
+
+} guac_status;
+
+#endif
+
diff --git a/src/libguac/guacamole/error.h b/src/libguac/guacamole/error.h
index 85eabf3..bad146d 100644
--- a/src/libguac/guacamole/error.h
+++ b/src/libguac/guacamole/error.h
@@ -1,39 +1,24 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
 
 #ifndef _GUAC_ERROR_H
@@ -46,58 +31,37 @@
  * @file error.h
  */
 
+#include "error-types.h"
+
 /**
- * Return codes shared by all Guacamole functions which can fail.
+ * Returns a human-readable explanation of the status code given.
  */
-typedef enum guac_status {
-
-    /**
-     * No errors occurred and the operation was successful.
-     */
-    GUAC_STATUS_SUCCESS = 0,
-
-    /**
-     * Insufficient memory to complete the operation.
-     */
-    GUAC_STATUS_NO_MEMORY,
-
-    /**
-     * The end of the input stream associated with the operation
-     * has been reached.
-     */
-    GUAC_STATUS_NO_INPUT,
-
-    /**
-     * A timeout occurred while reading from the input stream associated
-     * with the operation.
-     */
-    GUAC_STATUS_INPUT_TIMEOUT,
-
-    /**
-     * An error occurred, and further information about the error is already
-     * stored in errno.
-     */
-    GUAC_STATUS_SEE_ERRNO,
-
-    /**
-     * An error prevented the operation from writing to its associated
-     * output stream.
-     */
-    GUAC_STATUS_OUTPUT_ERROR,
-
-    /**
-     * The operation could not be performed because an invalid argument was
-     * given.
-     */
-    GUAC_STATUS_BAD_ARGUMENT,
-
-    /**
-     * The state of the associated system prevents an operation from being
-     * performed which would otherwise be allowed.
-     */
-    GUAC_STATUS_BAD_STATE
-
-} guac_status;
+const char* guac_status_string(guac_status status);
+
+/**
+ * Returns the status code associated with the error which occurred during the
+ * last function call. This value will only be set by functions documented to
+ * use it (most libguac functions), and is undefined if no error occurred.
+ *
+ * The storage of this value is thread-local. Assignment of a status code to
+ * guac_error in one thread will not affect its value in another thread.
+ */
+#define guac_error (*__guac_error())
+
+guac_status* __guac_error();
+
+/**
+ * Returns a message describing the error which occurred during the last
+ * function call. If an error occurred, but no message is associated with it,
+ * NULL is returned. This value is undefined if no error occurred.
+ *
+ * The storage of this value is thread-local. Assignment of a message to
+ * guac_error_message in one thread will not affect its value in another
+ * thread.
+ */
+#define guac_error_message (*__guac_error_message())
+
+const char** __guac_error_message();
 
 /**
  * Returns a human-readable explanation of the status code given.
@@ -130,3 +94,4 @@ guac_status* __guac_error();
 const char** __guac_error_message();
 
 #endif
+
diff --git a/src/libguac/guacamole/hash.h b/src/libguac/guacamole/hash.h
index d181e35..1505b36 100644
--- a/src/libguac/guacamole/hash.h
+++ b/src/libguac/guacamole/hash.h
@@ -1,45 +1,29 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
 
 #ifndef _GUAC_HASH_H
 #define _GUAC_HASH_H
 
-#include <cairo/cairo.h>
-
 /**
  * Provides functions and structures for producing likely-to-be-unique hash
  * values for images.
@@ -47,6 +31,8 @@
  * @file hash.h
  */
 
+#include <cairo/cairo.h>
+
 /**
  * Produces a 24-bit hash value from all pixels of the given surface. The
  * surface provided must be RGB or ARGB with each pixel stored in 32 bits.
diff --git a/src/libguac/guacamole/instruction-constants.h b/src/libguac/guacamole/instruction-constants.h
new file mode 100644
index 0000000..ac295dc
--- /dev/null
+++ b/src/libguac/guacamole/instruction-constants.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _GUAC_INSTRUCTION_CONSTANTS_H
+#define _GUAC_INSTRUCTION_CONSTANTS_H
+
+/**
+ * Constants related to Guacamole instructions.
+ *
+ * @file instruction-constants.h
+ */
+
+/**
+ * The maximum number of characters per instruction.
+ */
+#define GUAC_INSTRUCTION_MAX_LENGTH 8192
+
+/**
+ * The maximum number of digits to allow per length prefix.
+ */
+#define GUAC_INSTRUCTION_MAX_DIGITS 5
+
+/**
+ * The maximum number of elements per instruction, including the opcode.
+ */
+#define GUAC_INSTRUCTION_MAX_ELEMENTS 64
+
+#endif
+
diff --git a/src/libguac/guacamole/instruction-types.h b/src/libguac/guacamole/instruction-types.h
new file mode 100644
index 0000000..fd6cc69
--- /dev/null
+++ b/src/libguac/guacamole/instruction-types.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef _GUAC_INSTRUCTION_TYPES_H
+#define _GUAC_INSTRUCTION_TYPES_H
+
+/**
+ * Type definitions related to Guacamole instructions.
+ *
+ * @file instruction-types.h
+ */
+
+/**
+ * All possible states of the instruction parser.
+ */
+typedef enum guac_instruction_parse_state {
+
+    /**
+     * The parser is currently waiting for data to complete the length prefix
+     * of the current element of the instruction.
+     */
+    GUAC_INSTRUCTION_PARSE_LENGTH,
+
+    /**
+     * The parser has finished reading the length prefix and is currently
+     * waiting for data to complete the content of the instruction.
+     */
+    GUAC_INSTRUCTION_PARSE_CONTENT,
+
+    /**
+     * The instruction has been fully parsed.
+     */
+    GUAC_INSTRUCTION_PARSE_COMPLETE,
+
+    /**
+     * The instruction cannot be parsed because of a protocol error.
+     */
+    GUAC_INSTRUCTION_PARSE_ERROR
+
+} guac_instruction_parse_state;
+
+/**
+ * Represents a single instruction within the Guacamole protocol.
+ */
+typedef struct guac_instruction guac_instruction;
+
+#endif
+
diff --git a/src/libguac/guacamole/instruction.h b/src/libguac/guacamole/instruction.h
index 6eb2fd9..9444ad9 100644
--- a/src/libguac/guacamole/instruction.h
+++ b/src/libguac/guacamole/instruction.h
@@ -1,45 +1,29 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef _GUAC_INSTRUCTION_H
 #define _GUAC_INSTRUCTION_H
 
-#include "socket.h"
-
 /**
  * Provides functions and structures for reading, writing, and manipulating
  * Guacamole instructions.
@@ -47,10 +31,11 @@
  * @file instruction.h
  */
 
-/**
- * Represents a single instruction within the Guacamole protocol.
- */
-typedef struct guac_instruction {
+#include "instruction-types.h"
+#include "instruction-constants.h"
+#include "socket-types.h"
+
+struct guac_instruction {
 
     /**
      * The opcode of the instruction.
@@ -67,8 +52,61 @@ typedef struct guac_instruction {
      */
     char** argv;
 
-} guac_instruction;
+    /**
+     * The parse state of the instruction.
+     */
+    guac_instruction_parse_state state;
+
+    /**
+     * The length of the current element, if known.
+     */
+    int __element_length;
+
+    /**
+     * The number of elements currently parsed.
+     */
+    int __elementc;
+
+    /**
+     * All currently parsed elements.
+     */
+    char* __elementv[GUAC_INSTRUCTION_MAX_ELEMENTS];
 
+};
+
+/**
+ * Allocates a new instruction. Each instruction contains within itself the
+ * necessary facilities to parse instruction data.
+ *
+ * @return The newly allocated instruction, or NULL if an error occurs during
+ *         allocation, in which case guac_error will be set appropriately.
+ */
+guac_instruction* guac_instruction_alloc();
+
+/**
+ * Resets the parse state and contents of the given instruction, such that the
+ * memory of that instruction can be reused for another parse cycle.
+ *
+ * @param instruction The instruction to reset.
+ */
+void guac_instruction_reset(guac_instruction* instruction);
+
+/**
+ * Appends data from the given buffer to the given instruction. The data will
+ * be appended, if possible, to this instruction as a reference and thus the
+ * buffer must remain valid throughout the life of the instruction. This
+ * function may modify the contents of the buffer when those contents are
+ * part of an element within the instruction being read.
+ *
+ * @param instruction The instruction to append data to.
+ * @param buffer A buffer containing data that should be appended to this
+ *               instruction.
+ * @param length The number of bytes available for appending within the buffer.
+ * @return The number of bytes appended to this instruction, which may be
+ *         zero if more data is needed.
+ */
+int guac_instruction_append(guac_instruction* instruction,
+        void* buffer, int length);
 
 /**
  * Frees all memory allocated to the given instruction.
diff --git a/src/libguac/guacamole/layer-types.h b/src/libguac/guacamole/layer-types.h
new file mode 100644
index 0000000..2c91434
--- /dev/null
+++ b/src/libguac/guacamole/layer-types.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _GUAC_LAYER_TYPES_H
+#define _GUAC_LAYER_TYPES_H
+
+/**
+ * Type definitions related to Guacamole layers.
+ *
+ * @file layer-types.h
+ */
+
+/**
+ * Represents a single layer within the Guacamole protocol.
+ */
+typedef struct guac_layer guac_layer;
+
+#endif
+
diff --git a/src/libguac/guacamole/layer.h b/src/libguac/guacamole/layer.h
index 956a84e..b6fd8db 100644
--- a/src/libguac/guacamole/layer.h
+++ b/src/libguac/guacamole/layer.h
@@ -1,39 +1,24 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
 #ifndef _GUAC_LAYER_H
 #define _GUAC_LAYER_H
@@ -44,7 +29,7 @@
  * @file layer.h
  */
 
-typedef struct guac_layer guac_layer;
+#include "layer-types.h"
 
 /**
  * Represents a single layer within the Guacamole protocol.
diff --git a/src/libguac/guacamole/object-types.h b/src/libguac/guacamole/object-types.h
new file mode 100644
index 0000000..62639e6
--- /dev/null
+++ b/src/libguac/guacamole/object-types.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GUAC_OBJECT_TYPES_H
+#define GUAC_OBJECT_TYPES_H
+
+/**
+ * Type definitions related to Guacamole protocol objects.
+ *
+ * @file object-types.h
+ */
+
+/**
+ * Represents a single object within the Guacamole protocol.
+ */
+typedef struct guac_object guac_object;
+
+#endif
+
diff --git a/src/libguac/guacamole/object.h b/src/libguac/guacamole/object.h
new file mode 100644
index 0000000..01be132
--- /dev/null
+++ b/src/libguac/guacamole/object.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GUAC_OBJECT_H
+#define GUAC_OBJECT_H
+
+/**
+ * Provides functions and structures required for allocating and using objects.
+ *
+ * @file object.h
+ */
+
+#include "client-fntypes.h"
+#include "object-types.h"
+
+struct guac_object {
+
+    /**
+     * The index of this object.
+     */
+    int index;
+
+    /**
+     * Arbitrary data associated with this object.
+     */
+    void* data;
+
+    /**
+     * Handler for get events sent by the Guacamole web-client.
+     *
+     * The handler takes a guac_object, containing the object index which will
+     * persist through the duration of the transfer, and the name of the stream
+     * being requested. It is up to the get handler to create the required body
+     * stream.
+     *
+     * Example:
+     * @code
+     *     int get_handler(guac_client* client, guac_object* object,
+     *             char* name);
+     *
+     *     int some_function(guac_client* client) {
+     *
+     *         guac_object* object = guac_client_alloc_object(client);
+     *         object->get_handler = get_handler;
+     *
+     *     }
+     * @endcode
+     */
+    guac_client_get_handler* get_handler;
+
+    /**
+     * Handler for put events sent by the Guacamole web-client.
+     *
+     * The handler takes a guac_object and guac_stream, which each contain their
+     * respective indices which will persist through the duration of the
+     * transfer, the mimetype of the data being transferred, and the name of
+     * the stream within the object being written to.
+     *
+     * Example:
+     * @code
+     *     int put_handler(guac_client* client, guac_object* object,
+     *             guac_stream* stream, char* mimetype, char* name);
+     *
+     *     int some_function(guac_client* client) {
+     *
+     *         guac_object* object = guac_client_alloc_object(client);
+     *         object->put_handler = put_handler;
+     *
+     *     }
+     * @endcode
+     */
+    guac_client_put_handler* put_handler;
+
+};
+
+#endif
+
diff --git a/src/libguac/guacamole/plugin-constants.h b/src/libguac/guacamole/plugin-constants.h
new file mode 100644
index 0000000..65cee99
--- /dev/null
+++ b/src/libguac/guacamole/plugin-constants.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _GUAC_PLUGIN_CONSTANTS_H
+#define _GUAC_PLUGIN_CONSTANTS_H
+
+/**
+ * Constants related to client plugins.
+ *
+ * @file plugin-constants.h
+ */
+
+/**
+ * String prefix which begins the library filename of all client plugins.
+ */
+#define GUAC_PROTOCOL_LIBRARY_PREFIX "libguac-client-"
+
+/**
+ * String suffix which ends the library filename of all client plugins.
+ */
+#define GUAC_PROTOCOL_LIBRARY_SUFFIX ".so"
+
+/**
+ * The maximum number of characters (COUNTING NULL TERMINATOR) to allow
+ * for protocol names within the library filename of client plugins.
+ */
+#define GUAC_PROTOCOL_NAME_LIMIT 256
+
+/**
+ * The maximum number of characters (INCLUDING NULL TERMINATOR) that a
+ * character array containing the concatenation of the library prefix,
+ * protocol name, and suffix can contain, assuming the protocol name is
+ * limited to GUAC_PROTOCOL_NAME_LIMIT characters.
+ */
+#define GUAC_PROTOCOL_LIBRARY_LIMIT (                                  \
+                                                                       \
+      sizeof(GUAC_PROTOCOL_LIBRARY_PREFIX) - 1 /* "libguac-client-" */ \
+    +        GUAC_PROTOCOL_NAME_LIMIT      - 1 /* [up to 256 chars] */ \
+    + sizeof(GUAC_PROTOCOL_LIBRARY_SUFFIX) - 1 /* ".so"             */ \
+    + 1                                        /* NULL terminator   */ \
+                                                                       \
+)
+
+#endif
+
diff --git a/src/libguac/guacamole/plugin-types.h b/src/libguac/guacamole/plugin-types.h
new file mode 100644
index 0000000..69c9cc0
--- /dev/null
+++ b/src/libguac/guacamole/plugin-types.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _GUAC_PLUGIN_TYPES_H
+#define _GUAC_PLUGIN_TYPES_H
+
+/**
+ * Type definitions related to client plugins.
+ *
+ * @file plugin-types.h
+ */
+
+/**
+ * A handle to a client plugin, containing enough information about the
+ * plugin to complete the initial protocol handshake and instantiate a new
+ * client supporting the protocol provided by the client plugin. 
+ */
+typedef struct guac_client_plugin guac_client_plugin;
+
+#endif
+
diff --git a/src/libguac/guacamole/plugin.h b/src/libguac/guacamole/plugin.h
index f139885..52a7ba7 100644
--- a/src/libguac/guacamole/plugin.h
+++ b/src/libguac/guacamole/plugin.h
@@ -1,45 +1,31 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Original Code is libguac.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
 #ifndef _GUAC_PLUGIN_H
 #define _GUAC_PLUGIN_H
 
-#include "client.h"
+#include "client-types.h"
+#include "plugin-constants.h"
+#include "plugin-types.h"
 
 /**
  * Provides functions and structures required for handling a client plugin.
@@ -47,44 +33,6 @@
  * @file plugin.h
  */
 
-/**
- * String prefix which begins the library filename of all client plugins.
- */
-#define GUAC_PROTOCOL_LIBRARY_PREFIX "libguac-client-"
-
-/**
- * String suffix which ends the library filename of all client plugins.
- */
-#define GUAC_PROTOCOL_LIBRARY_SUFFIX ".so"
-
-/**
- * The maximum number of characters (COUNTING NULL TERMINATOR) to allow
- * for protocol names within the library filename of client plugins.
- */
-#define GUAC_PROTOCOL_NAME_LIMIT 256
-
-/**
- * The maximum number of characters (INCLUDING NULL TERMINATOR) that a
- * character array containing the concatenation of the library prefix,
- * protocol name, and suffix can contain, assuming the protocol name is
- * limited to GUAC_PROTOCOL_NAME_LIMIT characters.
- */
-#define GUAC_PROTOCOL_LIBRARY_LIMIT (                                  \
-                                                                       \
-      sizeof(GUAC_PROTOCOL_LIBRARY_PREFIX) - 1 /* "libguac-client-" */ \
-    +        GUAC_PROTOCOL_NAME_LIMIT      - 1 /* [up to 256 chars] */ \
-    + sizeof(GUAC_PROTOCOL_LIBRARY_SUFFIX) - 1 /* ".so"             */ \
-    + 1                                        /* NULL terminator   */ \
-                                                                       \
-)
-
-typedef struct guac_client_plugin guac_client_plugin;
-
-/**
- * A handle to a client plugin, containing enough information about the
- * plugin to complete the initial protocol handshake and instantiate a new
- * client supporting the protocol provided by the client plugin. 
- */
 struct guac_client_plugin {
 
     /**
diff --git a/src/libguac/guacamole/pool-types.h b/src/libguac/guacamole/pool-types.h
new file mode 100644
index 0000000..2688f2e
--- /dev/null
+++ b/src/libguac/guacamole/pool-types.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _GUAC_POOL_TYPES_H
+#define _GUAC_POOL_TYPES_H
+
+/**
+ * Type definitions related to the guac_pool pool of unique integers.
+ *
+ * @file pool-types.h
+ */
+
+/**
+ * Represents a single integer within a larger pool of integers.
+ */
+typedef struct guac_pool_int guac_pool_int;
+
+/**
+ * A pool of integers. Integers can be removed from and later free'd back
+ * into the pool. New integers are returned when the pool is exhausted,
+ * or when the pool has not met some minimum size. Old, free'd integers
+ * are returned otherwise.
+ */
+typedef struct guac_pool guac_pool;
+
+#endif
+
diff --git a/src/libguac/guacamole/pool.h b/src/libguac/guacamole/pool.h
index 629d863..5ab99ea 100644
--- a/src/libguac/guacamole/pool.h
+++ b/src/libguac/guacamole/pool.h
@@ -1,39 +1,24 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
 #ifndef _GUAC_POOL_H
 #define _GUAC_POOL_H
@@ -45,15 +30,9 @@
  * @file pool.h
  */
 
-typedef struct guac_pool_int guac_pool_int;
+#include "pool-types.h"
 
-/**
- * A pool of integers. Integers can be removed from and later free'd back
- * into the pool. New integers are returned when the pool is exhausted,
- * or when the pool has not met some minimum size. Old, free'd integers
- * are returned otherwise.
- */
-typedef struct guac_pool {
+struct guac_pool {
 
     /**
      * The minimum number of integers which must have been returned by
@@ -63,6 +42,11 @@ typedef struct guac_pool {
     int min_size;
 
     /**
+     * The number of integers currently in use.
+     */
+    int active;
+
+    /**
      * The next integer to be released (after no more integers remain in the
      * pool.
      */
@@ -78,11 +62,8 @@ typedef struct guac_pool {
      */
     guac_pool_int* __tail;
 
-} guac_pool;
+};
 
-/**
- * Represents a single integer within a larger pool of integers.
- */
 struct guac_pool_int {
 
     /**
@@ -102,8 +83,8 @@ struct guac_pool_int {
  * Allocates a new guac_pool having the given minimum size.
  *
  * @param size The minimum number of integers which must have been returned by
- *             guac_pool_next_int before freed integers (previously used integers)
- *             are allowed to be returned.
+ *             guac_pool_next_int before freed integers (previously used
+ *             integers) are allowed to be returned.
  * @return A new, empty guac_pool, having the given minimum size.
  */
 guac_pool* guac_pool_alloc(int size);
@@ -116,23 +97,23 @@ guac_pool* guac_pool_alloc(int size);
 void guac_pool_free(guac_pool* pool);
 
 /**
- * Returns the next available integer from the given guac_pool. All integers returned are
- * non-negative, and are returned in sequences, starting from 0.
+ * Returns the next available integer from the given guac_pool. All integers
+ * returned are non-negative, and are returned in sequences, starting from 0.
  *
  * @param pool The guac_pool to retrieve an integer from.
- * @return The next available integer, which may be either an integer not yet returned
- *         by a call to guac_pool_next_int, or an integer which was previosly returned,
- *         but has since been freed.
+ * @return The next available integer, which may be either an integer not yet
+ *         returned by a call to guac_pool_next_int, or an integer which was
+ *         previosly returned, but has since been freed.
  */
 int guac_pool_next_int(guac_pool* pool);
 
 /**
- * Frees the given integer back into the given guac_pool. The integer given will be
- * available for future calls to guac_pool_next_int.
+ * Frees the given integer back into the given guac_pool. The integer given
+ * will be available for future calls to guac_pool_next_int.
  *
  * @param pool The guac_pool to free the given integer into.
- * @param value The integer which should be readded to the given pool, such that it can
- *              be received by a future call to guac_pool_next_int.
+ * @param value The integer which should be returned to the given pool, such
+ *              that it can be received by a future call to guac_pool_next_int.
  */
 void guac_pool_free_int(guac_pool* pool, int value);
 
diff --git a/src/libguac/guacamole/protocol-types.h b/src/libguac/guacamole/protocol-types.h
new file mode 100644
index 0000000..4002c5d
--- /dev/null
+++ b/src/libguac/guacamole/protocol-types.h
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _GUAC_PROTOCOL_TYPES_H
+#define _GUAC_PROTOCOL_TYPES_H
+
+/**
+ * Type definitions related to the Guacamole protocol.
+ *
+ * @file protocol-types.h
+ */
+
+/**
+ * Set of all possible status codes returned by protocol operations. These
+ * codes relate to Guacamole server/client communication, and not to internal
+ * communication of errors within libguac and linked software.
+ *
+ * In general:
+ *
+ *     0x0000 - 0x00FF: Successful operations.
+ *     0x0100 - 0x01FF: Operations that failed due to implementation status.
+ *     0x0200 - 0x02FF: Operations that failed due to environmental.
+ *     0x0300 - 0x03FF: Operations that failed due to user action.
+ *
+ * There is a general correspondence of these status codes with HTTP response
+ * codes.
+ */
+typedef enum guac_protocol_status {
+
+    /**
+     * The operation succeeded.
+     */
+    GUAC_PROTOCOL_STATUS_SUCCESS = 0x0000,
+
+    /**
+     * The requested operation is unsupported.
+     */
+    GUAC_PROTOCOL_STATUS_UNSUPPORTED = 0x0100,
+
+    /**
+     * The operation could not be performed due to an internal failure.
+     */
+    GUAC_PROTOCOL_STATUS_SERVER_ERROR = 0x0200,
+
+    /**
+     * The operation could not be performed due as the server is busy.
+     */
+    GUAC_PROTOCOL_STATUS_SERVER_BUSY = 0x0201,
+
+    /**
+     * The operation could not be performed because the upstream server
+     * is not responding.
+     */
+    GUAC_PROTOCOL_STATUS_UPSTREAM_TIMEOUT = 0x202,
+
+    /**
+     * The operation was unsuccessful due to an error or otherwise
+     * unexpected condition of the upstream server.
+     */
+    GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR = 0x203,
+
+    /**
+     * The operation could not be performed as the requested resource
+     * does not exist.
+     */
+    GUAC_PROTOCOL_STATUS_RESOURCE_NOT_FOUND = 0x204,
+
+    /**
+     * The operation could not be performed as the requested resource is
+     * already in use.
+     */
+    GUAC_PROTOCOL_STATUS_RESOURCE_CONFLICT = 0x205,
+
+    /**
+     * The operation could not be performed because bad parameters were
+     * given.
+     */
+    GUAC_PROTOCOL_STATUS_CLIENT_BAD_REQUEST = 0x300,
+
+    /**
+     * Permission was denied to perform the operation, as the user is not
+     * yet authorized (not yet logged in, for example).
+     */
+    GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED = 0x0301,
+
+    /**
+     * Permission was denied to perform the operation, and this permission
+     * will not be granted even if the user is authorized.
+     */
+    GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN = 0x0303,
+
+    /**
+     * The client took too long to respond.
+     */
+    GUAC_PROTOCOL_STATUS_CLIENT_TIMEOUT = 0x308,
+
+    /**
+     * The client sent too much data.
+     */
+    GUAC_PROTOCOL_STATUS_CLIENT_OVERRUN = 0x30D,
+
+    /**
+     * The client sent data of an unsupported or unexpected type.
+     */
+    GUAC_PROTOCOL_STATUS_CLIENT_BAD_TYPE = 0x30F,
+
+    /**
+     * The operation failed because the current client is already
+     * using too many resources.
+     */
+    GUAC_PROTOCOL_STATUS_CLIENT_TOO_MANY = 0x31D
+
+} guac_protocol_status;
+
+/**
+ * Composite modes used by Guacamole draw instructions. Each
+ * composite mode maps to a unique channel mask integer.
+ */
+typedef enum guac_composite_mode {
+
+    /*
+     * A: Source where destination transparent = S n D'
+     * B: Source where destination opaque      = S n D
+     * C: Destination where source transparent = D n S'
+     * D: Destination where source opaque      = D n S
+     *
+     * 0 = Active, 1 = Inactive
+     */
+                           /* ABCD */
+    GUAC_COMP_ROUT  = 0x2, /* 0010 - Clears destination where source opaque  */
+    GUAC_COMP_ATOP  = 0x6, /* 0110 - Fill where destination opaque only      */
+    GUAC_COMP_XOR   = 0xA, /* 1010 - XOR                                     */
+    GUAC_COMP_ROVER = 0xB, /* 1011 - Fill where destination transparent only */
+    GUAC_COMP_OVER  = 0xE, /* 1110 - Draw normally                           */
+    GUAC_COMP_PLUS  = 0xF, /* 1111 - Add                                     */
+
+    /* Unimplemented in client: */
+    /* NOT IMPLEMENTED:       0000 - Clear          */
+    /* NOT IMPLEMENTED:       0011 - No operation   */
+    /* NOT IMPLEMENTED:       0101 - Additive IN    */
+    /* NOT IMPLEMENTED:       0111 - Additive ATOP  */
+    /* NOT IMPLEMENTED:       1101 - Additive RATOP */
+
+    /* Buggy in webkit browsers, as they keep channel C on in all cases: */
+    GUAC_COMP_RIN   = 0x1, /* 0001 */
+    GUAC_COMP_IN    = 0x4, /* 0100 */
+    GUAC_COMP_OUT   = 0x8, /* 1000 */
+    GUAC_COMP_RATOP = 0x9, /* 1001 */
+    GUAC_COMP_SRC   = 0xC  /* 1100 */
+
+    /* Bitwise composite operations (binary) */
+
+    /*
+     * A: S' & D'
+     * B: S' & D
+     * C: S  & D'
+     * D: S  & D
+     *
+     * 0 = Active, 1 = Inactive
+     */
+
+} guac_composite_mode;
+
+/**
+ * Default transfer functions. There is no current facility in the
+ * Guacamole protocol to define custom transfer functions.
+ */
+typedef enum guac_transfer_function {
+
+    /* Constant functions */               /* ABCD */
+    GUAC_TRANSFER_BINARY_BLACK      = 0x0, /* 0000 */
+    GUAC_TRANSFER_BINARY_WHITE      = 0xF, /* 1111 */
+
+    /* Copy functions */
+    GUAC_TRANSFER_BINARY_SRC        = 0x3, /* 0011 */
+    GUAC_TRANSFER_BINARY_DEST       = 0x5, /* 0101 */
+    GUAC_TRANSFER_BINARY_NSRC       = 0xC, /* 1100 */
+    GUAC_TRANSFER_BINARY_NDEST      = 0xA, /* 1010 */
+
+    /* AND / NAND */
+    GUAC_TRANSFER_BINARY_AND        = 0x1, /* 0001 */
+    GUAC_TRANSFER_BINARY_NAND       = 0xE, /* 1110 */
+
+    /* OR / NOR */
+    GUAC_TRANSFER_BINARY_OR         = 0x7, /* 0111 */
+    GUAC_TRANSFER_BINARY_NOR        = 0x8, /* 1000 */
+
+    /* XOR / XNOR */
+    GUAC_TRANSFER_BINARY_XOR        = 0x6, /* 0110 */
+    GUAC_TRANSFER_BINARY_XNOR       = 0x9, /* 1001 */
+
+    /* AND / NAND with inverted source */
+    GUAC_TRANSFER_BINARY_NSRC_AND   = 0x4, /* 0100 */
+    GUAC_TRANSFER_BINARY_NSRC_NAND  = 0xB, /* 1011 */
+
+    /* OR / NOR with inverted source */
+    GUAC_TRANSFER_BINARY_NSRC_OR    = 0xD, /* 1101 */
+    GUAC_TRANSFER_BINARY_NSRC_NOR   = 0x2, /* 0010 */
+
+    /* AND / NAND with inverted destination */
+    GUAC_TRANSFER_BINARY_NDEST_AND  = 0x2, /* 0010 */
+    GUAC_TRANSFER_BINARY_NDEST_NAND = 0xD, /* 1101 */
+
+    /* OR / NOR with inverted destination */
+    GUAC_TRANSFER_BINARY_NDEST_OR   = 0xB, /* 1011 */
+    GUAC_TRANSFER_BINARY_NDEST_NOR  = 0x4  /* 0100 */
+
+} guac_transfer_function;
+
+/**
+ * Supported line cap styles
+ */
+typedef enum guac_line_cap_style {
+    GUAC_LINE_CAP_BUTT   = 0x0,
+    GUAC_LINE_CAP_ROUND  = 0x1,
+    GUAC_LINE_CAP_SQUARE = 0x2
+} guac_line_cap_style;
+
+/**
+ * Supported line join styles
+ */
+typedef enum guac_line_join_style {
+    GUAC_LINE_JOIN_BEVEL = 0x0,
+    GUAC_LINE_JOIN_MITER = 0x1,
+    GUAC_LINE_JOIN_ROUND = 0x2
+} guac_line_join_style;
+
+#endif
+
diff --git a/src/libguac/guacamole/protocol.h b/src/libguac/guacamole/protocol.h
index e6c7ad1..77d69e4 100644
--- a/src/libguac/guacamole/protocol.h
+++ b/src/libguac/guacamole/protocol.h
@@ -1,49 +1,28 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Original Code is libguac.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
 #ifndef _GUAC_PROTOCOL_H
 #define _GUAC_PROTOCOL_H
 
-#include <cairo/cairo.h>
-
-#include "layer.h"
-#include "socket.h"
-#include "timestamp.h"
-
 /**
  * Provides functions and structures required for communicating using the
  * Guacamole protocol over a guac_socket connection, such as that provided by
@@ -52,121 +31,34 @@
  * @file protocol.h
  */
 
-/**
- * Composite modes used by Guacamole draw instructions. Each
- * composite mode maps to a unique channel mask integer.
- */
-typedef enum guac_composite_mode {
-
-    /*
-     * A: Source where destination transparent = S n D'
-     * B: Source where destination opaque      = S n D
-     * C: Destination where source transparent = D n S'
-     * D: Destination where source opaque      = D n S
-     *
-     * 0 = Active, 1 = Inactive
-     */
-                           /* ABCD */
-    GUAC_COMP_ROUT  = 0x2, /* 0010 - Clears destination where source opaque  */
-    GUAC_COMP_ATOP  = 0x6, /* 0110 - Fill where destination opaque only      */
-    GUAC_COMP_XOR   = 0xA, /* 1010 - XOR                                     */
-    GUAC_COMP_ROVER = 0xB, /* 1011 - Fill where destination transparent only */
-    GUAC_COMP_OVER  = 0xE, /* 1110 - Draw normally                           */
-    GUAC_COMP_PLUS  = 0xF, /* 1111 - Add                                     */
-
-    /* Unimplemented in client: */
-    /* NOT IMPLEMENTED:       0000 - Clear          */
-    /* NOT IMPLEMENTED:       0011 - No operation   */
-    /* NOT IMPLEMENTED:       0101 - Additive IN    */
-    /* NOT IMPLEMENTED:       0111 - Additive ATOP  */
-    /* NOT IMPLEMENTED:       1101 - Additive RATOP */
-
-    /* Buggy in webkit browsers, as they keep channel C on in all cases: */
-    GUAC_COMP_RIN   = 0x1, /* 0001 */
-    GUAC_COMP_IN    = 0x4, /* 0100 */
-    GUAC_COMP_OUT   = 0x8, /* 1000 */
-    GUAC_COMP_RATOP = 0x9, /* 1001 */
-    GUAC_COMP_SRC   = 0xC  /* 1100 */
-
-    /* Bitwise composite operations (binary) */
-
-    /*
-     * A: S' & D'
-     * B: S' & D
-     * C: S  & D'
-     * D: S  & D
-     *
-     * 0 = Active, 1 = Inactive
-     */
-
-} guac_composite_mode;
-
-
-/**
- * Default transfer functions. There is no current facility in the
- * Guacamole protocol to define custom transfer functions.
- */
-typedef enum guac_transfer_function {
+#include "layer-types.h"
+#include "object-types.h"
+#include "protocol-types.h"
+#include "socket-types.h"
+#include "stream-types.h"
+#include "timestamp-types.h"
 
-    /* Constant functions */               /* ABCD */
-    GUAC_TRANSFER_BINARY_BLACK      = 0x0, /* 0000 */
-    GUAC_TRANSFER_BINARY_WHITE      = 0xF, /* 1111 */
-
-    /* Copy functions */
-    GUAC_TRANSFER_BINARY_SRC        = 0x3, /* 0011 */
-    GUAC_TRANSFER_BINARY_DEST       = 0x5, /* 0101 */
-    GUAC_TRANSFER_BINARY_NSRC       = 0xC, /* 1100 */
-    GUAC_TRANSFER_BINARY_NDEST      = 0xA, /* 1010 */
-
-    /* AND / NAND */
-    GUAC_TRANSFER_BINARY_AND        = 0x1, /* 0001 */
-    GUAC_TRANSFER_BINARY_NAND       = 0xE, /* 1110 */
-
-    /* OR / NOR */
-    GUAC_TRANSFER_BINARY_OR         = 0x7, /* 0111 */
-    GUAC_TRANSFER_BINARY_NOR        = 0x8, /* 1000 */
-
-    /* XOR / XNOR */
-    GUAC_TRANSFER_BINARY_XOR        = 0x6, /* 0110 */
-    GUAC_TRANSFER_BINARY_XNOR       = 0x9, /* 1001 */
-
-    /* AND / NAND with inverted source */
-    GUAC_TRANSFER_BINARY_NSRC_AND   = 0x4, /* 0100 */
-    GUAC_TRANSFER_BINARY_NSRC_NAND  = 0xB, /* 1011 */
-
-    /* OR / NOR with inverted source */
-    GUAC_TRANSFER_BINARY_NSRC_OR    = 0xD, /* 1101 */
-    GUAC_TRANSFER_BINARY_NSRC_NOR   = 0x2, /* 0010 */
-
-    /* AND / NAND with inverted destination */
-    GUAC_TRANSFER_BINARY_NDEST_AND  = 0x2, /* 0010 */
-    GUAC_TRANSFER_BINARY_NDEST_NAND = 0xD, /* 1101 */
-
-    /* OR / NOR with inverted destination */
-    GUAC_TRANSFER_BINARY_NDEST_OR   = 0xB, /* 1011 */
-    GUAC_TRANSFER_BINARY_NDEST_NOR  = 0x4  /* 0100 */
-
-} guac_transfer_function;
+#include <cairo/cairo.h>
+#include <stdarg.h>
 
-/**
- * Supported line cap styles
- */
-typedef enum guac_line_cap_style {
-    GUAC_LINE_CAP_BUTT   = 0x0,
-    GUAC_LINE_CAP_ROUND  = 0x1,
-    GUAC_LINE_CAP_SQUARE = 0x2
-} guac_line_cap_style;
+/* CONTROL INSTRUCTIONS */
 
 /**
- * Supported line join styles
+ * Sends an ack instruction over the given guac_socket connection.
+ *
+ * If an error occurs sending the instruction, a non-zero value is
+ * returned, and guac_error is set appropriately.
+ *
+ * @param socket The guac_socket connection to use.
+ * @param stream The guac_stream associated with the operation this ack is
+ *               acknowledging.
+ * @param error The human-readable description associated with the error or
+ *              status update.
+ * @param status The status code related to the error or status.
+ * @return Zero on success, non-zero on error.
  */
-typedef enum guac_line_join_style {
-    GUAC_LINE_JOIN_BEVEL = 0x0,
-    GUAC_LINE_JOIN_MITER = 0x1,
-    GUAC_LINE_JOIN_ROUND = 0x2
-} guac_line_join_style;
-
-/* CONTROL INSTRUCTIONS */
+int guac_protocol_send_ack(guac_socket* socket, guac_stream* stream,
+        const char* error, guac_protocol_status status);
 
 /**
  * Sends an args instruction over the given guac_socket connection.
@@ -210,10 +102,50 @@ int guac_protocol_send_disconnect(guac_socket* socket);
  * returned, and guac_error is set appropriately.
  *
  * @param socket The guac_socket connection to use.
- * @param error The description associated with the error.
+ * @param error The human-readable description associated with the error.
+ * @param status The status code related to the error.
  * @return Zero on success, non-zero on error.
  */
-int guac_protocol_send_error(guac_socket* socket, const char* error);
+int guac_protocol_send_error(guac_socket* socket, const char* error,
+        guac_protocol_status status);
+
+/**
+ * Sends a log instruction over the given guac_socket connection. This is
+ * mainly useful in debugging.
+ *
+ * If an error occurs sending the instruction, a non-zero value is
+ * returned, and guac_error is set appropriately.
+ *
+ * @param socket The guac_socket connection to use.
+ * @param format A printf-style format string to log.
+ * @param ... Arguments to use when filling the format string for printing.
+ * @return Zero on success, non-zero on error.
+ */
+int guac_protocol_send_log(guac_socket* socket, const char* format, ...);
+
+/**
+ * Sends a log instruction over the given guac_socket connection. This is
+ * mainly useful in debugging.
+ *
+ * If an error occurs sending the instruction, a non-zero value is
+ * returned, and guac_error is set appropriately.
+ *
+ * @param socket
+ *     The guac_socket connection to use.
+ *
+ * @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.
+ *
+ * @return
+ *     Zero if the instruction was sent successfully, non-zero if an error
+ *     occurs.
+ */
+int vguac_protocol_send_log(guac_socket* socket, const char* format,
+        va_list args);
 
 /**
  * Sends a nest instruction over the given guac_socket connection.
@@ -232,6 +164,30 @@ int guac_protocol_send_nest(guac_socket* socket, int index,
         const char* data);
 
 /**
+ * Sends a nop instruction (null-operation) over the given guac_socket
+ * connection.
+ *
+ * If an error occurs sending the instruction, a non-zero value is
+ * returned, and guac_error is set appropriately.
+ *
+ * @param socket The guac_socket connection to use.
+ * @return Zero on success, non-zero on error.
+ */
+int guac_protocol_send_nop(guac_socket* socket);
+
+/**
+ * Sends a ready instruction over the given guac_socket connection.
+ *
+ * If an error occurs sending the instruction, a non-zero value is
+ * returned, and guac_error is set appropriately.
+ *
+ * @param socket The guac_socket connection to use.
+ * @param id The connection ID of the connection that is ready.
+ * @return Zero on success, non-zero on error.
+ */
+int guac_protocol_send_ready(guac_socket* socket, const char* id);
+
+/**
  * Sends a set instruction over the given guac_socket connection.
  *
  * If an error occurs sending the instruction, a non-zero value is
@@ -271,210 +227,180 @@ int guac_protocol_send_select(guac_socket* socket, const char* protocol);
  */
 int guac_protocol_send_sync(guac_socket* socket, guac_timestamp timestamp);
 
-/* MEDIA INSTRUCTIONS */
+/* OBJECT INSTRUCTIONS */
 
 /**
- * Sends an audio instruction over the given guac_socket connection.
+ * Sends a body instruction over the given guac_socket connection.
  *
  * If an error occurs sending the instruction, a non-zero value is
  * returned, and guac_error is set appropriately.
  *
- * @param socket The guac_socket connection to use.
- * @param channel The index of the audio channel the sound should play on.
- * @param mimetype The mimetype of the data being sent.
- * @param duration The duration of the sound being sent, in milliseconds.
- * @param data The audio data to be sent.
- * @param size The number of bytes of audio data to send.
- * @return Zero on success, non-zero on error.
- */
-int guac_protocol_send_audio(guac_socket* socket, int channel,
-        const char* mimetype, double duration, void* data, int size);
-
-/**
- * Begins a audio instruction over the given guac_socket connection. Only the
- * initial non-data part of the instruction and the length of the data part
- * of the instruction are sent. The actual contents of the data must be
- * sent with guac_protocol_send_audio_data(), and the instruction must be
- * completed with guac_protocol_send_audio_end().
+ * @param socket
+ *     The guac_socket connection to use.
  *
- * Note that the size of the audio to be sent MUST be known ahead of time,
- * even though the data of the audio may be sent in chunks.
+ * @param object
+ *     The object to associated with the stream being used.
  *
- * No further instruction data may be sent along the givven guac_socket
- * except via guac_protocol_send_audio_data() until the audio instruction
- * is completed with guac_protocol_send_audio_end().
+ * @param stream
+ *     The stream to use.
  *
- * Note that if you send this instruction over a threadsafe socket, you
- * MUST also call guac_protocol_send_audio_end() or the socket will be
- * left in an unsafe state.
+ * @param mimetype
+ *     The mimetype of the data being sent.
  *
- * If an error occurs sending the instruction, a non-zero value is
- * returned, and guac_error is set appropriately.
+ * @param name
+ *     The name of the stream whose body is being sent, as requested by a "get"
+ *     instruction.
  *
- * @param socket The guac_socket connection to use.
- * @param channel The index of the audio channel the sound should play on.
- * @param mimetype The mimetype of the data being sent.
- * @param duration The duration of the audio being sent, in milliseconds.
- * @param size The number of bytes of audio data to send.
- * @return Zero on success, non-zero on error.
+ * @return
+ *     Zero on success, non-zero on error.
  */
-int guac_protocol_send_audio_header(guac_socket* socket,
-        int channel, const char* mimetype, double duration, int size);
+int guac_protocol_send_body(guac_socket* socket, const guac_object* object,
+        const guac_stream* stream, const char* mimetype, const char* name);
 
 /**
- * Writes a block of audio data to the currently in-progress audio instruction
- * which was started with guac_protocol_send_audio_header(). Exactly the
- * number of requested bytes are written unless an error occurs. This function
- * may be called multiple times per audio instruction for each chunk of audio
- * data being written, allowing the potentially huge audio instruction to be
- * split across multiple writes.
+ * Sends a filesystem instruction over the given guac_socket connection.
  *
  * If an error occurs sending the instruction, a non-zero value is
  * returned, and guac_error is set appropriately.
  *
- * @param socket The guac_socket connection to use.
- * @param data The audio data to write.
- * @param count The number of bytes within the given buffer of audio data
- *              that must be written.
- * @return Zero on success, non-zero on error.
- */
-int guac_protocol_send_audio_data(guac_socket* socket, void* data, int count);
-
-/**
- * Completes the audio instruction which was started with
- * guac_protocol_send_audio_header(), and whose data has been written with
- * guac_protocol_send_audio_data().
+ * @param socket
+ *     The guac_socket connection to use.
  *
- * If an error occurs sending the instruction, a non-zero value is
- * returned, and guac_error is set appropriately.
+ * @param object
+ *     The object representing the filesystem being exposed.
  *
- * @param socket The guac_socket connection to use.
- * @return Zero on success, non-zero on error.
+ * @param name
+ *     A name describing the filesystem being exposed.
+ *
+ * @return
+ *     Zero on success, non-zero on error.
  */
-int guac_protocol_send_audio_end(guac_socket* socket);
+int guac_protocol_send_filesystem(guac_socket* socket,
+        const guac_object* object, const char* name);
 
 /**
- * Sends a file instruction over the given guac_socket connection.
+ * Sends an undefine instruction over the given guac_socket connection.
  *
  * If an error occurs sending the instruction, a non-zero value is
  * returned, and guac_error is set appropriately.
  *
- * @param socket The guac_socket connection to use.
- * @param index The index of the blob that will contain the contents
- *              of this file.
- * @param mimetype The mimetype of the data being sent.
- * @param name A name describing the file being sent.
- * @return Zero on success, non-zero on error.
+ * @param socket
+ *     The guac_socket connection to use.
+ *
+ * @param object
+ *     The object being undefined.
+ *
+ * @return
+ *     Zero on success, non-zero on error.
  */
-int guac_protocol_send_file(guac_socket* socket, int index, const char* mimetype, const char* name);
+int guac_protocol_send_undefine(guac_socket* socket,
+        const guac_object* object);
+
+/* MEDIA INSTRUCTIONS */
 
 /**
- * Writes a block of data to the currently in-progress blob which was already
- * created.
+ * Sends an audio instruction over the given guac_socket connection.
  *
  * If an error occurs sending the instruction, a non-zero value is
  * returned, and guac_error is set appropriately.
  *
- * @param socket The guac_socket connection to use.
- * @param index The index of the blob to append data to.
- * @param data The file data to write.
- * @param count The number of bytes within the given buffer of file data
- *              that must be written.
- * @return Zero on success, non-zero on error.
+ * @param socket
+ *     The guac_socket connection to use when sending the audio instruction.
+ *
+ * @param stream
+ *     The stream to use for future audio data.
+ *
+ * @param mimetype
+ *     The mimetype of the audio data which will be sent over the given stream.
+ *
+ * @return
+ *     Zero on success, non-zero on error.
  */
-int guac_protocol_send_blob(guac_socket* socket, int index, void* data, int count);
+int guac_protocol_send_audio(guac_socket* socket, const guac_stream* stream,
+        const char* mimetype);
 
 /**
- * Sends an end instruction over the given guac_socket connection.
+ * Sends a file instruction over the given guac_socket connection.
  *
  * If an error occurs sending the instruction, a non-zero value is
  * returned, and guac_error is set appropriately.
  *
  * @param socket The guac_socket connection to use.
- * @param index The index of the blob which is now complete.
+ * @param stream The stream to use.
+ * @param mimetype The mimetype of the data being sent.
+ * @param name A name describing the file being sent.
  * @return Zero on success, non-zero on error.
  */
-int guac_protocol_send_end(guac_socket* socket, int index);
+int guac_protocol_send_file(guac_socket* socket, const guac_stream* stream,
+        const char* mimetype, const char* name);
 
 /**
- * Sends a video instruction over the given guac_socket connection.
+ * Sends a pipe instruction over the given guac_socket connection.
  *
  * If an error occurs sending the instruction, a non-zero value is
  * returned, and guac_error is set appropriately.
  *
  * @param socket The guac_socket connection to use.
- * @param layer The destination layer.
+ * @param stream The stream to use.
  * @param mimetype The mimetype of the data being sent.
- * @param duration The duration of the video being sent, in milliseconds.
- * @param data The video data to be sent.
- * @param size The number of bytes of video data to send.
+ * @param name An arbitrary name uniquely identifying this pipe.
  * @return Zero on success, non-zero on error.
  */
-int guac_protocol_send_video(guac_socket* socket, const guac_layer* layer,
-        const char* mimetype, double duration, void* data, int size);
+int guac_protocol_send_pipe(guac_socket* socket, const guac_stream* stream,
+        const char* mimetype, const char* name);
 
 /**
- * Begins a video instruction over the given guac_socket connection. Only the
- * initial non-data part of the instruction and the length of the data part
- * of the instruction are sent. The actual contents of the data must be
- * sent with guac_protocol_send_video_data(), and the instruction must be
- * completed with guac_protocol_send_video_end().
- *
- * Note that the size of the video to be sent MUST be known ahead of time,
- * even though the data of the video may be sent in chunks.
- *
- * No further instruction data may be sent along the givven guac_socket
- * except via guac_protocol_send_video_data() until the video instruction
- * is completed with guac_protocol_send_video_end().
- *
- * Note that if you send this instruction over a threadsafe socket, you
- * MUST also call guac_protocol_send_video_end() or the socket will be
- * left in an unsafe state.
+ * Writes a block of data to the currently in-progress blob which was already
+ * created.
  *
  * If an error occurs sending the instruction, a non-zero value is
  * returned, and guac_error is set appropriately.
  *
  * @param socket The guac_socket connection to use.
- * @param layer The destination layer.
- * @param mimetype The mimetype of the data being sent.
- * @param duration The duration of the video being sent, in milliseconds.
- * @param size The number of bytes of video data to send.
+ * @param stream The stream to use.
+ * @param data The file data to write.
+ * @param count The number of bytes within the given buffer of file data
+ *              that must be written.
  * @return Zero on success, non-zero on error.
  */
-int guac_protocol_send_video_header(guac_socket* socket,
-        const guac_layer* layer, const char* mimetype, double duration, int size);
+int guac_protocol_send_blob(guac_socket* socket, const guac_stream* stream,
+        const void* data, int count);
 
 /**
- * Writes a block of video data to the currently in-progress video instruction
- * which was started with guac_protocol_send_video_header(). Exactly the
- * number of requested bytes are written unless an error occurs. This function
- * may be called multiple times per video instruction for each chunk of video
- * data being written, allowing the potentially huge video instruction to be
- * split across multiple writes.
+ * Sends an end instruction over the given guac_socket connection.
  *
  * If an error occurs sending the instruction, a non-zero value is
  * returned, and guac_error is set appropriately.
  *
  * @param socket The guac_socket connection to use.
- * @param data The video data to write.
- * @param count The number of bytes within the given buffer of video data
- *              that must be written.
+ * @param stream The stream to use.
  * @return Zero on success, non-zero on error.
  */
-int guac_protocol_send_video_data(guac_socket* socket, void* data, int count);
+int guac_protocol_send_end(guac_socket* socket, const guac_stream* stream);
 
 /**
- * Completes the video instruction which was started with
- * guac_protocol_send_video_header(), and whose data has been written with
- * guac_protocol_send_video_data().
+ * Sends a video instruction over the given guac_socket connection.
  *
  * If an error occurs sending the instruction, a non-zero value is
  * returned, and guac_error is set appropriately.
  *
- * @param socket The guac_socket connection to use.
- * @return Zero on success, non-zero on error.
+ * @param socket
+ *     The guac_socket connection to use when sending the video instruction.
+ *
+ * @param stream
+ *     The stream to use for future video data.
+ *
+ * @param layer
+ *     The destination layer on which the streamed video should be played.
+ *
+ * @param mimetype
+ *     The mimetype of the video data which will be sent over the given stream.
+ *
+ * @return
+ *     Zero on success, non-zero on error.
  */
-int guac_protocol_send_video_end(guac_socket* socket);
+int guac_protocol_send_video(guac_socket* socket, const guac_stream* stream,
+        const guac_layer* layer, const char* mimetype);
 
 /* DRAWING INSTRUCTIONS */
 
@@ -691,22 +617,41 @@ int guac_protocol_send_lstroke(guac_socket* socket,
         const guac_layer* srcl);
 
 /**
- * Sends a png instruction over the given guac_socket connection. The PNG image
- * data given will be automatically base64-encoded for transmission.
+ * Sends an img instruction over the given guac_socket connection.
  *
  * If an error occurs sending the instruction, a non-zero value is
  * returned, and guac_error is set appropriately.
  *
- * @param socket The guac_socket connection to use.
- * @param mode The composite mode to use.
- * @param layer The destination layer.
- * @param x The destination X coordinate.
- * @param y The destination Y coordinate.
- * @param surface A cairo surface containing the image data to send.
- * @return Zero on success, non-zero on error.
+ * @param socket
+ *     The guac_socket connection to use when sending the img instruction.
+ *
+ * @param stream
+ *     The stream over which the image data will be sent.
+ *
+ * @param mode
+ *     The composite mode to use when drawing the image over the destination
+ *     layer.
+ *
+ * @param layer
+ *     The destination layer.
+ *
+ * @param mimetype
+ *     The mimetype of the image data being sent.
+ *
+ * @param x
+ *     The X coordinate of the upper-left corner of the destination rectangle
+ *     within the destination layer, in pixels.
+ *
+ * @param y
+ *     The Y coordinate of the upper-left corner of the destination rectangle
+ *     within the destination layer, in pixels.
+ *
+ * @return
+ *     Zero if the instruction was successfully sent, non-zero on error.
  */
-int guac_protocol_send_png(guac_socket* socket, guac_composite_mode mode,
-        const guac_layer* layer, int x, int y, cairo_surface_t* surface);
+int guac_protocol_send_img(guac_socket* socket, const guac_stream* stream,
+        guac_composite_mode mode, const guac_layer* layer,
+        const char* mimetype, int x, int y);
 
 /**
  * Sends a pop instruction over the given guac_socket connection.
@@ -912,10 +857,12 @@ int guac_protocol_send_size(guac_socket* socket, const guac_layer* layer,
  * returned, and guac_error is set appropriately.
  *
  * @param socket The guac_socket connection to use.
- * @param data The clipboard data to send.
+ * @param stream The stream to use.
+ * @param mimetype The mimetype of the clipboard data being sent.
  * @return Zero on success, non-zero on error.
  */
-int guac_protocol_send_clipboard(guac_socket* socket, const char* data);
+int guac_protocol_send_clipboard(guac_socket* socket, const guac_stream* stream,
+        const char* mimetype);
 
 /**
  * Sends a name instruction over the given guac_socket connection.
@@ -926,5 +873,14 @@ int guac_protocol_send_clipboard(guac_socket* socket, const char* data);
  */
 int guac_protocol_send_name(guac_socket* socket, const char* name);
 
+/**
+ * Decodes the given base64-encoded string in-place. The base64 string must
+ * be NULL-terminated.
+ *
+ * @param base64 The base64-encoded string to decode.
+ * @return The number of bytes resulting from the decode operation.
+ */
+int guac_protocol_decode_base64(char* base64);
+
 #endif
 
diff --git a/src/libguac/guacamole/socket-constants.h b/src/libguac/guacamole/socket-constants.h
new file mode 100644
index 0000000..3c435fa
--- /dev/null
+++ b/src/libguac/guacamole/socket-constants.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _GUAC_SOCKET_CONSTANTS_H
+#define _GUAC_SOCKET_CONSTANTS_H
+
+/**
+ * Constants related to the guac_socket object.
+ *
+ * @file socket-constants.h
+ */
+
+/**
+ * The number of bytes to buffer within each socket before flushing.
+ */
+#define GUAC_SOCKET_OUTPUT_BUFFER_SIZE 8192
+
+/**
+ * The number of milliseconds to wait between keep-alive pings on a socket
+ * with keep-alive enabled.
+ */
+#define GUAC_SOCKET_KEEP_ALIVE_INTERVAL 5000
+
+#endif
+
diff --git a/src/libguac/guacamole/socket-fntypes.h b/src/libguac/guacamole/socket-fntypes.h
new file mode 100644
index 0000000..8862c16
--- /dev/null
+++ b/src/libguac/guacamole/socket-fntypes.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _GUAC_SOCKET_FNTYPES_H
+#define _GUAC_SOCKET_FNTYPES_H
+
+/**
+ * Function type definitions related to the guac_socket object.
+ *
+ * @file socket-fntypes.h
+ */
+
+#include "socket-types.h"
+
+#include <unistd.h>
+
+/**
+ * Generic read handler for socket read operations, modeled after the standard
+ * POSIX read() function. When set within a guac_socket, a handler of this type
+ * will be called when data needs to be read into the socket.
+ *
+ * @param socket The guac_socket being read from.
+ * @param buf The arbitrary buffer we must populate with data.
+ * @param count The maximum number of bytes to read into the buffer.
+ * @return The number of bytes read, or -1 if an error occurs.
+ */
+typedef ssize_t guac_socket_read_handler(guac_socket* socket,
+        void* buf, size_t count);
+
+/**
+ * Generic write handler for socket write operations, modeled after the standard
+ * POSIX write() function. When set within a guac_socket, a handler of this type
+ * will be called when data needs to be write into the socket.
+ *
+ * @param socket The guac_socket being written to.
+ * @param buf The arbitrary buffer containing data to be written.
+ * @param count The maximum number of bytes to write from the buffer.
+ * @return The number of bytes written, or -1 if an error occurs.
+ */
+typedef ssize_t guac_socket_write_handler(guac_socket* socket,
+        const void* buf, size_t count);
+
+/**
+ * Generic handler for socket select operations, similar to the POSIX select()
+ * function. When guac_socket_select() is called on a guac_socket, its
+ * guac_socket_select_handler will be invoked, if defined.
+ *
+ * @param socket The guac_socket being selected.
+ * @param usec_timeout The maximum number of microseconds to wait for data, or
+ *                     -1 to potentially wait forever.
+ * @return Positive on success, zero if the timeout elapsed and no data is
+ *         available, negative on error.
+ */
+typedef int guac_socket_select_handler(guac_socket* socket, int usec_timeout);
+
+/**
+ * Generic handler for the closing of a socket, modeled after the standard
+ * POSIX close() function. When set within a guac_socket, a handler of this type
+ * will be called when the socket is closed.
+ *
+ * @param socket The guac_socket being closed.
+ * @return Zero on success, or -1 if an error occurs.
+ */
+typedef int guac_socket_free_handler(guac_socket* socket);
+
+#endif
+
diff --git a/src/libguac/guacamole/socket-types.h b/src/libguac/guacamole/socket-types.h
new file mode 100644
index 0000000..5315f50
--- /dev/null
+++ b/src/libguac/guacamole/socket-types.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _GUAC_SOCKET_TYPES_H
+#define _GUAC_SOCKET_TYPES_H
+
+/**
+ * Type definitions related to the guac_socket object.
+ *
+ * @file socket-types.h
+ */
+
+/**
+ * The core I/O object of Guacamole. guac_socket provides buffered input and
+ * output as well as convenience methods for efficiently writing base64 data.
+ */
+typedef struct guac_socket guac_socket;
+
+/**
+ * Possible current states of a guac_socket.
+ */
+typedef enum guac_socket_state {
+
+    /**
+     * The socket is open and can be written to / read from.
+     */
+    GUAC_SOCKET_OPEN,
+
+    /**
+     * The socket is closed. Reads and writes will fail.
+     */
+    GUAC_SOCKET_CLOSED
+
+} guac_socket_state;
+
+#endif
+
diff --git a/src/libguac/guacamole/socket.h b/src/libguac/guacamole/socket.h
index a9d63d7..50bd934 100644
--- a/src/libguac/guacamole/socket.h
+++ b/src/libguac/guacamole/socket.h
@@ -1,113 +1,43 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
 #ifndef _GUAC_SOCKET_H
 #define _GUAC_SOCKET_H
 
-#include <pthread.h>
-#include <stdint.h>
-#include <unistd.h>
-
 /**
  * Defines the guac_socket object and functionss for using and manipulating it.
  *
  * @file socket.h
  */
 
-/**
- * The number of bytes to buffer within each socket before flushing.
- */
-#define GUAC_SOCKET_OUTPUT_BUFFER_SIZE 8192
+#include "socket-constants.h"
+#include "socket-fntypes.h"
+#include "socket-types.h"
+#include "timestamp-types.h"
 
-typedef struct guac_socket guac_socket;
-
-/**
- * Generic read handler for socket read operations, modeled after the standard
- * POSIX read() function. When set within a guac_socket, a handler of this type
- * will be called when data needs to be read into the socket.
- *
- * @param socket The guac_socket being read from.
- * @param buf The arbitrary buffer we must populate with data.
- * @param count The maximum number of bytes to read into the buffer.
- * @return The number of bytes read, or -1 if an error occurs.
- */
-typedef ssize_t guac_socket_read_handler(guac_socket* socket,
-        void* buf, size_t count);
-
-/**
- * Generic write handler for socket write operations, modeled after the standard
- * POSIX write() function. When set within a guac_socket, a handler of this type
- * will be called when data needs to be write into the socket.
- *
- * @param socket The guac_socket being written to.
- * @param buf The arbitrary buffer containing data to be written.
- * @param count The maximum number of bytes to write from the buffer.
- * @return The number of bytes written, or -1 if an error occurs.
- */
-typedef ssize_t guac_socket_write_handler(guac_socket* socket,
-        const void* buf, size_t count);
-
-/**
- * Generic handler for socket select operations, similar to the POSIX select()
- * function. When guac_socket_select() is called on a guac_socket, its
- * guac_socket_select_handler will be invoked, if defined.
- *
- * @param socket The guac_socket being selected.
- * @param usec_timeout The maximum number of microseconds to wait for data, or
- *                     -1 to potentially wait forever.
- * @return Positive on success, zero if the timeout elapsed and no data is
- *         available, negative on error.
- */
-typedef int guac_socket_select_handler(guac_socket* socket, int usec_timeout);
-
-/**
- * Generic handler for the closing of a socket, modeled after the standard
- * POSIX close() function. When set within a guac_socket, a handler of this type
- * will be called when the socket is closed.
- *
- * @param socket The guac_socket being closed.
- * @return Zero on success, or -1 if an error occurs.
- */
-typedef int guac_socket_free_handler(guac_socket* socket);
+#include <pthread.h>
+#include <stdint.h>
+#include <unistd.h>
 
-/**
- * The core I/O object of Guacamole. guac_socket provides buffered input and
- * output as well as convenience methods for efficiently writing base64 data.
- */
 struct guac_socket {
 
     /**
@@ -137,7 +67,18 @@ struct guac_socket {
      * Handler which will be called when the socket is free'd (closed).
      */
     guac_socket_free_handler* free_handler;
-    
+
+    /**
+     * The current state of this guac_socket.
+     */
+    guac_socket_state state;
+
+    /**
+     * The timestamp associated with the time the last block of data was
+     * written to this guac_socket.
+     */
+    guac_timestamp last_write_timestamp;
+
     /**
      * The number of bytes present in the base64 "ready" buffer.
      */
@@ -161,37 +102,22 @@ struct guac_socket {
     char __out_buf[GUAC_SOCKET_OUTPUT_BUFFER_SIZE];
 
     /**
-     * The current location of parsing within the instruction buffer.
+     * Pointer to the first character of the current in-progress instruction
+     * within the buffer.
      */
-    int __instructionbuf_parse_start;
+    char* __instructionbuf_unparsed_start;
 
     /**
-     * The current size of the instruction buffer.
+     * Pointer to the first unused section of the instruction buffer.
      */
-    int __instructionbuf_size;
-
-    /**
-     * The number of bytes currently in the instruction buffer.
-     */
-    int __instructionbuf_used_length;
+    char* __instructionbuf_unparsed_end;
 
     /**
      * The instruction buffer. This is essentially the input buffer,
      * provided as a convenience to be used to buffer instructions until
      * those instructions are complete and ready to be parsed.
      */
-    char* __instructionbuf;
-
-    /**
-     * The number of elements parsed so far.
-     */
-    int __instructionbuf_elementc;
-
-    /**
-     * Array of pointers into the instruction buffer, where each pointer
-     * points to the start of the corresponding element.
-     */
-    char* __instructionbuf_elementv[64];
+    char __instructionbuf[32768];
 
     /**
      * Whether instructions should be guaranteed atomic across threads using
@@ -210,6 +136,16 @@ struct guac_socket {
      */
     pthread_mutex_t __buffer_lock;
 
+    /**
+     * Whether automatic keep-alive is enabled.
+     */
+    int __keep_alive_enabled;
+
+    /**
+     * The keep-alive thread.
+     */
+    pthread_t __keep_alive_thread;
+
 };
 
 /**
@@ -240,6 +176,17 @@ void guac_socket_free(guac_socket* socket);
 void guac_socket_require_threadsafe(guac_socket* socket);
 
 /**
+ * Declares that the given socket must automatically send a keep-alive ping
+ * to ensure neither side of the socket times out while the socket is open.
+ * This ping will take the form of a "nop" instruction. Enabling keep-alive
+ * automatically enables threadsafety.
+ *
+ * @param socket The guac_socket to declare as requiring an automatic
+ *               keep-alive ping.
+ */
+void guac_socket_require_keep_alive(guac_socket* socket);
+
+/**
  * Marks the beginning of a Guacamole protocol instruction. If threadsafety
  * is enabled on the socket, other instructions will be blocked from sending
  * until this instruction is complete.
@@ -403,7 +350,6 @@ ssize_t guac_socket_flush_base64(guac_socket* socket);
  */
 ssize_t guac_socket_flush(guac_socket* socket);
 
-
 /**
  * Waits for input to be available on the given guac_socket object until the
  * specified timeout elapses.
diff --git a/src/libguac/guacamole/stream-types.h b/src/libguac/guacamole/stream-types.h
new file mode 100644
index 0000000..94dbfa4
--- /dev/null
+++ b/src/libguac/guacamole/stream-types.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _GUAC_STREAM_TYPES_H
+#define _GUAC_STREAM_TYPES_H
+
+/**
+ * Type definitions related to Guacamole protocol streams.
+ *
+ * @file stream-types.h
+ */
+
+/**
+ * Represents a single stream within the Guacamole protocol.
+ */
+typedef struct guac_stream guac_stream;
+
+#endif
+
diff --git a/src/libguac/guacamole/stream.h b/src/libguac/guacamole/stream.h
index fa0bb08..01102e1 100644
--- a/src/libguac/guacamole/stream.h
+++ b/src/libguac/guacamole/stream.h
@@ -1,66 +1,112 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
 #ifndef _GUAC_STREAM_H
 #define _GUAC_STREAM_H
 
 /**
- * Provides functions and structures required for allocating and using nested
- * streams.
+ * Provides functions and structures required for allocating and using streams.
  *
  * @file stream.h
  */
 
-typedef struct guac_stream guac_stream;
+#include "client-fntypes.h"
+#include "stream-types.h"
 
-/**
- * Represents a single nested stream within the Guacamole protocol.
- */
 struct guac_stream {
 
     /**
-     * The index of this layer.
+     * The index of this stream.
      */
     int index;
 
     /**
-     * A guac_socket which writes to this stream.
+     * Arbitrary data associated with this stream.
+     */
+    void* data;
+
+    /**
+     * Handler for ack events sent by the Guacamole web-client.
+     *
+     * The handler takes a guac_stream which contains the stream index and
+     * will persist through the duration of the transfer, a string containing
+     * the error or status message, and a status code.
+     *
+     * Example:
+     * @code
+     *     int ack_handler(guac_client* client, guac_stream* stream,
+     *             char* error, guac_protocol_status status);
+     *
+     *     int some_function(guac_client* client) {
+     *
+     *         guac_stream* stream = guac_client_alloc_stream(client);
+     *         stream->ack_handler = ack_handler;
+     *
+     *         guac_protocol_send_clipboard(client->socket,
+     *             stream, "text/plain");
+     *
+     *     }
+     * @endcode
+     */
+    guac_client_ack_handler* ack_handler;
+
+    /**
+     * Handler for blob events sent by the Guacamole web-client.
+     *
+     * The handler takes a guac_stream which contains the stream index and
+     * will persist through the duration of the transfer, an arbitrary buffer
+     * containing the blob, and the length of the blob.
+     *
+     * Example:
+     * @code
+     *     int blob_handler(guac_client* client, guac_stream* stream,
+     *             void* data, int length);
+     *
+     *     int my_clipboard_handler(guac_client* client, guac_stream* stream,
+     *             char* mimetype) {
+     *         stream->blob_handler = blob_handler;
+     *     }
+     * @endcode
+     */
+    guac_client_blob_handler* blob_handler;
+
+    /**
+     * Handler for stream end events sent by the Guacamole web-client.
+     *
+     * The handler takes only a guac_stream which contains the stream index.
+     * This guac_stream will be disposed of immediately after this event is
+     * finished.
+     *
+     * Example:
+     * @code
+     *     int end_handler(guac_client* client, guac_stream* stream);
+     *
+     *     int my_clipboard_handler(guac_client* client, guac_stream* stream,
+     *             char* mimetype) {
+     *         stream->end_handler = end_handler;
+     *     }
+     * @endcode
      */
-    guac_socket* socket;
+    guac_client_end_handler* end_handler;
 
 };
 
diff --git a/src/libguac/guacamole/timestamp-types.h b/src/libguac/guacamole/timestamp-types.h
new file mode 100644
index 0000000..b46be30
--- /dev/null
+++ b/src/libguac/guacamole/timestamp-types.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _GUAC_TIMESTAMP_TYPES_H
+#define _GUAC_TIMESTAMP_TYPES_H
+
+/**
+ * Type definitions related to Guacamole protocol timestamps.
+ *
+ * @file timestamp-types.h
+ */
+
+#include <stdint.h>
+
+/**
+ * An arbitrary timestamp denoting a relative time value in milliseconds.
+ */
+typedef int64_t guac_timestamp;
+
+#endif
+
diff --git a/src/libguac/guacamole/timestamp.h b/src/libguac/guacamole/timestamp.h
index baff204..05bb0f8 100644
--- a/src/libguac/guacamole/timestamp.h
+++ b/src/libguac/guacamole/timestamp.h
@@ -1,44 +1,27 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef _GUAC_TIME_H
-#define _GUAC_TIME_H
-
-#include <stdint.h>
+#ifndef _GUAC_TIMESTAMP_H
+#define _GUAC_TIMESTAMP_H
 
 /**
  * Provides functions and structures for creating timestamps.
@@ -46,10 +29,7 @@
  * @file timestamp.h
  */
 
-/**
- * An arbitrary timestamp denoting a relative time value in milliseconds.
- */
-typedef int64_t guac_timestamp;
+#include "timestamp-types.h"
 
 /**
  * Returns an arbitrary timestamp. The difference between return values of any
diff --git a/src/libguac/guacamole/unicode.h b/src/libguac/guacamole/unicode.h
index fac3a65..46a5019 100644
--- a/src/libguac/guacamole/unicode.h
+++ b/src/libguac/guacamole/unicode.h
@@ -1,51 +1,37 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Original Code is libguac.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef _GUAC_UNICODE_H
 #define _GUAC_UNICODE_H
 
-#include <stddef.h>
-
 /**
  * Provides functions for manipulating Unicode strings.
  *
  * @file unicode.h
  */
 
+#include <stddef.h>
+
 /**
  * Given the initial byte of a single UTF-8 character, returns the overall
  * byte size of the entire character.
diff --git a/src/libguac/hash.c b/src/libguac/hash.c
index 225f0de..6fe3b45 100644
--- a/src/libguac/hash.c
+++ b/src/libguac/hash.c
@@ -1,44 +1,31 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
+#include "config.h"
+
+#include <cairo/cairo.h>
 
 #include <stdint.h>
 #include <string.h>
-#include <cairo/cairo.h>
 
 /*
  * Arbitrary hash function whhich maps ALL 32-bit numbers onto 24-bit numbers
diff --git a/src/libguac/instruction.c b/src/libguac/instruction.c
index 78724d6..90f9067 100644
--- a/src/libguac/instruction.c
+++ b/src/libguac/instruction.c
@@ -1,275 +1,272 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Original Code is libguac.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
+#include "config.h"
 
 #include "error.h"
 #include "instruction.h"
-#include "protocol.h"
 #include "socket.h"
 #include "unicode.h"
 
-int __guac_fill_instructionbuf(guac_socket* socket) {
-
-    int retval;
-    
-    /* Attempt to fill buffer */
-    retval = guac_socket_read(
-        socket,
-        socket->__instructionbuf + socket->__instructionbuf_used_length,
-        socket->__instructionbuf_size - socket->__instructionbuf_used_length
-    );
-
-    /* Set guac_error if recv() unsuccessful */
-    if (retval < 0) {
-        guac_error = GUAC_STATUS_SEE_ERRNO;
-        guac_error_message = "Error filling instruction buffer";
-        return retval;
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+guac_instruction* guac_instruction_alloc() {
+
+    /* Allocate space for instruction */
+    guac_instruction* instruction = malloc(sizeof(guac_instruction));
+    if (instruction == NULL) {
+        guac_error = GUAC_STATUS_NO_MEMORY;
+        guac_error_message = "Insufficient memory to allocate instruction";
+        return NULL;
     }
 
-    socket->__instructionbuf_used_length += retval;
+    guac_instruction_reset(instruction);
+    return instruction;
+
+}
+
+void guac_instruction_reset(guac_instruction* instruction) {
+    instruction->opcode = NULL;
+    instruction->argc = 0;
+    instruction->state = GUAC_INSTRUCTION_PARSE_LENGTH;
+    instruction->__elementc = 0;
+    instruction->__element_length = 0;
+}
 
-    /* Expand buffer if necessary */
-    if (socket->__instructionbuf_used_length >
-            socket->__instructionbuf_size / 2) {
+int guac_instruction_append(guac_instruction* instr,
+        void* buffer, int length) {
 
-        socket->__instructionbuf_size *= 2;
-        socket->__instructionbuf = realloc(socket->__instructionbuf,
-                socket->__instructionbuf_size);
+    char* char_buffer = (char*) buffer;
+    int bytes_parsed = 0;
+
+    /* Do not exceed maximum number of elements */
+    if (instr->__elementc == GUAC_INSTRUCTION_MAX_ELEMENTS
+            && instr->state != GUAC_INSTRUCTION_PARSE_COMPLETE) {
+        instr->state = GUAC_INSTRUCTION_PARSE_ERROR;
+        return 0;
     }
 
-    return retval;
+    /* Parse element length */
+    if (instr->state == GUAC_INSTRUCTION_PARSE_LENGTH) {
 
-}
+        int parsed_length = instr->__element_length;
+        while (bytes_parsed < length) {
+
+            /* Pull next character */
+            char c = *(char_buffer++);
+            bytes_parsed++;
+
+            /* If digit, add to length */
+            if (c >= '0' && c <= '9')
+                parsed_length = parsed_length*10 + c - '0';
+
+            /* If period, switch to parsing content */
+            else if (c == '.') {
+                instr->__elementv[instr->__elementc++] = char_buffer;
+                instr->state = GUAC_INSTRUCTION_PARSE_CONTENT;
+                break;
+            }
+
+            /* If not digit, parse error */
+            else {
+                instr->state = GUAC_INSTRUCTION_PARSE_ERROR;
+                return 0;
+            }
+
+        }
+
+        /* If too long, parse error */
+        if (parsed_length > GUAC_INSTRUCTION_MAX_LENGTH) {
+            instr->state = GUAC_INSTRUCTION_PARSE_ERROR;
+            return 0;
+        }
+
+        /* Save length */
+        instr->__element_length = parsed_length;
+
+    } /* end parse length */
+
+    /* Parse element content */
+    if (instr->state == GUAC_INSTRUCTION_PARSE_CONTENT) {
+
+        while (bytes_parsed < length && instr->__element_length >= 0) {
+
+            /* Get length of current character */
+            char c = *char_buffer;
+            int char_length = guac_utf8_charsize((unsigned char) c);
+
+            /* If full character not present in buffer, stop now */
+            if (char_length + bytes_parsed > length)
+                break;
+
+            /* Record character as parsed */
+            bytes_parsed += char_length;
+
+            /* If end of element, handle terminator */
+            if (instr->__element_length == 0) {
 
+                *char_buffer = '\0';
+
+                /* If semicolon, store end-of-instruction */
+                if (c == ';') {
+                    instr->state = GUAC_INSTRUCTION_PARSE_COMPLETE;
+                    instr->opcode = instr->__elementv[0];
+                    instr->argv = &(instr->__elementv[1]);
+                    instr->argc = instr->__elementc - 1;
+                    break;
+                }
+
+                /* If comma, move on to next element */
+                else if (c == ',') {
+                    instr->state = GUAC_INSTRUCTION_PARSE_LENGTH;
+                    break;
+                }
+
+                /* Otherwise, parse error */
+                else {
+                    instr->state = GUAC_INSTRUCTION_PARSE_ERROR;
+                    return 0;
+                }
+
+            } /* end if end of element */
+
+            /* Advance to next character */
+            instr->__element_length--;
+            char_buffer += char_length;
+
+        }
+
+    } /* end parse content */
+
+    return bytes_parsed;
+
+}
 
 /* Returns new instruction if one exists, or NULL if no more instructions. */
 guac_instruction* guac_instruction_read(guac_socket* socket,
         int usec_timeout) {
 
-    int retval;
-   
-    /* Loop until a instruction is read */
-    for (;;) {
+    char* unparsed_end = socket->__instructionbuf_unparsed_end;
+    char* unparsed_start = socket->__instructionbuf_unparsed_start;
+    char* instr_start = socket->__instructionbuf_unparsed_start;
+    char* buffer_end = socket->__instructionbuf
+                            + sizeof(socket->__instructionbuf);
 
-        /* Length of element, in Unicode characters */
-        int element_length = 0;
+    guac_instruction* instruction = guac_instruction_alloc();
 
-        /* Length of element, in bytes */
-        int element_byte_length = 0;
+    while (instruction->state != GUAC_INSTRUCTION_PARSE_COMPLETE
+        && instruction->state != GUAC_INSTRUCTION_PARSE_ERROR) {
 
-        /* Current position within the element, in Unicode characters */
-        int current_unicode_length = 0;
+        /* Add any available data to buffer */
+        int parsed = guac_instruction_append(instruction, unparsed_start,
+                unparsed_end - unparsed_start);
 
-        /* Position within buffer */
-        int i = socket->__instructionbuf_parse_start;
+        /* Read more data if not enough data to parse */
+        if (parsed == 0) {
 
-        /* Parse instruction in buffer */
-        while (i < socket->__instructionbuf_used_length) {
+            int retval;
 
-            /* Read character from buffer */
-            char c = socket->__instructionbuf[i++];
+            /* If no space left to read, fail */
+            if (unparsed_end == buffer_end) {
 
-            /* If digit, calculate element length */
-            if (c >= '0' && c <= '9')
-                element_length = element_length * 10 + c - '0';
+                /* Shift backward if possible */
+                if (instr_start != socket->__instructionbuf) {
 
-            /* Otherwise, if end of length */
-            else if (c == '.') {
+                    int i;
 
-                /* Calculate element byte length by walking buffer */
-                while (i + element_byte_length <
-                            socket->__instructionbuf_used_length
-                    && current_unicode_length < element_length) {
+                    /* Shift buffer */
+                    int offset = instr_start - socket->__instructionbuf;
+                    memmove(socket->__instructionbuf, instr_start,
+                            unparsed_end - instr_start);
 
-                    /* Get next byte */
-                    c = socket->__instructionbuf[i + element_byte_length];
+                    /* Update tracking pointers */
+                    unparsed_end -= offset;
+                    unparsed_start -= offset;
+                    instr_start = socket->__instructionbuf;
 
-                    /* Update byte and character lengths */
-                    element_byte_length += guac_utf8_charsize((unsigned) c);
-                    current_unicode_length++;
+                    /* Update parsed elements, if any */
+                    for (i=0; i<instruction->__elementc; i++)
+                        instruction->__elementv[i] -= offset;
 
                 }
 
-                /* Verify element is fully read */
-                if (current_unicode_length == element_length) {
-
-                    /* Get element value */
-                    char* elementv = &(socket->__instructionbuf[i]);
-                   
-                    /* Get terminator, set null terminator of elementv */ 
-                    char terminator = elementv[element_byte_length];
-                    elementv[element_byte_length] = '\0';
-
-                    /* Move to char after terminator of element */
-                    i += element_byte_length+1;
-
-                    /* Reset element length */
-                    element_length =
-                    element_byte_length =
-                    current_unicode_length = 0;
-
-                    /* As element has been read successfully, update
-                     * parse start */
-                    socket->__instructionbuf_parse_start = i;
-
-                    /* Save element */
-                    socket->__instructionbuf_elementv[socket->__instructionbuf_elementc++] = elementv;
-
-                    /* Finish parse if terminator is a semicolon */
-                    if (terminator == ';') {
-
-                        guac_instruction* parsed_instruction;
-                        int j;
-
-                        /* Allocate instruction */
-                        parsed_instruction = malloc(sizeof(guac_instruction));
-                        if (parsed_instruction == NULL) {
-                            guac_error = GUAC_STATUS_NO_MEMORY;
-                            guac_error_message = "Could not allocate memory for parsed instruction";
-                            return NULL;
-                        }
-
-                        /* Init parsed instruction */
-                        parsed_instruction->argc = socket->__instructionbuf_elementc - 1;
-                        parsed_instruction->argv = malloc(sizeof(char*) * parsed_instruction->argc);
-
-                        /* Fail if memory could not be alloc'd for argv */
-                        if (parsed_instruction->argv == NULL) {
-                            guac_error = GUAC_STATUS_NO_MEMORY;
-                            guac_error_message = "Could not allocate memory for arguments of parsed instruction";
-                            free(parsed_instruction);
-                            return NULL;
-                        }
-
-                        /* Set opcode */
-                        parsed_instruction->opcode = strdup(socket->__instructionbuf_elementv[0]);
-
-                        /* Fail if memory could not be alloc'd for opcode */
-                        if (parsed_instruction->opcode == NULL) {
-                            guac_error = GUAC_STATUS_NO_MEMORY;
-                            guac_error_message = "Could not allocate memory for opcode of parsed instruction";
-                            free(parsed_instruction->argv);
-                            free(parsed_instruction);
-                            return NULL;
-                        }
-
-
-                        /* Copy element values to parsed instruction */
-                        for (j=0; j<parsed_instruction->argc; j++) {
-                            parsed_instruction->argv[j] = strdup(socket->__instructionbuf_elementv[j+1]);
-
-                            /* Free memory and fail if out of mem */
-                            if (parsed_instruction->argv[j] == NULL) {
-                                guac_error = GUAC_STATUS_NO_MEMORY;
-                                guac_error_message = "Could not allocate memory for single argument of parsed instruction";
-
-                                /* Free all alloc'd argv values */
-                                while (--j >= 0)
-                                    free(parsed_instruction->argv[j]);
-
-                                free(parsed_instruction->opcode);
-                                free(parsed_instruction->argv);
-                                free(parsed_instruction);
-                                return NULL;
-                            }
-
-                        }
-
-                        /* Reset buffer */
-                        memmove(socket->__instructionbuf, socket->__instructionbuf + i, socket->__instructionbuf_used_length - i);
-                        socket->__instructionbuf_used_length -= i;
-                        socket->__instructionbuf_parse_start = 0;
-                        socket->__instructionbuf_elementc = 0;
-
-                        /* Done */
-                        return parsed_instruction;
-
-                    } /* end if terminator */
-
-                    /* Error if expected comma is not present */
-                    else if (terminator != ',') {
-                        guac_error = GUAC_STATUS_BAD_ARGUMENT;
-                        guac_error_message = "Element terminator of instruction was not ';' nor ','";
-                        return NULL;
-                    }
-
-                } /* end if element fully read */
-
-                /* Otherwise, read more data */
-                else
-                    break;
+                /* Otherwise, no memory to read */
+                else {
+                    guac_error = GUAC_STATUS_NO_MEMORY;
+                    guac_error_message = "Instruction too long";
+                    return NULL;
+                }
 
             }
 
-            /* Error if length is non-numeric or does not end in a period */
-            else {
-                guac_error = GUAC_STATUS_BAD_ARGUMENT;
-                guac_error_message = "Non-numeric character in element length";
+            /* No instruction yet? Get more data ... */
+            retval = guac_socket_select(socket, usec_timeout);
+            if (retval <= 0)
+                return NULL;
+           
+            /* Attempt to fill buffer */
+            retval = guac_socket_read(socket, unparsed_end,
+                    buffer_end - unparsed_end);
+
+            /* Set guac_error if read unsuccessful */
+            if (retval < 0) {
+                guac_error = GUAC_STATUS_SEE_ERRNO;
+                guac_error_message = "Error filling instruction buffer";
                 return NULL;
             }
 
-        }
+            /* EOF */
+            if (retval == 0) {
+                guac_error = GUAC_STATUS_CLOSED;
+                guac_error_message = "End of stream reached while "
+                                     "reading instruction";
+                return NULL;
+            }
 
-        /* No instruction yet? Get more data ... */
-        retval = guac_socket_select(socket, usec_timeout);
-        if (retval <= 0)
-            return NULL;
+            /* Update internal buffer */
+            unparsed_end += retval;
 
-        /* If more data is available, fill into buffer */
-        retval = __guac_fill_instructionbuf(socket);
+        }
 
-        /* Error, guac_error already set */
-        if (retval < 0)
-            return NULL;
+        /* If data was parsed, advance buffer */
+        else
+            unparsed_start += parsed;
 
-        /* EOF */
-        if (retval == 0) {
-            guac_error = GUAC_STATUS_NO_INPUT;
-            guac_error_message = "End of stream reached while reading instruction";
-            return NULL;
-        }
+    } /* end while parsing data */
 
+    /* Fail on error */
+    if (instruction->state == GUAC_INSTRUCTION_PARSE_ERROR) {
+        guac_error = GUAC_STATUS_PROTOCOL_ERROR;
+        guac_error_message = "Instruction parse error";
+        return NULL;
     }
 
-}
+    socket->__instructionbuf_unparsed_start = unparsed_start;
+    socket->__instructionbuf_unparsed_end = unparsed_end;
+    return instruction;
 
+}
 
 guac_instruction* guac_instruction_expect(guac_socket* socket, int usec_timeout,
         const char* opcode) {
@@ -287,7 +284,7 @@ guac_instruction* guac_instruction_expect(guac_socket* socket, int usec_timeout,
 
     /* Validate instruction */
     if (strcmp(instruction->opcode, opcode) != 0) {
-        guac_error = GUAC_STATUS_BAD_STATE;
+        guac_error = GUAC_STATUS_PROTOCOL_ERROR;
         guac_error_message = "Instruction read did not have expected opcode";
         guac_instruction_free(instruction);
         return NULL;
@@ -298,35 +295,14 @@ guac_instruction* guac_instruction_expect(guac_socket* socket, int usec_timeout,
 
 }
 
-
 void guac_instruction_free(guac_instruction* instruction) {
-
-    int argc = instruction->argc;
-
-    /* Free opcode */
-    free(instruction->opcode);
-
-    /* Free argv if set (may be NULL of argc is 0) */
-    if (instruction->argv) {
-
-        /* All argument values */
-        while (argc > 0)
-            free(instruction->argv[--argc]);
-
-        /* Free actual array */
-        free(instruction->argv);
-
-    }
-
-    /* Free instruction */
     free(instruction);
-
 }
 
-
 int guac_instruction_waiting(guac_socket* socket, int usec_timeout) {
 
-    if (socket->__instructionbuf_used_length > 0)
+    if (socket->__instructionbuf_unparsed_end >
+            socket->__instructionbuf_unparsed_start)
         return 1;
 
     return guac_socket_select(socket, usec_timeout);
diff --git a/src/libguac/ogg_encoder.c b/src/libguac/ogg_encoder.c
deleted file mode 100644
index e76b64e..0000000
--- a/src/libguac/ogg_encoder.c
+++ /dev/null
@@ -1,211 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include <stdlib.h>
-
-#include <guacamole/audio.h>
-#include <guacamole/client.h>
-#include <guacamole/protocol.h>
-
-#include <vorbis/vorbisenc.h>
-
-#include "ogg_encoder.h"
-
-void ogg_encoder_begin_handler(guac_audio_stream* audio) {
-
-    /* Allocate stream state */
-    ogg_encoder_state* state = (ogg_encoder_state*)
-        malloc(sizeof(ogg_encoder_state));
-
-    /* Init state */
-    vorbis_info_init(&(state->info));
-    vorbis_encode_init_vbr(&(state->info), audio->channels, audio->rate, 0.4);
-
-    vorbis_analysis_init(&(state->vorbis_state), &(state->info));
-    vorbis_block_init(&(state->vorbis_state), &(state->vorbis_block));
-
-    vorbis_comment_init(&(state->comment));
-    vorbis_comment_add_tag(&(state->comment), "ENCODER", "libguac-client-rdp");
-
-    ogg_stream_init(&(state->ogg_state), rand());
-
-    /* Write headers */
-    {
-        ogg_packet header;
-        ogg_packet header_comm;
-        ogg_packet header_code;
-
-        vorbis_analysis_headerout(
-                &(state->vorbis_state),
-                &(state->comment),
-                &header, &header_comm, &header_code);
-
-        ogg_stream_packetin(&(state->ogg_state), &header);
-        ogg_stream_packetin(&(state->ogg_state), &header_comm);
-        ogg_stream_packetin(&(state->ogg_state), &header_code);
-
-        /* For each packet */
-        while (ogg_stream_flush(&(state->ogg_state), &(state->ogg_page)) != 0) {
-
-            /* Write packet header */
-            guac_audio_stream_write_encoded(audio,
-                    state->ogg_page.header,
-                    state->ogg_page.header_len);
-
-            /* Write packet body */
-            guac_audio_stream_write_encoded(audio,
-                    state->ogg_page.body,
-                    state->ogg_page.body_len);
-        }
-
-    }
-
-    audio->data = state;
-
-}
-
-void ogg_encoder_write_blocks(guac_audio_stream* audio) {
-
-    /* Get state */
-    ogg_encoder_state* state = (ogg_encoder_state*) audio->data;
-
-    while (vorbis_analysis_blockout(&(state->vorbis_state),
-                &(state->vorbis_block)) == 1) {
-
-        /* Analyze */
-        vorbis_analysis(&(state->vorbis_block), NULL);
-        vorbis_bitrate_addblock(&(state->vorbis_block));
-
-        /* Flush Ogg pages */
-        while (vorbis_bitrate_flushpacket(&(state->vorbis_state),
-                    &(state->ogg_packet))) {
-
-            /* Weld packet into bitstream */
-            ogg_stream_packetin(&(state->ogg_state), &(state->ogg_packet));
-
-            /* Write out pages */
-            while (ogg_stream_pageout(&(state->ogg_state),
-                        &(state->ogg_page)) != 0) {
-
-                /* Write packet header */
-                guac_audio_stream_write_encoded(audio,
-                        state->ogg_page.header,
-                        state->ogg_page.header_len);
-
-                /* Write packet body */
-                guac_audio_stream_write_encoded(audio,
-                        state->ogg_page.body,
-                        state->ogg_page.body_len);
-
-                if (ogg_page_eos(&(state->ogg_page)))
-                    break;
-
-            }
-
-        }
-
-    }
-
-}
-
-void ogg_encoder_end_handler(guac_audio_stream* audio) {
-
-    /* Get state */
-    ogg_encoder_state* state = (ogg_encoder_state*) audio->data;
-
-    /* Write end-of-stream */
-    vorbis_analysis_wrote(&(state->vorbis_state), 0);
-    ogg_encoder_write_blocks(audio);
-
-    /* Clean up encoder */
-    ogg_stream_clear(&(state->ogg_state));
-    vorbis_block_clear(&(state->vorbis_block));
-    vorbis_dsp_clear(&(state->vorbis_state));
-    vorbis_comment_clear(&(state->comment));
-    vorbis_info_clear(&(state->info));
-
-    /* Free stream state */
-    free(audio->data);
-
-}
-
-void ogg_encoder_write_handler(guac_audio_stream* audio, 
-        const unsigned char* pcm_data, int length) {
-
-    /* Get state */
-    ogg_encoder_state* state = (ogg_encoder_state*) audio->data;
-
-    /* Calculate samples */
-    int samples = length / audio->channels * 8 / audio->bps;
-    int i;
-
-    /* Get buffer */
-    float** buffer = vorbis_analysis_buffer(&(state->vorbis_state), samples);
-
-    signed char* readbuffer = (signed char*) pcm_data;
-
-    for (i=0; i<samples; i++) {
-
-        /* FIXME: For now, assume 2 channels, 16-bit */
-        int left  = ((readbuffer[i*4+1]<<8)|(0x00ff&(int)readbuffer[i*4]));
-        int right = ((readbuffer[i*4+3]<<8)|(0x00ff&(int)readbuffer[i*4+2]));
-
-        /* Store sample in buffer */
-        buffer[0][i] = left  / 32768.f;
-        buffer[1][i] = right / 32768.f;
-
-    }
-
-    /* Submit data */
-    vorbis_analysis_wrote(&(state->vorbis_state), samples);
-
-    /* Write data */
-    ogg_encoder_write_blocks(audio);
-
-}
-
-/* Encoder handlers */
-guac_audio_encoder _ogg_encoder = {
-    .mimetype      = "audio/ogg",
-    .begin_handler = ogg_encoder_begin_handler,
-    .write_handler = ogg_encoder_write_handler,
-    .end_handler   = ogg_encoder_end_handler
-};
-
-/* Actual encoder */
-guac_audio_encoder* ogg_encoder = &_ogg_encoder;
-
diff --git a/src/libguac/ogg_encoder.h b/src/libguac/ogg_encoder.h
deleted file mode 100644
index 97d81eb..0000000
--- a/src/libguac/ogg_encoder.h
+++ /dev/null
@@ -1,67 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef __GUAC_OGG_ENCODER_H
-#define __GUAC_OGG_ENCODER_H
-
-#include <guacamole/audio.h>
-
-#include <vorbis/vorbisenc.h>
-
-typedef struct ogg_encoder_state {
-
-    /**
-     * Ogg state
-     */
-    ogg_stream_state ogg_state;
-    ogg_page ogg_page;
-    ogg_packet ogg_packet;
-
-    /**
-     * Vorbis state
-     */
-    vorbis_info info;
-    vorbis_comment comment;
-    vorbis_dsp_state vorbis_state;
-    vorbis_block vorbis_block;
-
-} ogg_encoder_state;
-
-extern guac_audio_encoder* ogg_encoder;
-
-#endif
-
diff --git a/src/libguac/palette.c b/src/libguac/palette.c
index b04d1ae..e3676cb 100644
--- a/src/libguac/palette.c
+++ b/src/libguac/palette.c
@@ -1,51 +1,35 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
-#include <inttypes.h>
+#include "config.h"
 
-#include <cairo/cairo.h>
+#include "palette.h"
 
-#include <sys/types.h>
+#include <cairo/cairo.h>
 
-#include "palette.h"
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 
 guac_palette* guac_palette_alloc(cairo_surface_t* surface) {
 
diff --git a/src/libguac/palette.h b/src/libguac/palette.h
index 7db3379..98d58d7 100644
--- a/src/libguac/palette.h
+++ b/src/libguac/palette.h
@@ -1,45 +1,31 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef __GUAC_PALETTE_H
 #define __GUAC_PALETTE_H
 
-#include <png.h>
 #include <cairo/cairo.h>
+#include <png.h>
 
 typedef struct guac_palette_entry {
 
@@ -48,7 +34,6 @@ typedef struct guac_palette_entry {
 
 } guac_palette_entry;
 
-
 typedef struct guac_palette {
 
     guac_palette_entry entries[0x1000];
diff --git a/src/libguac/plugin.c b/src/libguac/plugin.c
index 5bf5826..96845d4 100644
--- a/src/libguac/plugin.c
+++ b/src/libguac/plugin.c
@@ -1,52 +1,35 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <dlfcn.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include "config.h"
 
 #include "client.h"
-#include "client-handlers.h"
 #include "error.h"
 #include "plugin.h"
-#include "protocol.h"
-#include "socket.h"
-#include "time.h"
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 
 guac_client_plugin* guac_client_plugin_open(const char* protocol) {
 
@@ -74,7 +57,7 @@ guac_client_plugin* guac_client_plugin_open(const char* protocol) {
     /* Load client plugin */
     client_plugin_handle = dlopen(protocol_lib, RTLD_LAZY);
     if (!client_plugin_handle) {
-        guac_error = GUAC_STATUS_BAD_ARGUMENT;
+        guac_error = GUAC_STATUS_NOT_FOUND;
         guac_error_message = dlerror();
         return NULL;
     }
@@ -86,7 +69,7 @@ guac_client_plugin* guac_client_plugin_open(const char* protocol) {
 
     /* Fail if cannot find guac_client_init */
     if (dlerror() != NULL) {
-        guac_error = GUAC_STATUS_BAD_ARGUMENT;
+        guac_error = GUAC_STATUS_INTERNAL_ERROR;
         guac_error_message = dlerror();
         return NULL;
     }
@@ -96,7 +79,7 @@ guac_client_plugin* guac_client_plugin_open(const char* protocol) {
 
     /* Fail if cannot find GUAC_CLIENT_ARGS */
     if (dlerror() != NULL) {
-        guac_error = GUAC_STATUS_BAD_ARGUMENT;
+        guac_error = GUAC_STATUS_INTERNAL_ERROR;
         guac_error_message = dlerror();
         return NULL;
     }
@@ -121,7 +104,7 @@ int guac_client_plugin_close(guac_client_plugin* plugin) {
 
     /* Unload client plugin */
     if (dlclose(plugin->__client_plugin_handle)) {
-        guac_error = GUAC_STATUS_BAD_STATE;
+        guac_error = GUAC_STATUS_INTERNAL_ERROR;
         guac_error_message = dlerror();
         return -1;
     }
diff --git a/src/libguac/pool.c b/src/libguac/pool.c
index 8516761..37984e5 100644
--- a/src/libguac/pool.c
+++ b/src/libguac/pool.c
@@ -1,45 +1,31 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <stdlib.h>
-#include <string.h>
+#include "config.h"
 
 #include "pool.h"
 
+#include <stdlib.h>
+
 guac_pool* guac_pool_alloc(int size) {
 
     guac_pool* pool = malloc(sizeof(guac_pool));
@@ -50,6 +36,7 @@ guac_pool* guac_pool_alloc(int size) {
 
     /* Initialize empty pool */
     pool->min_size = size;
+    pool->active = 0;
     pool->__next_value = 0;
     pool->__head = NULL;
     pool->__tail = NULL;
@@ -79,7 +66,9 @@ int guac_pool_next_int(guac_pool* pool) {
 
     int value;
 
-    /* If more integers are needed, or we are out of integers, return a new one. */
+    pool->active++;
+
+    /* If more integers are needed, return a new one. */
     if (pool->__head == NULL || pool->__next_value < pool->min_size)
         return pool->__next_value++;
 
@@ -111,6 +100,8 @@ void guac_pool_free_int(guac_pool* pool, int value) {
     pool_int->value = value;
     pool_int->__next = NULL;
 
+    pool->active--;
+
     /* If pool empty, store as sole entry. */
     if (pool->__tail == NULL)
         pool->__head = pool->__tail = pool_int;
diff --git a/src/libguac/protocol.c b/src/libguac/protocol.c
index 4e2c4cf..f660181 100644
--- a/src/libguac/protocol.c
+++ b/src/libguac/protocol.c
@@ -1,64 +1,47 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <inttypes.h>
-#include <string.h>
-#include <errno.h>
-
-#ifdef HAVE_PNGSTRUCT_H
-#include <pngstruct.h>
-#endif
-
-#include <png.h>
-
-#include <cairo/cairo.h>
-
-#include <sys/types.h>
+#include "config.h"
 
 #include "error.h"
 #include "layer.h"
+#include "object.h"
 #include "palette.h"
 #include "protocol.h"
 #include "socket.h"
+#include "stream.h"
 #include "unicode.h"
 
+#include <cairo/cairo.h>
+
+#include <inttypes.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
 /* Output formatting functions */
 
 ssize_t __guac_socket_write_length_string(guac_socket* socket, const char* str) {
@@ -70,7 +53,6 @@ ssize_t __guac_socket_write_length_string(guac_socket* socket, const char* str)
 
 }
 
-
 ssize_t __guac_socket_write_length_int(guac_socket* socket, int64_t i) {
 
     char buffer[128];
@@ -79,7 +61,6 @@ ssize_t __guac_socket_write_length_int(guac_socket* socket, int64_t i) {
 
 }
 
-
 ssize_t __guac_socket_write_length_double(guac_socket* socket, double d) {
 
     char buffer[128];
@@ -88,273 +69,28 @@ ssize_t __guac_socket_write_length_double(guac_socket* socket, double d) {
 
 }
 
+/* Protocol functions */
 
-/* PNG output formatting */
-
-typedef struct __guac_socket_write_png_data {
-
-    guac_socket* socket;
-
-    char* buffer;
-    int buffer_size;
-    int data_size;
-
-} __guac_socket_write_png_data;
-
-cairo_status_t __guac_socket_write_png_cairo(void* closure, const unsigned char* data, unsigned int length) {
-
-    __guac_socket_write_png_data* png_data = (__guac_socket_write_png_data*) closure;
-
-    /* Calculate next buffer size */
-    int next_size = png_data->data_size + length;
-
-    /* If need resizing, double buffer size until big enough */
-    if (next_size > png_data->buffer_size) {
-
-        char* new_buffer;
-
-        do {
-            png_data->buffer_size <<= 1;
-        } while (next_size > png_data->buffer_size);
-
-        /* Resize buffer */
-        new_buffer = realloc(png_data->buffer, png_data->buffer_size);
-        png_data->buffer = new_buffer;
-
-    }
-
-    /* Append data to buffer */
-    memcpy(png_data->buffer + png_data->data_size, data, length);
-    png_data->data_size += length;
-
-    return CAIRO_STATUS_SUCCESS;
-
-}
-
-
-int __guac_socket_write_length_png_cairo(guac_socket* socket, cairo_surface_t* surface) {
-
-    __guac_socket_write_png_data png_data;
-    int base64_length;
-
-    /* Write surface */
-
-    png_data.socket = socket;
-    png_data.buffer_size = 8192;
-    png_data.buffer = malloc(png_data.buffer_size);
-    png_data.data_size = 0;
-
-    if (cairo_surface_write_to_png_stream(surface, __guac_socket_write_png_cairo, &png_data) != CAIRO_STATUS_SUCCESS) {
-        guac_error = GUAC_STATUS_OUTPUT_ERROR;
-        guac_error_message = "Cairo PNG backend failed";
-        return -1;
-    }
-
-    base64_length = (png_data.data_size + 2) / 3 * 4;
-
-    /* Write length and data */
-    if (
-           guac_socket_write_int(socket, base64_length)
-        || guac_socket_write_string(socket, ".")
-        || guac_socket_write_base64(socket, png_data.buffer, png_data.data_size)
-        || guac_socket_flush_base64(socket)) {
-        free(png_data.buffer);
-        return -1;
-    }
-
-    free(png_data.buffer);
-    return 0;
-
-}
-
-void __guac_socket_write_png(png_structp png,
-        png_bytep data, png_size_t length) {
-
-    /* Get png buffer structure */
-    __guac_socket_write_png_data* png_data;
-#ifdef HAVE_PNG_GET_IO_PTR
-    png_data = (__guac_socket_write_png_data*) png_get_io_ptr(png);
-#else
-    png_data = (__guac_socket_write_png_data*) png->io_ptr;
-#endif
-
-    /* Calculate next buffer size */
-    int next_size = png_data->data_size + length;
-
-    /* If need resizing, double buffer size until big enough */
-    if (next_size > png_data->buffer_size) {
-
-        char* new_buffer;
-
-        do {
-            png_data->buffer_size <<= 1;
-        } while (next_size > png_data->buffer_size);
-
-        /* Resize buffer */
-        new_buffer = realloc(png_data->buffer, png_data->buffer_size);
-        png_data->buffer = new_buffer;
-
-    }
-
-    /* Append data to buffer */
-    memcpy(png_data->buffer + png_data->data_size, data, length);
-    png_data->data_size += length;
-
-}
-
-void __guac_socket_flush_png(png_structp png) {
-    /* Dummy function */
-}
-
-int __guac_socket_write_length_png(guac_socket* socket, cairo_surface_t* surface) {
-
-    png_structp png;
-    png_infop png_info;
-    png_byte** png_rows;
-    int bpp;
-
-    int x, y;
-
-    __guac_socket_write_png_data png_data;
-    int base64_length;
-
-    /* Get image surface properties and data */
-    cairo_format_t format = cairo_image_surface_get_format(surface);
-    int width = cairo_image_surface_get_width(surface);
-    int height = cairo_image_surface_get_height(surface);
-    int stride = cairo_image_surface_get_stride(surface);
-    unsigned char* data = cairo_image_surface_get_data(surface);
-
-    /* If not RGB24, use Cairo PNG writer */
-    if (format != CAIRO_FORMAT_RGB24 || data == NULL)
-        return __guac_socket_write_length_png_cairo(socket, surface);
-
-    /* Flush pending operations to surface */
-    cairo_surface_flush(surface);
-
-    /* Attempt to build palette */
-    guac_palette* palette = guac_palette_alloc(surface);
-
-    /* If not possible, resort to Cairo PNG writer */
-    if (palette == NULL)
-        return __guac_socket_write_length_png_cairo(socket, surface);
-
-    /* Calculate BPP from palette size */
-    if      (palette->size <= 2)  bpp = 1;
-    else if (palette->size <= 4)  bpp = 2;
-    else if (palette->size <= 16) bpp = 4;
-    else                          bpp = 8;
-
-    /* Set up PNG writer */
-    png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
-    if (!png) {
-        guac_error = GUAC_STATUS_OUTPUT_ERROR;
-        guac_error_message = "libpng failed to create write structure";
-        return -1;
-    }
-
-    png_info = png_create_info_struct(png);
-    if (!png_info) {
-        png_destroy_write_struct(&png, NULL);
-        guac_error = GUAC_STATUS_OUTPUT_ERROR;
-        guac_error_message = "libpng failed to create info structure";
-        return -1;
-    }
-
-    /* Set error handler */
-    if (setjmp(png_jmpbuf(png))) {
-        png_destroy_write_struct(&png, &png_info);
-        guac_error = GUAC_STATUS_OUTPUT_ERROR;
-        guac_error_message = "libpng output error";
-        return -1;
-    }
-
-    /* Set up buffer structure */
-    png_data.socket = socket;
-    png_data.buffer_size = 8192;
-    png_data.buffer = malloc(png_data.buffer_size);
-    png_data.data_size = 0;
-
-    /* Set up writer */
-    png_set_write_fn(png, &png_data,
-            __guac_socket_write_png,
-            __guac_socket_flush_png);
-
-    /* Copy data from surface into PNG data */
-    png_rows = (png_byte**) malloc(sizeof(png_byte*) * height);
-    for (y=0; y<height; y++) {
-
-        /* Allocate new PNG row */
-        png_byte* row = (png_byte*) malloc(sizeof(png_byte) * width);
-        png_rows[y] = row;
-
-        /* Copy data from surface into current row */
-        for (x=0; x<width; x++) {
-
-            /* Get pixel color */
-            int color = ((uint32_t*) data)[x] & 0xFFFFFF;
-
-            /* Set index in row */
-            row[x] = guac_palette_find(palette, color);
-
-        }
-
-        /* Advance to next data row */
-        data += stride;
+int guac_protocol_send_ack(guac_socket* socket, guac_stream* stream,
+        const char* error, guac_protocol_status status) {
 
-    }
+    int ret_val;
 
-    /* Write image info */
-    png_set_IHDR(
-        png,
-        png_info,
-        width,
-        height,
-        bpp,
-        PNG_COLOR_TYPE_PALETTE,
-        PNG_INTERLACE_NONE,
-        PNG_COMPRESSION_TYPE_DEFAULT,
-        PNG_FILTER_TYPE_DEFAULT
-    );
-
-    /* Write palette */
-    png_set_PLTE(png, png_info, palette->colors, palette->size);
-
-    /* Write image */
-    png_set_rows(png, png_info, png_rows);
-    png_write_png(png, png_info, PNG_TRANSFORM_PACKING, NULL);
-
-    /* Finish write */
-    png_destroy_write_struct(&png, &png_info);
-
-    /* Free palette */
-    guac_palette_free(palette);
-
-    /* Free PNG data */
-    for (y=0; y<height; y++)
-        free(png_rows[y]);
-    free(png_rows);
-
-    base64_length = (png_data.data_size + 2) / 3 * 4;
-
-    /* Write length and data */
-    if (
-           guac_socket_write_int(socket, base64_length)
-        || guac_socket_write_string(socket, ".")
-        || guac_socket_write_base64(socket, png_data.buffer, png_data.data_size)
-        || guac_socket_flush_base64(socket)) {
-        free(png_data.buffer);
-        return -1;
-    }
+    guac_socket_instruction_begin(socket);
+    ret_val =
+           guac_socket_write_string(socket, "3.ack,")
+        || __guac_socket_write_length_int(socket, stream->index)
+        || guac_socket_write_string(socket, ",")
+        || __guac_socket_write_length_string(socket, error)
+        || guac_socket_write_string(socket, ",")
+        || __guac_socket_write_length_int(socket, status)
+        || guac_socket_write_string(socket, ";");
 
-    free(png_data.buffer);
-    return 0;
+    guac_socket_instruction_end(socket);
+    return ret_val;
 
 }
 
-
-/* Protocol functions */
-
 static int __guac_protocol_send_args(guac_socket* socket, const char** args) {
 
     int i;
@@ -416,55 +152,26 @@ int guac_protocol_send_arc(guac_socket* socket, const guac_layer* layer,
 
 }
 
-int guac_protocol_send_audio(guac_socket* socket, int channel,
-        const char* mimetype, double duration, void* data, int size) {
-
-    /* By the spec, guac_protocol_send_audio_end() must be called */
-    return
-           (guac_protocol_send_audio_header(socket, channel,
-                mimetype, duration, size)
-        || guac_protocol_send_audio_data(socket, data, size))
-        |  guac_protocol_send_audio_end(socket);
-
-}
-
-int guac_protocol_send_audio_header(guac_socket* socket,
-        int channel, const char* mimetype, double duration, int size) {
+int guac_protocol_send_audio(guac_socket* socket, const guac_stream* stream,
+        const char* mimetype) {
 
-    int base64_length = (size + 2) / 3 * 4;
+    int ret_val;
 
     guac_socket_instruction_begin(socket);
-    return 
+    ret_val = 
            guac_socket_write_string(socket, "5.audio,")
-        || __guac_socket_write_length_int(socket, channel)
+        || __guac_socket_write_length_int(socket, stream->index)
         || guac_socket_write_string(socket, ",")
         || __guac_socket_write_length_string(socket, mimetype)
-        || guac_socket_write_string(socket, ",")
-        || __guac_socket_write_length_double(socket, duration)
-        || guac_socket_write_string(socket, ",")
-        || guac_socket_write_int(socket, base64_length)
-        || guac_socket_write_string(socket, ".");
-
-}
-
-int guac_protocol_send_audio_data(guac_socket* socket, void* data, int count) {
-
-    return guac_socket_write_base64(socket, data, count);
-
-}
-
-int guac_protocol_send_audio_end(guac_socket* socket) {
-
-    int ret_val =
-           guac_socket_flush_base64(socket)
         || guac_socket_write_string(socket, ";");
-
     guac_socket_instruction_end(socket);
+
     return ret_val;
 
 }
 
-int guac_protocol_send_blob(guac_socket* socket, int index, void* data, int count) {
+int guac_protocol_send_blob(guac_socket* socket, const guac_stream* stream,
+        const void* data, int count) {
 
     int base64_length = (count + 2) / 3 * 4;
 
@@ -473,7 +180,7 @@ int guac_protocol_send_blob(guac_socket* socket, int index, void* data, int coun
     guac_socket_instruction_begin(socket);
     ret_val =
            guac_socket_write_string(socket, "4.blob,")
-        || __guac_socket_write_length_int(socket, index)
+        || __guac_socket_write_length_int(socket, stream->index)
         || guac_socket_write_string(socket, ",")
         || guac_socket_write_int(socket, base64_length)
         || guac_socket_write_string(socket, ".")
@@ -486,6 +193,28 @@ int guac_protocol_send_blob(guac_socket* socket, int index, void* data, int coun
 
 }
 
+int guac_protocol_send_body(guac_socket* socket, const guac_object* object,
+        const guac_stream* stream, const char* mimetype, const char* name) {
+
+    int ret_val;
+
+    guac_socket_instruction_begin(socket);
+    ret_val =
+           guac_socket_write_string(socket, "4.body,")
+        || __guac_socket_write_length_int(socket, object->index)
+        || guac_socket_write_string(socket, ",")
+        || __guac_socket_write_length_int(socket, stream->index)
+        || guac_socket_write_string(socket, ",")
+        || __guac_socket_write_length_string(socket, mimetype)
+        || guac_socket_write_string(socket, ",")
+        || __guac_socket_write_length_string(socket, name)
+        || guac_socket_write_string(socket, ";");
+
+    guac_socket_instruction_end(socket);
+    return ret_val;
+
+}
+
 int guac_protocol_send_cfill(guac_socket* socket,
         guac_composite_mode mode, const guac_layer* layer,
         int r, int g, int b, int a) {
@@ -513,7 +242,6 @@ int guac_protocol_send_cfill(guac_socket* socket,
 
 }
 
-
 int guac_protocol_send_close(guac_socket* socket, const guac_layer* layer) {
 
     int ret_val;
@@ -529,7 +257,6 @@ int guac_protocol_send_close(guac_socket* socket, const guac_layer* layer) {
 
 }
 
-
 static int __guac_protocol_send_connect(guac_socket* socket, const char** args) {
 
     int i;
@@ -577,15 +304,17 @@ int guac_protocol_send_clip(guac_socket* socket, const guac_layer* layer) {
 
 }
 
-
-int guac_protocol_send_clipboard(guac_socket* socket, const char* data) {
+int guac_protocol_send_clipboard(guac_socket* socket, const guac_stream* stream,
+        const char* mimetype) {
 
     int ret_val;
 
     guac_socket_instruction_begin(socket);
     ret_val =
            guac_socket_write_string(socket, "9.clipboard,")
-        || __guac_socket_write_length_string(socket, data)
+        || __guac_socket_write_length_int(socket, stream->index)
+        || guac_socket_write_string(socket, ",")
+        || __guac_socket_write_length_string(socket, mimetype)
         || guac_socket_write_string(socket, ";");
 
     guac_socket_instruction_end(socket);
@@ -593,7 +322,6 @@ int guac_protocol_send_clipboard(guac_socket* socket, const char* data) {
 
 }
 
-
 int guac_protocol_send_copy(guac_socket* socket,
         const guac_layer* srcl, int srcx, int srcy, int w, int h,
         guac_composite_mode mode, const guac_layer* dstl, int dstx, int dsty) {
@@ -627,7 +355,6 @@ int guac_protocol_send_copy(guac_socket* socket,
 
 }
 
-
 int guac_protocol_send_cstroke(guac_socket* socket,
         guac_composite_mode mode, const guac_layer* layer,
         guac_line_cap_style cap, guac_line_join_style join, int thickness,
@@ -662,7 +389,6 @@ int guac_protocol_send_cstroke(guac_socket* socket,
 
 }
 
-
 int guac_protocol_send_cursor(guac_socket* socket, int x, int y,
         const guac_layer* srcl, int srcx, int srcy, int w, int h) {
     int ret_val;
@@ -690,7 +416,6 @@ int guac_protocol_send_cursor(guac_socket* socket, int x, int y,
 
 }
 
-
 int guac_protocol_send_curve(guac_socket* socket, const guac_layer* layer,
         int cp1x, int cp1y, int cp2x, int cp2y, int x, int y) {
 
@@ -719,7 +444,6 @@ int guac_protocol_send_curve(guac_socket* socket, const guac_layer* layer,
 
 }
 
-
 int guac_protocol_send_disconnect(guac_socket* socket) {
     int ret_val;
 
@@ -730,7 +454,6 @@ int guac_protocol_send_disconnect(guac_socket* socket) {
 
 }
 
-
 int guac_protocol_send_dispose(guac_socket* socket, const guac_layer* layer) {
 
     int ret_val;
@@ -746,7 +469,6 @@ int guac_protocol_send_dispose(guac_socket* socket, const guac_layer* layer) {
 
 }
 
-
 int guac_protocol_send_distort(guac_socket* socket, const guac_layer* layer,
         double a, double b, double c,
         double d, double e, double f) {
@@ -776,15 +498,14 @@ int guac_protocol_send_distort(guac_socket* socket, const guac_layer* layer,
 
 }
 
-
-int guac_protocol_send_end(guac_socket* socket, int index) {
+int guac_protocol_send_end(guac_socket* socket, const guac_stream* stream) {
 
     int ret_val;
 
     guac_socket_instruction_begin(socket);
     ret_val =
            guac_socket_write_string(socket, "3.end,")
-        || __guac_socket_write_length_int(socket, index)
+        || __guac_socket_write_length_int(socket, stream->index)
         || guac_socket_write_string(socket, ";");
 
     guac_socket_instruction_end(socket);
@@ -792,8 +513,8 @@ int guac_protocol_send_end(guac_socket* socket, int index) {
 
 }
 
-
-int guac_protocol_send_error(guac_socket* socket, const char* error) {
+int guac_protocol_send_error(guac_socket* socket, const char* error,
+        guac_protocol_status status) {
 
     int ret_val;
 
@@ -801,6 +522,8 @@ int guac_protocol_send_error(guac_socket* socket, const char* error) {
     ret_val =
            guac_socket_write_string(socket, "5.error,")
         || __guac_socket_write_length_string(socket, error)
+        || guac_socket_write_string(socket, ",")
+        || __guac_socket_write_length_int(socket, status)
         || guac_socket_write_string(socket, ";");
 
     guac_socket_instruction_end(socket);
@@ -808,15 +531,49 @@ int guac_protocol_send_error(guac_socket* socket, const char* error) {
 
 }
 
+int vguac_protocol_send_log(guac_socket* socket, const char* format,
+        va_list args) {
+
+    int ret_val;
+
+    /* Copy log message into buffer */
+    char message[4096];
+    vsnprintf(message, sizeof(message), format, args);
 
-int guac_protocol_send_file(guac_socket* socket, int index, const char* mimetype, const char* name) {
+    /* Log to instruction */
+    guac_socket_instruction_begin(socket);
+    ret_val =
+           guac_socket_write_string(socket, "3.log,")
+        || __guac_socket_write_length_string(socket, message)
+        || guac_socket_write_string(socket, ";");
+
+    guac_socket_instruction_end(socket);
+    return ret_val;
+
+}
+
+int guac_protocol_send_log(guac_socket* socket, const char* format, ...) {
+
+    int ret_val;
+
+    va_list args;
+    va_start(args, format);
+    ret_val = vguac_protocol_send_log(socket, format, args);
+    va_end(args);
+
+    return ret_val;
+
+}
+
+int guac_protocol_send_file(guac_socket* socket, const guac_stream* stream,
+        const char* mimetype, const char* name) {
 
     int ret_val;
 
     guac_socket_instruction_begin(socket);
     ret_val =
            guac_socket_write_string(socket, "4.file,")
-        || __guac_socket_write_length_int(socket, index)
+        || __guac_socket_write_length_int(socket, stream->index)
         || guac_socket_write_string(socket, ",")
         || __guac_socket_write_length_string(socket, mimetype)
         || guac_socket_write_string(socket, ",")
@@ -828,6 +585,23 @@ int guac_protocol_send_file(guac_socket* socket, int index, const char* mimetype
 
 }
 
+int guac_protocol_send_filesystem(guac_socket* socket,
+        const guac_object* object, const char* name) {
+
+    int ret_val;
+
+    guac_socket_instruction_begin(socket);
+    ret_val =
+           guac_socket_write_string(socket, "10.filesystem,")
+        || __guac_socket_write_length_int(socket, object->index)
+        || guac_socket_write_string(socket, ",")
+        || __guac_socket_write_length_string(socket, name)
+        || guac_socket_write_string(socket, ";");
+
+    guac_socket_instruction_end(socket);
+    return ret_val;
+
+}
 
 int guac_protocol_send_identity(guac_socket* socket, const guac_layer* layer) {
 
@@ -844,7 +618,6 @@ int guac_protocol_send_identity(guac_socket* socket, const guac_layer* layer) {
 
 }
 
-
 int guac_protocol_send_lfill(guac_socket* socket,
         guac_composite_mode mode, const guac_layer* layer,
         const guac_layer* srcl) {
@@ -866,7 +639,6 @@ int guac_protocol_send_lfill(guac_socket* socket,
 
 }
 
-
 int guac_protocol_send_line(guac_socket* socket, const guac_layer* layer,
         int x, int y) {
 
@@ -887,7 +659,6 @@ int guac_protocol_send_line(guac_socket* socket, const guac_layer* layer,
 
 }
 
-
 int guac_protocol_send_lstroke(guac_socket* socket,
         guac_composite_mode mode, const guac_layer* layer,
         guac_line_cap_style cap, guac_line_join_style join, int thickness,
@@ -916,7 +687,6 @@ int guac_protocol_send_lstroke(guac_socket* socket,
 
 }
 
-
 int guac_protocol_send_move(guac_socket* socket, const guac_layer* layer,
         const guac_layer* parent, int x, int y, int z) {
 
@@ -941,7 +711,6 @@ int guac_protocol_send_move(guac_socket* socket, const guac_layer* layer,
 
 }
 
-
 int guac_protocol_send_name(guac_socket* socket, const char* name) {
 
     int ret_val;
@@ -975,23 +744,58 @@ int guac_protocol_send_nest(guac_socket* socket, int index,
 
 }
 
-int guac_protocol_send_png(guac_socket* socket, guac_composite_mode mode,
-        const guac_layer* layer, int x, int y, cairo_surface_t* surface) {
+int guac_protocol_send_nop(guac_socket* socket) {
+
+    int ret_val;
+
+    guac_socket_instruction_begin(socket);
+    ret_val = guac_socket_write_string(socket, "3.nop;");
+    guac_socket_instruction_end(socket);
+
+    return ret_val;
+
+}
+
+int guac_protocol_send_pipe(guac_socket* socket, const guac_stream* stream,
+        const char* mimetype, const char* name) {
+
+    int ret_val;
+
+    guac_socket_instruction_begin(socket);
+    ret_val =
+           guac_socket_write_string(socket, "4.pipe,")
+        || __guac_socket_write_length_int(socket, stream->index)
+        || guac_socket_write_string(socket, ",")
+        || __guac_socket_write_length_string(socket, mimetype)
+        || guac_socket_write_string(socket, ",")
+        || __guac_socket_write_length_string(socket, name)
+        || guac_socket_write_string(socket, ";");
+
+    guac_socket_instruction_end(socket);
+    return ret_val;
+
+}
+
+int guac_protocol_send_img(guac_socket* socket, const guac_stream* stream,
+        guac_composite_mode mode, const guac_layer* layer,
+        const char* mimetype, int x, int y) {
 
     int ret_val;
 
     guac_socket_instruction_begin(socket);
     ret_val =
-           guac_socket_write_string(socket, "3.png,")
+           guac_socket_write_string(socket, "3.img,")
+        || __guac_socket_write_length_int(socket, stream->index)
+        || guac_socket_write_string(socket, ",")
         || __guac_socket_write_length_int(socket, mode)
         || guac_socket_write_string(socket, ",")
         || __guac_socket_write_length_int(socket, layer->index)
         || guac_socket_write_string(socket, ",")
+        || __guac_socket_write_length_string(socket, mimetype)
+        || guac_socket_write_string(socket, ",")
         || __guac_socket_write_length_int(socket, x)
         || guac_socket_write_string(socket, ",")
         || __guac_socket_write_length_int(socket, y)
-        || guac_socket_write_string(socket, ",")
-        || __guac_socket_write_length_png(socket, surface)
         || guac_socket_write_string(socket, ";");
 
     guac_socket_instruction_end(socket);
@@ -999,7 +803,6 @@ int guac_protocol_send_png(guac_socket* socket, guac_composite_mode mode,
 
 }
 
-
 int guac_protocol_send_pop(guac_socket* socket, const guac_layer* layer) {
 
     int ret_val;
@@ -1015,7 +818,6 @@ int guac_protocol_send_pop(guac_socket* socket, const guac_layer* layer) {
 
 }
 
-
 int guac_protocol_send_push(guac_socket* socket, const guac_layer* layer) {
 
     int ret_val;
@@ -1031,6 +833,20 @@ int guac_protocol_send_push(guac_socket* socket, const guac_layer* layer) {
 
 }
 
+int guac_protocol_send_ready(guac_socket* socket, const char* id) {
+
+    int ret_val;
+
+    guac_socket_instruction_begin(socket);
+    ret_val =
+           guac_socket_write_string(socket, "5.ready,")
+        || __guac_socket_write_length_string(socket, id)
+        || guac_socket_write_string(socket, ";");
+
+    guac_socket_instruction_end(socket);
+    return ret_val;
+
+}
 
 int guac_protocol_send_rect(guac_socket* socket,
         const guac_layer* layer, int x, int y, int width, int height) {
@@ -1056,7 +872,6 @@ int guac_protocol_send_rect(guac_socket* socket,
 
 }
 
-
 int guac_protocol_send_reset(guac_socket* socket, const guac_layer* layer) {
 
     int ret_val;
@@ -1072,7 +887,6 @@ int guac_protocol_send_reset(guac_socket* socket, const guac_layer* layer) {
 
 }
 
-
 int guac_protocol_send_set(guac_socket* socket, const guac_layer* layer,
         const char* name, const char* value) {
 
@@ -1093,7 +907,6 @@ int guac_protocol_send_set(guac_socket* socket, const guac_layer* layer,
 
 }
 
-
 int guac_protocol_send_select(guac_socket* socket, const char* protocol) {
 
     int ret_val;
@@ -1109,7 +922,6 @@ int guac_protocol_send_select(guac_socket* socket, const char* protocol) {
 
 }
 
-
 int guac_protocol_send_shade(guac_socket* socket, const guac_layer* layer,
         int a) {
 
@@ -1128,7 +940,6 @@ int guac_protocol_send_shade(guac_socket* socket, const guac_layer* layer,
 
 }
 
-
 int guac_protocol_send_size(guac_socket* socket, const guac_layer* layer,
         int w, int h) {
 
@@ -1149,7 +960,6 @@ int guac_protocol_send_size(guac_socket* socket, const guac_layer* layer,
 
 }
 
-
 int guac_protocol_send_start(guac_socket* socket, const guac_layer* layer,
         int x, int y) {
 
@@ -1170,7 +980,6 @@ int guac_protocol_send_start(guac_socket* socket, const guac_layer* layer,
 
 }
 
-
 int guac_protocol_send_sync(guac_socket* socket, guac_timestamp timestamp) {
 
     int ret_val;
@@ -1186,7 +995,6 @@ int guac_protocol_send_sync(guac_socket* socket, guac_timestamp timestamp) {
 
 }
 
-
 int guac_protocol_send_transfer(guac_socket* socket,
         const guac_layer* srcl, int srcx, int srcy, int w, int h,
         guac_transfer_function fn, const guac_layer* dstl, int dstx, int dsty) {
@@ -1220,7 +1028,6 @@ int guac_protocol_send_transfer(guac_socket* socket,
 
 }
 
-
 int guac_protocol_send_transform(guac_socket* socket, const guac_layer* layer,
         double a, double b, double c,
         double d, double e, double f) {
@@ -1250,51 +1057,98 @@ int guac_protocol_send_transform(guac_socket* socket, const guac_layer* layer,
 
 }
 
-int guac_protocol_send_video(guac_socket* socket, const guac_layer* layer,
-        const char* mimetype, double duration, void* data, int size) {
+int guac_protocol_send_undefine(guac_socket* socket,
+        const guac_object* object) {
 
-    /* By the spec, guac_protocol_send_video_end() must be called */
-    return
-           (guac_protocol_send_video_header(socket, layer,
-                mimetype, duration, size)
-        || guac_protocol_send_video_data(socket, data, size))
-        |  guac_protocol_send_video_end(socket);
+    int ret_val;
+
+    guac_socket_instruction_begin(socket);
+    ret_val =
+           guac_socket_write_string(socket, "8.undefine,")
+        || __guac_socket_write_length_int(socket, object->index)
+        || guac_socket_write_string(socket, ";");
+
+    guac_socket_instruction_end(socket);
+    return ret_val;
 
 }
 
-int guac_protocol_send_video_header(guac_socket* socket,
-        const guac_layer* layer, const char* mimetype, double duration, int size) {
+int guac_protocol_send_video(guac_socket* socket, const guac_stream* stream,
+        const guac_layer* layer, const char* mimetype) {
 
-    int base64_length = (size + 2) / 3 * 4;
+    int ret_val;
 
     guac_socket_instruction_begin(socket);
-    return
+    ret_val = 
            guac_socket_write_string(socket, "5.video,")
+        || __guac_socket_write_length_int(socket, stream->index)
+        || guac_socket_write_string(socket, ",")
         || __guac_socket_write_length_int(socket, layer->index)
         || guac_socket_write_string(socket, ",")
         || __guac_socket_write_length_string(socket, mimetype)
-        || guac_socket_write_string(socket, ",")
-        || __guac_socket_write_length_double(socket, duration)
-        || guac_socket_write_string(socket, ",")
-        || guac_socket_write_int(socket, base64_length)
-        || guac_socket_write_string(socket, ".");
+        || guac_socket_write_string(socket, ";");
+    guac_socket_instruction_end(socket);
+
+    return ret_val;
 
 }
 
-int guac_protocol_send_video_data(guac_socket* socket, void* data, int count) {
+/**
+ * Returns the value of a single base64 character.
+ */
+static int __guac_base64_value(char c) {
+
+    if (c >= 'A' && c <= 'Z')
+        return c - 'A';
+
+    if (c >= 'a' && c <= 'z')
+        return c - 'a' + 26;
+
+    if (c >= '0' && c <= '9')
+        return c - '0' + 52;
+
+    if (c == '+')
+        return 62;
 
-    return guac_socket_write_base64(socket, data, count);
+    if (c == '/')
+        return 63;
+
+    return 0;
 
 }
 
-int guac_protocol_send_video_end(guac_socket* socket) {
+int guac_protocol_decode_base64(char* base64) {
 
-    int ret_val =
-           guac_socket_flush_base64(socket)
-        || guac_socket_write_string(socket, ";");
+    char* input = base64;
+    char* output = base64;
 
-    guac_socket_instruction_end(socket);
-    return ret_val;
+    int length = 0;
+    int bits_read = 0;
+    int value = 0;
+    char current;
+
+    /* For all characters in string */
+    while ((current = *(input++)) != 0) {
+
+        /* If we've reached padding, then we're done */
+        if (current == '=')
+            break;
+
+        /* Otherwise, shift on the latest 6 bits */
+        value = (value << 6) | __guac_base64_value(current);
+        bits_read += 6;
+
+        /* If we have at least one byte, write out the latest whole byte */
+        if (bits_read >= 8) {
+            *(output++) = (value >> (bits_read % 8)) & 0xFF;
+            bits_read -= 8;
+            length++;
+        }
+
+    }
+
+    /* Return number of bytes written */
+    return length;
 
 }
 
diff --git a/src/libguac/raw_encoder.c b/src/libguac/raw_encoder.c
new file mode 100644
index 0000000..91efdc0
--- /dev/null
+++ b/src/libguac/raw_encoder.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "audio.h"
+#include "raw_encoder.h"
+
+#include <guacamole/audio.h>
+#include <guacamole/client.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+static void raw_encoder_begin_handler(guac_audio_stream* audio) {
+
+    raw_encoder_state* state;
+    char mimetype[256];
+
+    /* Produce mimetype string from format info */
+    snprintf(mimetype, sizeof(mimetype), "audio/L%i;rate=%i,channels=%i",
+            audio->bps, audio->rate, audio->channels);
+
+    /* Associate stream */
+    guac_protocol_send_audio(audio->client->socket, audio->stream, mimetype);
+
+    /* Allocate and init encoder state */
+    audio->data = state = malloc(sizeof(raw_encoder_state));
+    state->written = 0;
+    state->length = GUAC_RAW_ENCODER_BUFFER_SIZE
+                    * audio->rate * audio->channels * audio->bps
+                    / 8 / 1000;
+
+    state->buffer = malloc(state->length);
+
+    guac_client_log(audio->client, GUAC_LOG_DEBUG,
+            "Using raw encoder (%s) with a %i byte buffer.",
+            mimetype, state->length);
+
+}
+
+static void raw_encoder_end_handler(guac_audio_stream* audio) {
+
+    raw_encoder_state* state = (raw_encoder_state*) audio->data;
+
+    /* Send end of stream */
+    guac_protocol_send_end(audio->client->socket, audio->stream);
+
+    /* Free state information */
+    free(state->buffer);
+    free(state);
+
+}
+
+static void raw_encoder_write_handler(guac_audio_stream* audio, 
+        const unsigned char* pcm_data, int length) {
+
+    raw_encoder_state* state = (raw_encoder_state*) audio->data;
+
+    while (length > 0) {
+
+        /* Prefer to copy a chunk of equal size to available buffer space */
+        int chunk_size = state->length - state->written;
+
+        /* If no space remains, flush and retry */
+        if (chunk_size == 0) {
+            guac_audio_stream_flush(audio);
+            continue;
+        }
+
+        /* Do not copy more data than is available in source PCM */
+        if (chunk_size > length)
+            chunk_size = length;
+
+        /* Copy block of PCM data into buffer */
+        memcpy(state->buffer + state->written, pcm_data, chunk_size);
+
+        /* Advance to next block */
+        state->written += chunk_size;
+        pcm_data += chunk_size;
+        length -= chunk_size;
+
+    }
+
+}
+
+static void raw_encoder_flush_handler(guac_audio_stream* audio) {
+
+    raw_encoder_state* state = (raw_encoder_state*) audio->data;
+    guac_socket* socket = audio->client->socket;
+    guac_stream* stream = audio->stream;
+
+    unsigned char* current = state->buffer;
+    int remaining = state->written;
+
+    /* Flush all data in buffer as blobs */
+    while (remaining > 0) {
+
+        /* Determine size of blob to be written */
+        int chunk_size = remaining;
+        if (chunk_size > 6048)
+            chunk_size = 6048;
+
+        /* Send audio data */
+        guac_protocol_send_blob(socket, stream, current, chunk_size);
+
+        /* Advance to next blob */
+        current += chunk_size;
+        remaining -= chunk_size;
+
+    }
+
+    /* All data has been flushed */
+    state->written = 0;
+
+}
+
+/* 8-bit raw encoder handlers */
+guac_audio_encoder _raw8_encoder = {
+    .mimetype      = "audio/L8",
+    .begin_handler = raw_encoder_begin_handler,
+    .write_handler = raw_encoder_write_handler,
+    .flush_handler = raw_encoder_flush_handler,
+    .end_handler   = raw_encoder_end_handler
+};
+
+/* 16-bit raw encoder handlers */
+guac_audio_encoder _raw16_encoder = {
+    .mimetype      = "audio/L16",
+    .begin_handler = raw_encoder_begin_handler,
+    .write_handler = raw_encoder_write_handler,
+    .flush_handler = raw_encoder_flush_handler,
+    .end_handler   = raw_encoder_end_handler
+};
+
+/* Actual encoder definitions */
+guac_audio_encoder* raw8_encoder  = &_raw8_encoder;
+guac_audio_encoder* raw16_encoder = &_raw16_encoder;
+
diff --git a/src/libguac/raw_encoder.h b/src/libguac/raw_encoder.h
new file mode 100644
index 0000000..2ebbf06
--- /dev/null
+++ b/src/libguac/raw_encoder.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef GUAC_RAW_ENCODER_H
+#define GUAC_RAW_ENCODER_H
+
+#include "config.h"
+
+#include "audio.h"
+
+/**
+ * The number of bytes to send in each audio blob.
+ */
+#define GUAC_RAW_ENCODER_BLOB_SIZE 6048
+
+/**
+ * The size of the raw encoder output PCM buffer, in milliseconds. The
+ * equivalent size in bytes will vary by PCM rate, number of channels, and bits
+ * per sample.
+ */
+#define GUAC_RAW_ENCODER_BUFFER_SIZE 250
+
+/**
+ * The current state of the raw encoder. The raw encoder performs very minimal
+ * processing, buffering provided PCM data only as necessary to ensure audio
+ * packet sizes are reasonable.
+ */
+typedef struct raw_encoder_state {
+
+    /**
+     * Buffer of not-yet-written raw PCM data.
+     */
+    unsigned char* buffer;
+
+    /**
+     * Size of the PCM buffer, in bytes.
+     */
+    int length;
+
+    /**
+     * The current number of bytes stored within the PCM buffer.
+     */
+    int written;
+
+} raw_encoder_state;
+
+/**
+ * Audio encoder which writes 8-bit raw PCM (one byte per sample).
+ */
+extern guac_audio_encoder* raw8_encoder;
+
+/**
+ * Audio encoder which writes 16-bit raw PCM (two bytes per sample).
+ */
+extern guac_audio_encoder* raw16_encoder;
+
+#endif
+
diff --git a/src/libguac/socket-fd.c b/src/libguac/socket-fd.c
index 3bb2ddf..13c66ed 100644
--- a/src/libguac/socket-fd.c
+++ b/src/libguac/socket-fd.c
@@ -1,48 +1,35 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
- * David PHAM-VAN <d.pham-van at ulteo.com> Ulteo SAS - http://www.ulteo.com
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "error.h"
+#include "socket.h"
 
+#include <stddef.h>
+#include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
+#include <sys/time.h>
 #include <unistd.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <inttypes.h>
 
 #ifdef __MINGW32__
 #include <winsock2.h>
@@ -50,12 +37,6 @@
 #include <sys/select.h>
 #endif
 
-#include <time.h>
-#include <sys/time.h>
-
-#include "socket.h"
-#include "error.h"
-
 typedef struct __guac_socket_fd_data {
 
     int fd;
@@ -103,7 +84,6 @@ ssize_t __guac_socket_fd_write_handler(guac_socket* socket,
     return retval;
 }
 
-
 int __guac_socket_fd_select_handler(guac_socket* socket, int usec_timeout) {
 
     __guac_socket_fd_data* data = (__guac_socket_fd_data*) socket->data;
@@ -134,7 +114,7 @@ int __guac_socket_fd_select_handler(guac_socket* socket, int usec_timeout) {
     }
 
     if (retval == 0) {
-        guac_error = GUAC_STATUS_INPUT_TIMEOUT;
+        guac_error = GUAC_STATUS_TIMEOUT;
         guac_error_message = "Timeout while waiting for data on socket";
     }
 
diff --git a/src/libguac/socket-nest.c b/src/libguac/socket-nest.c
index c6aa427..3b3b313 100644
--- a/src/libguac/socket-nest.c
+++ b/src/libguac/socket-nest.c
@@ -1,63 +1,35 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
- * David PHAM-VAN <d.pham-van at ulteo.com> Ulteo SAS - http://www.ulteo.com
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <inttypes.h>
-
-#ifdef __MINGW32__
-#include <winsock2.h>
-#else
-#include <sys/select.h>
-#endif
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <time.h>
-#include <sys/time.h>
+#include "config.h"
 
-#include "socket.h"
 #include "protocol.h"
-#include "error.h"
+#include "socket.h"
 #include "unicode.h"
 
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+
 #define GUAC_SOCKET_NEST_BUFFER_SIZE 8192
 
 typedef struct __guac_socket_nest_data {
diff --git a/src/libguac/socket.c b/src/libguac/socket.c
index e25c5f2..e078679 100644
--- a/src/libguac/socket.c
+++ b/src/libguac/socket.c
@@ -1,61 +1,40 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Original Code is libguac.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Contributor(s):
- * David PHAM-VAN <d.pham-van at ulteo.com> Ulteo SAS - http://www.ulteo.com
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "error.h"
+#include "protocol.h"
+#include "socket.h"
+#include "timestamp.h"
 
-#include <fcntl.h>
 #include <inttypes.h>
 #include <pthread.h>
+#include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#ifdef __MINGW32__
-#include <winsock2.h>
-#else
-#include <sys/select.h>
-#endif
-
 #include <time.h>
-#include <sys/time.h>
-
-#include "socket.h"
-#include "error.h"
+#include <unistd.h>
 
 char __guac_socket_BASE64_CHARACTERS[64] = {
     'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
@@ -65,9 +44,44 @@ char __guac_socket_BASE64_CHARACTERS[64] = {
     '8', '9', '+', '/'
 };
 
+static void* __guac_socket_keep_alive_thread(void* data) {
+
+    /* Calculate sleep interval */
+    struct timespec interval;
+    interval.tv_sec  =  GUAC_SOCKET_KEEP_ALIVE_INTERVAL / 1000;
+    interval.tv_nsec = (GUAC_SOCKET_KEEP_ALIVE_INTERVAL % 1000) * 1000000L;
+
+    /* Socket keep-alive loop */
+    guac_socket* socket = (guac_socket*) data;
+    while (socket->state == GUAC_SOCKET_OPEN) {
+
+        /* Send NOP keep-alive if it's been a while since the last output */
+        guac_timestamp timestamp = guac_timestamp_current();
+        if (timestamp - socket->last_write_timestamp >
+                GUAC_SOCKET_KEEP_ALIVE_INTERVAL) {
+
+            /* Send NOP */
+            if (guac_protocol_send_nop(socket)
+                || guac_socket_flush(socket))
+                break;
+
+        }
+
+        /* Sleep until next keep-alive check */
+        nanosleep(&interval, NULL);
+
+    }
+
+    return NULL;
+
+}
+
 static ssize_t __guac_socket_write(guac_socket* socket,
         const void* buf, size_t count) {
 
+    /* Update timestamp of last write */
+    socket->last_write_timestamp = guac_timestamp_current();
+
     /* If handler defined, call it. */
     if (socket->write_handler)
         return socket->write_handler(socket, buf, count);
@@ -122,7 +136,6 @@ int guac_socket_select(guac_socket* socket, int usec_timeout) {
 
 }
 
-
 guac_socket* guac_socket_alloc() {
 
     pthread_mutexattr_t lock_attributes;
@@ -138,27 +151,19 @@ guac_socket* guac_socket_alloc() {
     socket->__ready = 0;
     socket->__written = 0;
     socket->data = NULL;
-
-    /* Allocate instruction buffer */
-    socket->__instructionbuf_size = 1024;
-    socket->__instructionbuf = malloc(socket->__instructionbuf_size);
-
-    /* If no memory available, return with error */
-    if (socket->__instructionbuf == NULL) {
-        guac_error = GUAC_STATUS_NO_MEMORY;
-        guac_error_message = "Could not allocate memory for instruction buffer";
-        free(socket);
-        return NULL;
-    }
+    socket->state = GUAC_SOCKET_OPEN;
+    socket->last_write_timestamp = guac_timestamp_current();
 
     /* Init members */
-    socket->__instructionbuf_used_length = 0;
-    socket->__instructionbuf_parse_start = 0;
-    socket->__instructionbuf_elementc = 0;
+    socket->__instructionbuf_unparsed_start = socket->__instructionbuf;
+    socket->__instructionbuf_unparsed_end = socket->__instructionbuf;
 
     /* Default to unsafe threading */
     socket->__threadsafe_instructions = 0;
 
+    /* No keep alive ping by default */
+    socket->__keep_alive_enabled = 0;
+
     pthread_mutexattr_init(&lock_attributes);
     pthread_mutexattr_setpshared(&lock_attributes, PTHREAD_PROCESS_SHARED);
 
@@ -179,6 +184,18 @@ void guac_socket_require_threadsafe(guac_socket* socket) {
     socket->__threadsafe_instructions = 1;
 }
 
+void guac_socket_require_keep_alive(guac_socket* socket) {
+
+    /* Keep-alive thread requires a threadsafe socket */
+    guac_socket_require_threadsafe(socket);
+
+    /* Start keep-alive thread */
+    socket->__keep_alive_enabled = 1;
+    pthread_create(&(socket->__keep_alive_thread), NULL,
+                __guac_socket_keep_alive_thread, (void*) socket);
+
+}
+
 void guac_socket_instruction_begin(guac_socket* socket) {
 
     /* Lock writes if threadsafety enabled */
@@ -218,8 +235,15 @@ void guac_socket_free(guac_socket* socket) {
         socket->free_handler(socket);
 
     guac_socket_flush(socket);
+
+    /* Mark as closed */
+    socket->state = GUAC_SOCKET_CLOSED;
+
+    /* Wait for keep-alive, if enabled */
+    if (socket->__keep_alive_enabled)
+        pthread_join(socket->__keep_alive_thread, NULL);
+
     pthread_mutex_destroy(&(socket->__instruction_write_lock));
-    free(socket->__instructionbuf);
     free(socket);
 }
 
diff --git a/src/libguac/timestamp.c b/src/libguac/timestamp.c
index a610d8f..1e1d965 100644
--- a/src/libguac/timestamp.c
+++ b/src/libguac/timestamp.c
@@ -1,49 +1,34 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_NANOSLEEP)
-#include <time.h>
-#endif
+#include "config.h"
+
+#include "timestamp.h"
 
-#ifndef HAVE_CLOCK_GETTIME
 #include <sys/time.h>
-#endif
 
-#include "timestamp.h"
+#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_NANOSLEEP)
+#include <time.h>
+#endif
 
 guac_timestamp guac_timestamp_current() {
 
diff --git a/src/libguac/unicode.c b/src/libguac/unicode.c
index d2e6eeb..c3a21bf 100644
--- a/src/libguac/unicode.c
+++ b/src/libguac/unicode.c
@@ -1,44 +1,31 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <stddef.h>
+#include "config.h"
 
 #include "unicode.h"
 
+#include <stddef.h>
+
 size_t guac_utf8_charsize(unsigned char c) {
 
     /* Determine size in bytes of character */
diff --git a/src/libguac/wav_encoder.c b/src/libguac/wav_encoder.c
deleted file mode 100644
index fb7e40e..0000000
--- a/src/libguac/wav_encoder.c
+++ /dev/null
@@ -1,201 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#define WAV_BUFFER_SIZE 0x4000
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <guacamole/audio.h>
-#include <guacamole/client.h>
-#include <guacamole/protocol.h>
-
-#include "wav_encoder.h"
-
-void wav_encoder_begin_handler(guac_audio_stream* audio) {
-
-    /* Allocate stream state */
-    wav_encoder_state* state = (wav_encoder_state*)
-        malloc(sizeof(wav_encoder_state));
-
-    /* Initialize buffer */
-    state->length = WAV_BUFFER_SIZE;
-    state->used = 0;
-    state->data_buffer = (unsigned char*) malloc(state->length);
-
-    audio->data = state;
-
-}
-
-void _wav_encoder_write_le(unsigned char* buffer, int value, int length) {
-
-    int offset;
-
-    /* Write all bytes in the given value in little-endian byte order */
-    for (offset=0; offset<length; offset++) {
-
-        /* Store byte */
-        *buffer = value & 0xFF;
-
-        /* Move to next byte */
-        value >>= 8;
-        buffer++;
-
-    }
-
-}
-
-void wav_encoder_end_handler(guac_audio_stream* audio) {
-
-    /*
-     * Static header init
-     */
-
-    wav_encoder_riff_header riff_header = {
-        .chunk_id     = "RIFF",
-        .chunk_format = "WAVE"
-    };
-
-    wav_encoder_fmt_header fmt_header = {
-        .subchunk_id     = "fmt ",
-        .subchunk_size   = {0x10, 0x00, 0x00, 0x00}, /* 16 */
-        .subchunk_format = {0x01, 0x00}              /* 1 = PCM */
-    };
-
-    wav_encoder_data_header data_header = {
-        .subchunk_id = "data"
-    };
-
-    /* Get state */
-    wav_encoder_state* state = (wav_encoder_state*) audio->data;
-
-    /*
-     * RIFF HEADER
-     */
-
-    /* Chunk size */
-    _wav_encoder_write_le(riff_header.chunk_size,
-            4 + sizeof(fmt_header) + sizeof(data_header) + state->used,
-            sizeof(riff_header.chunk_size));
-
-    guac_audio_stream_write_encoded(audio,
-            (unsigned char*) &riff_header,
-            sizeof(riff_header));
-
-    /*
-     * FMT HEADER
-     */
-
-    /* Channels */
-    _wav_encoder_write_le(fmt_header.subchunk_channels,
-            audio->channels, sizeof(fmt_header.subchunk_channels));
-
-    /* Sample rate */
-    _wav_encoder_write_le(fmt_header.subchunk_sample_rate,
-            audio->rate, sizeof(fmt_header.subchunk_sample_rate));
-
-    /* Byte rate */
-    _wav_encoder_write_le(fmt_header.subchunk_byte_rate,
-            audio->rate * audio->channels * audio->bps / 8,
-            sizeof(fmt_header.subchunk_byte_rate));
-
-    /* Block align */
-    _wav_encoder_write_le(fmt_header.subchunk_block_align,
-            audio->channels * audio->bps / 8,
-            sizeof(fmt_header.subchunk_block_align));
-
-    /* Bits per second */
-    _wav_encoder_write_le(fmt_header.subchunk_bps,
-            audio->bps, sizeof(fmt_header.subchunk_bps));
-
-    guac_audio_stream_write_encoded(audio,
-            (unsigned char*) &fmt_header,
-            sizeof(fmt_header));
-
-    /*
-     * DATA HEADER
-     */
-
-    /* PCM data size */
-    _wav_encoder_write_le(data_header.subchunk_size,
-            state->used, sizeof(data_header.subchunk_size));
-
-    guac_audio_stream_write_encoded(audio,
-            (unsigned char*) &data_header,
-            sizeof(data_header));
-
-    /* Write .wav data */
-    guac_audio_stream_write_encoded(audio, state->data_buffer, state->used);
-
-    /* Free stream state */
-    free(state);
-
-}
-
-void wav_encoder_write_handler(guac_audio_stream* audio, 
-        const unsigned char* pcm_data, int length) {
-
-    /* Get state */
-    wav_encoder_state* state = (wav_encoder_state*) audio->data;
-
-    /* Increase size of buffer if necessary */
-    if (state->used + length > state->length) {
-
-        /* Increase to double concatenated size to accomodate */
-        state->length = (state->length + length)*2;
-        state->data_buffer = realloc(state->data_buffer,
-                state->length);
-
-    }
-
-    /* Append to buffer */
-    memcpy(&(state->data_buffer[state->used]), pcm_data, length);
-    state->used += length;
-
-}
-
-/* Encoder handlers */
-guac_audio_encoder _wav_encoder = {
-    .mimetype      = "audio/wav",
-    .begin_handler = wav_encoder_begin_handler,
-    .write_handler = wav_encoder_write_handler,
-    .end_handler   = wav_encoder_end_handler
-};
-
-/* Actual encoder */
-guac_audio_encoder* wav_encoder = &_wav_encoder;
-
diff --git a/src/libguac/wav_encoder.h b/src/libguac/wav_encoder.h
deleted file mode 100644
index c860ad1..0000000
--- a/src/libguac/wav_encoder.h
+++ /dev/null
@@ -1,144 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef __GUAC_WAV_ENCODER_H
-#define __GUAC_WAV_ENCODER_H
-
-#include <guacamole/audio.h>
-
-typedef struct wav_encoder_riff_header {
-
-    /**
-     * The RIFF chunk header, normally the string "RIFF".
-     */
-    unsigned char chunk_id[4];
-
-    /**
-     * Size of the entire file, not including chunk_id or chunk_size.
-     */
-    unsigned char chunk_size[4];
-
-    /**
-     * The format of this file, normally the string "WAVE".
-     */
-    unsigned char chunk_format[4];
-
-} wav_encoder_riff_header;
-
-typedef struct wav_encoder_fmt_header {
-
-    /**
-     * ID of this subchunk. For the fmt subchunk, this should be "fmt ".
-     */
-    unsigned char subchunk_id[4];
-
-    /**
-     * The size of the rest of this subchunk. For PCM, this will be 16.
-     */
-    unsigned char subchunk_size[4];
-
-    /**
-     * Format of this subchunk. For PCM, this will be 1.
-     */
-    unsigned char subchunk_format[2];
-
-    /**
-     * The number of channels in the PCM data.
-     */
-    unsigned char subchunk_channels[2];
-
-    /**
-     * The sample rate of the PCM data.
-     */
-    unsigned char subchunk_sample_rate[4];
-
-    /**
-     * The sample rate of the PCM data in bytes per second.
-     */
-    unsigned char subchunk_byte_rate[4];
-
-    /**
-     * The number of bytes per sample.
-     */
-    unsigned char subchunk_block_align[2];
-
-    /**
-     * The number of bits per sample.
-     */
-    unsigned char subchunk_bps[2];
-
-} wav_encoder_fmt_header;
-
-typedef struct wav_encoder_state {
-
-    /**
-     * Arbitrary PCM data available for writing when the overall WAV is
-     * flushed.
-     */
-    unsigned char* data_buffer;
-
-    /**
-     * The number of bytes currently present in the data buffer.
-     */
-    int used;
-
-    /**
-     * The total number of bytes that can be written into the data buffer
-     * without requiring resizing.
-     */
-    int length;
-
-} wav_encoder_state;
-
-typedef struct wav_encoder_data_header {
-
-    /**
-     * ID of this subchunk. For the data subchunk, this should be "data".
-     */
-    unsigned char subchunk_id[4];
-
-    /**
-     * The number of bytes in the PCM data.
-     */
-    unsigned char subchunk_size[4];
-
-} wav_encoder_data_header;
-
-extern guac_audio_encoder* wav_encoder;
-
-#endif
-
diff --git a/src/protocols/rdp/Makefile.am b/src/protocols/rdp/Makefile.am
index bcc6cd1..acd23dc 100644
--- a/src/protocols/rdp/Makefile.am
+++ b/src/protocols/rdp/Makefile.am
@@ -1,142 +1,272 @@
-# ***** BEGIN LICENSE BLOCK *****
-# Version: MPL 1.1/GPL 2.0/LGPL 2.1
-#
-# The contents of this file are subject to the Mozilla Public License Version
-# 1.1 (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.mozilla.org/MPL/
-#
-# Software distributed under the License is distributed on an "AS IS" basis,
-# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-# for the specific language governing rights and limitations under the
-# License.
-#
-# The Original Code is libguac-client-rdp.
-#
-# The Initial Developer of the Original Code is
-# Michael Jumper.
-# Portions created by the Initial Developer are Copyright (C) 2011
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either the GNU General Public License Version 2 or later (the "GPL"), or
-# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-# in which case the provisions of the GPL or the LGPL are applicable instead
-# of those above. If you wish to allow use of your version of this file only
-# under the terms of either the GPL or the LGPL, and not to allow others to
-# use your version of this file under the terms of the MPL, indicate your
-# decision by deleting the provisions above and replace them with the notice
-# and other provisions required by the GPL or the LGPL. If you do not delete
-# the provisions above, a recipient may use your version of this file under
-# the terms of any one of the MPL, the GPL or the LGPL.
-#
-# ***** END LICENSE BLOCK *****
+#
+# Copyright (C) 2015 Glyptodon LLC
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
 
 AUTOMAKE_OPTIONS = foreign
-
 ACLOCAL_AMFLAGS = -I m4
-AM_CFLAGS = -Werror -Wall -Iinclude @LIBGUAC_INCLUDE@
 
 lib_LTLIBRARIES = libguac-client-rdp.la
 
 libguac_client_rdp_la_SOURCES = \
-	audio.c                     \
-	client.c                    \
-	default_pointer.c           \
-	guac_handlers.c             \
-	rdp_bitmap.c                \
-	rdp_cliprdr.c               \
-	rdp_gdi.c                   \
-	rdp_glyph.c                 \
-	rdp_keymap_base.c           \
-	rdp_keymap.c                \
-	rdp_keymap_de_de.c          \
-	rdp_keymap_failsafe.c       \
-	rdp_keymap_fr_fr.c          \
-	rdp_keymap_en_us.c          \
-	rdp_pointer.c               \
-	rdp_settings.c              \
-	wav_encoder.c
+    _generated_keymaps.c        \
+    client.c                    \
+    guac_handlers.c             \
+    rdp_bitmap.c                \
+    rdp_cliprdr.c               \
+    rdp_color.c                 \
+    rdp_fs.c                    \
+    rdp_gdi.c                   \
+    rdp_glyph.c                 \
+    rdp_keymap.c                \
+    rdp_pointer.c               \
+    rdp_rail.c                  \
+    rdp_settings.c              \
+    rdp_stream.c                \
+    rdp_svc.c                   \
+    resolution.c                \
+    unicode.c
+
+guacsvc_sources =          \
+    guac_svc/svc_service.c \
+    rdp_svc.c
 
 guacsnd_sources =                 \
-	guac_rdpsnd/rdpsnd_messages.c \
-	guac_rdpsnd/rdpsnd_service.c  \
-	audio.c
-
-guacdr_sources =                \
-	guac_rdpdr/rdpdr_messages.c \
-	guac_rdpdr/rdpdr_printer.c  \
-	guac_rdpdr/rdpdr_service.c
-
-noinst_HEADERS =                     \
-	compat/client-cliprdr.h          \
-	guac_rdpdr/rdpdr_messages.h      \
-	guac_rdpdr/rdpdr_printer.h       \
-	guac_rdpdr/rdpdr_service.h       \
-	guac_rdpsnd/rdpsnd_messages.h    \
-	guac_rdpsnd/rdpsnd_service.h     \
-	audio.h                          \
-	client.h                         \
-	config.h                         \
-	default_pointer.h                \
-	guac_handlers.h                  \
-	rdp_bitmap.h                     \
-	rdp_cliprdr.h                    \
-	rdp_gdi.h                        \
-	rdp_glyph.h                      \
-	rdp_keymap.h                     \
-	rdp_pointer.h                    \
-	rdp_settings.h                   \
-	wav_encoder.h
+    guac_rdpsnd/rdpsnd_messages.c \
+    guac_rdpsnd/rdpsnd_service.c
+
+guacdr_sources =                             \
+    guac_rdpdr/rdpdr_fs_messages.c           \
+    guac_rdpdr/rdpdr_fs_messages_dir_info.c  \
+    guac_rdpdr/rdpdr_fs_messages_file_info.c \
+    guac_rdpdr/rdpdr_fs_messages_vol_info.c  \
+    guac_rdpdr/rdpdr_fs_service.c            \
+    guac_rdpdr/rdpdr_messages.c              \
+    guac_rdpdr/rdpdr_printer.c               \
+    guac_rdpdr/rdpdr_service.c               \
+    rdp_fs.c                                 \
+    rdp_stream.c                             \
+    unicode.c
+
+noinst_HEADERS =                             \
+    compat/client-cliprdr.h                  \
+    compat/rail.h                            \
+    guac_rdpdr/rdpdr_fs_messages.h           \
+    guac_rdpdr/rdpdr_fs_messages_dir_info.h  \
+    guac_rdpdr/rdpdr_fs_messages_file_info.h \
+    guac_rdpdr/rdpdr_fs_messages_vol_info.h  \
+    guac_rdpdr/rdpdr_fs_service.h            \
+    guac_rdpdr/rdpdr_messages.h              \
+    guac_rdpdr/rdpdr_printer.h               \
+    guac_rdpdr/rdpdr_service.h               \
+    guac_rdpsnd/rdpsnd_messages.h            \
+    guac_rdpsnd/rdpsnd_service.h             \
+    guac_svc/svc_service.h                   \
+    client.h                                 \
+    guac_handlers.h                          \
+    rdp_bitmap.h                             \
+    rdp_cliprdr.h                            \
+    rdp_color.h                              \
+    rdp_fs.h                                 \
+    rdp_gdi.h                                \
+    rdp_glyph.h                              \
+    rdp_keymap.h                             \
+    rdp_pointer.h                            \
+    rdp_rail.h                               \
+    rdp_settings.h                           \
+    rdp_status.h                             \
+    rdp_stream.h                             \
+    rdp_svc.h                                \
+    resolution.h                             \
+    unicode.h
 
 # Add compatibility layer for WinPR if not available
 if ! ENABLE_WINPR
-    noinst_HEADERS  += compat/winpr-stream.h compat/winpr-wtypes.h
-    guacsnd_sources += compat/winpr-stream.c
-    guacdr_sources  += compat/winpr-stream.c
+noinst_HEADERS  += compat/winpr-stream.h compat/winpr-wtypes.h
+libguac_client_rdp_la_SOURCES += compat/winpr-stream.c
+guacsvc_sources += compat/winpr-stream.c
+guacsnd_sources += compat/winpr-stream.c
+guacdr_sources  += compat/winpr-stream.c
 endif
 
-# Compile OGG support if available
-if ENABLE_OGG
-    libguac_client_rdp_la_SOURCES += ogg_encoder.c
-    noinst_HEADERS += ogg_encoder.h
+# Add display update channel support, if supported by FreeRDP
+if ENABLE_DISPLAY_UPDATE
+noinst_HEADERS += rdp_disp.h
+libguac_client_rdp_la_SOURCES += rdp_disp.c
 endif
 
-libguac_client_rdp_la_LDFLAGS = -version-info 0:0:0 @RDP_LIBS@ @VORBIS_LIBS@ @PTHREAD_LIBS@ @CAIRO_LIBS@
-guacsnd_ldflags = -module -avoid-version -shared @RDP_LIBS@ @VORBIS_LIBS@ @PTHREAD_LIBS@
-guacdr_ldflags = -module -avoid-version -shared @RDP_LIBS@ @PTHREAD_LIBS@
+#
+# Main RDP client library
+#
+
+libguac_client_rdp_la_CFLAGS = \
+    -Werror -Wall -Iinclude    \
+    @COMMON_INCLUDE@           \
+    @COMMON_SSH_INCLUDE@       \
+    @LIBGUAC_INCLUDE@
+
+libguac_client_rdp_la_LDFLAGS = \
+    -version-info 0:0:0         \
+    @CAIRO_LIBS@                \
+    @PTHREAD_LIBS@              \
+    @RDP_LIBS@
+
+libguac_client_rdp_la_LIBADD =     \
+    @COMMON_LTLIB@                 \
+    @LIBGUAC_LTLIB@
+
+#
+# RDPDR
+#
+
+guacdr_cflags                = \
+    -Werror -Wall -Iinclude    \
+    @COMMON_INCLUDE@           \
+    @COMMON_SSH_INCLUDE@       \
+    @LIBGUAC_INCLUDE@
+
+guacdr_ldflags =                   \
+    -module -avoid-version -shared \
+    @PTHREAD_LIBS@                 \
+    @RDP_LIBS@
+
+guacdr_libadd =     \
+    @COMMON_LTLIB@  \
+    @LIBGUAC_LTLIB@
+
+#
+# RDPSND
+#
+
+guacsnd_cflags               = \
+    -Werror -Wall -Iinclude    \
+    @COMMON_INCLUDE@           \
+    @COMMON_SSH_INCLUDE@       \
+    @LIBGUAC_INCLUDE@
+
+guacsnd_ldflags =                  \
+    -module -avoid-version -shared \
+    @PTHREAD_LIBS@                 \
+    @RDP_LIBS@
+
+guacsnd_libadd =    \
+    @COMMON_LTLIB@  \
+    @LIBGUAC_LTLIB@
+
+#
+# Static Virtual Channels
+#
 
-libguac_client_rdp_la_LIBADD = @LIBGUAC_LTLIB@
-guacsnd_libadd = @LIBGUAC_LTLIB@
-guacdr_libadd = @LIBGUAC_LTLIB@
+guacsvc_cflags               = \
+    -Werror -Wall -Iinclude    \
+    @COMMON_INCLUDE@           \
+    @COMMON_SSH_INCLUDE@       \
+    @LIBGUAC_INCLUDE@
+
+guacsvc_ldflags =                  \
+    -module -avoid-version -shared \
+    @PTHREAD_LIBS@                 \
+    @RDP_LIBS@
+
+guacsvc_libadd =    \
+    @COMMON_LTLIB@  \
+    @LIBGUAC_LTLIB@
+
+#
+# Optional SFTP support
+#
+
+if ENABLE_COMMON_SSH
+libguac_client_rdp_la_SOURCES += sftp.c
+noinst_HEADERS                += sftp.h
+libguac_client_rdp_la_LIBADD  += @COMMON_SSH_LTLIB@
+endif
+
+#
+# Autogenerate keymaps
+#
+
+CLEANFILES = _generated_keymaps.c
+BUILT_SOURCES = _generated_keymaps.c
+
+rdp_keymaps =                   \
+    keymaps/base.keymap         \
+    keymaps/failsafe.keymap     \
+    keymaps/de_de_qwertz.keymap \
+    keymaps/en_us_qwerty.keymap \
+    keymaps/fr_fr_azerty.keymap \
+    keymaps/it_it_qwerty.keymap \
+    keymaps/sv_se_qwerty.keymap
+
+_generated_keymaps.c: $(rdp_keymaps)
+	keymaps/generate.pl $(rdp_keymaps)
+
+EXTRA_DIST =            \
+    $(rdp_keymaps)      \
+    keymaps/generate.pl
 
 if LEGACY_FREERDP_EXTENSIONS
 
 # FreeRDP 1.0-style extensions
-freerdp_LTLIBRARIES = guacsnd.la guacdr.la
+freerdp_LTLIBRARIES = \
+    guacdr.la         \
+    guacsnd.la        \
+    guacsvc.la
+
+guacdr_la_SOURCES = ${guacdr_sources}
+guacdr_la_CFLAGS  = ${guacdr_cflags}
+guacdr_la_LDFLAGS = ${guacdr_ldflags}
+guacdr_la_LIBADD  = ${guacdr_libadd}
 
 guacsnd_la_SOURCES = ${guacsnd_sources}
+guacsnd_la_CFLAGS  = ${guacsnd_cflags}
 guacsnd_la_LDFLAGS = ${guacsnd_ldflags}
 guacsnd_la_LIBADD  = ${guacsnd_libadd}
 
-guacdr_la_SOURCES = ${guacdr_sources}
-guacdr_la_LDFLAGS = ${guacdr_ldflags}
-guacdr_la_LIBADD  = ${guacdr_libadd}
+guacsvc_la_SOURCES = ${guacsvc_sources}
+guacsvc_la_CFLAGS  = ${guacsvc_cflags}
+guacsvc_la_LDFLAGS = ${guacsvc_ldflags}
+guacsvc_la_LIBADD  = ${guacsvc_libadd}
 
 else
 
 # FreeRDP 1.1 (and hopefully onward) extensions
-freerdp_LTLIBRARIES = guacsnd-client.la guacdr-client.la
+freerdp_LTLIBRARIES = \
+    guacdr-client.la  \
+    guacsnd-client.la \
+    guacsvc-client.la
+
+guacdr_client_la_SOURCES = ${guacdr_sources}
+guacdr_client_la_CFLAGS  = ${guacdr_cflags}
+guacdr_client_la_LDFLAGS = ${guacdr_ldflags}
+guacdr_client_la_LIBADD  = ${guacdr_libadd}
 
 guacsnd_client_la_SOURCES = ${guacsnd_sources}
+guacsnd_client_la_CFLAGS  = ${guacsnd_cflags}
 guacsnd_client_la_LDFLAGS = ${guacsnd_ldflags}
 guacsnd_client_la_LIBADD  = ${guacsnd_libadd}
 
-guacdr_client_la_SOURCES = ${guacdr_sources}
-guacdr_client_la_LDFLAGS = ${guacdr_ldflags}
-guacdr_client_la_LIBADD  = ${guacdr_libadd}
+guacsvc_client_la_SOURCES = ${guacsvc_sources}
+guacsvc_client_la_CFLAGS  = ${guacsvc_cflags}
+guacsvc_client_la_LDFLAGS = ${guacsvc_ldflags}
+guacsvc_client_la_LIBADD  = ${guacsvc_libadd}
 
 endif
 
diff --git a/src/protocols/rdp/Makefile.in b/src/protocols/rdp/Makefile.in
index 25f4a0c..a28aa41 100644
--- a/src/protocols/rdp/Makefile.in
+++ b/src/protocols/rdp/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.6 from Makefile.am.
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software
-# Foundation, Inc.
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
@@ -15,61 +14,75 @@
 
 @SET_MAKE@
 
-# ***** BEGIN LICENSE BLOCK *****
-# Version: MPL 1.1/GPL 2.0/LGPL 2.1
-#
-# The contents of this file are subject to the Mozilla Public License Version
-# 1.1 (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.mozilla.org/MPL/
-#
-# Software distributed under the License is distributed on an "AS IS" basis,
-# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-# for the specific language governing rights and limitations under the
-# License.
 #
-# The Original Code is libguac-client-rdp.
+# Copyright (C) 2015 Glyptodon LLC
 #
-# The Initial Developer of the Original Code is
-# Michael Jumper.
-# Portions created by the Initial Developer are Copyright (C) 2011
-# the Initial Developer. All Rights Reserved.
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
 #
-# Contributor(s):
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
 #
-# Alternatively, the contents of this file may be used under the terms of
-# either the GNU General Public License Version 2 or later (the "GPL"), or
-# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-# in which case the provisions of the GPL or the LGPL are applicable instead
-# of those above. If you wish to allow use of your version of this file only
-# under the terms of either the GPL or the LGPL, and not to allow others to
-# use your version of this file under the terms of the MPL, indicate your
-# decision by deleting the provisions above and replace them with the notice
-# and other provisions required by the GPL or the LGPL. If you do not delete
-# the provisions above, a recipient may use your version of this file under
-# the terms of any one of the MPL, the GPL or the LGPL.
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
 #
-# ***** END LICENSE BLOCK *****
 
 
 VPATH = @srcdir@
-am__make_dryrun = \
-  { \
-    am__dry=no; \
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
     case $$MAKEFLAGS in \
       *\\[\ \	]*) \
-        echo 'am--echo: ; @echo "AM"  OK' | $(MAKE) -f - 2>/dev/null \
-          | grep '^AM OK$$' >/dev/null || am__dry=yes;; \
-      *) \
-        for am__flg in $$MAKEFLAGS; do \
-          case $$am__flg in \
-            *=*|--*) ;; \
-            *n*) am__dry=yes; break;; \
-          esac; \
-        done;; \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
     esac; \
-    test $$am__dry = yes; \
-  }
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
 pkgdatadir = $(datadir)/@PACKAGE@
 pkgincludedir = $(includedir)/@PACKAGE@
 pkglibdir = $(libdir)/@PACKAGE@
@@ -93,13 +106,22 @@ host_triplet = @host@
 @ENABLE_WINPR_FALSE at am__append_1 = compat/winpr-stream.h compat/winpr-wtypes.h
 @ENABLE_WINPR_FALSE at am__append_2 = compat/winpr-stream.c
 @ENABLE_WINPR_FALSE at am__append_3 = compat/winpr-stream.c
+ at ENABLE_WINPR_FALSE@am__append_4 = compat/winpr-stream.c
+ at ENABLE_WINPR_FALSE@am__append_5 = compat/winpr-stream.c
+
+# Add display update channel support, if supported by FreeRDP
+ at ENABLE_DISPLAY_UPDATE_TRUE@am__append_6 = rdp_disp.h
+ at ENABLE_DISPLAY_UPDATE_TRUE@am__append_7 = rdp_disp.c
 
-# Compile OGG support if available
- at ENABLE_OGG_TRUE@am__append_4 = ogg_encoder.c
- at ENABLE_OGG_TRUE@am__append_5 = ogg_encoder.h
+#
+# Optional SFTP support
+#
+ at ENABLE_COMMON_SSH_TRUE@am__append_8 = sftp.c
+ at ENABLE_COMMON_SSH_TRUE@am__append_9 = sftp.h
+ at ENABLE_COMMON_SSH_TRUE@am__append_10 = @COMMON_SSH_LTLIB@
 subdir = src/protocols/rdp
-DIST_COMMON = $(am__noinst_HEADERS_DIST) $(srcdir)/Makefile.am \
-	$(srcdir)/Makefile.in
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(top_srcdir)/depcomp $(am__noinst_HEADERS_DIST)
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
 	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
@@ -108,6 +130,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
 CONFIG_CLEAN_FILES =
 CONFIG_CLEAN_VPATH_FILES =
 am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
@@ -142,117 +165,254 @@ LTLIBRARIES = $(freerdp_LTLIBRARIES) $(lib_LTLIBRARIES)
 am__DEPENDENCIES_1 =
 @LEGACY_FREERDP_EXTENSIONS_FALSE at guacdr_client_la_DEPENDENCIES =  \
 @LEGACY_FREERDP_EXTENSIONS_FALSE@	$(am__DEPENDENCIES_1)
-am__guacdr_client_la_SOURCES_DIST = guac_rdpdr/rdpdr_messages.c \
-	guac_rdpdr/rdpdr_printer.c guac_rdpdr/rdpdr_service.c \
-	compat/winpr-stream.c
- at ENABLE_WINPR_FALSE@am__objects_1 = winpr-stream.lo
-am__objects_2 = rdpdr_messages.lo rdpdr_printer.lo rdpdr_service.lo \
-	$(am__objects_1)
+am__guacdr_client_la_SOURCES_DIST = guac_rdpdr/rdpdr_fs_messages.c \
+	guac_rdpdr/rdpdr_fs_messages_dir_info.c \
+	guac_rdpdr/rdpdr_fs_messages_file_info.c \
+	guac_rdpdr/rdpdr_fs_messages_vol_info.c \
+	guac_rdpdr/rdpdr_fs_service.c guac_rdpdr/rdpdr_messages.c \
+	guac_rdpdr/rdpdr_printer.c guac_rdpdr/rdpdr_service.c rdp_fs.c \
+	rdp_stream.c unicode.c compat/winpr-stream.c
+am__dirstamp = $(am__leading_dot)dirstamp
+ at ENABLE_WINPR_FALSE@am__objects_1 =  \
+ at ENABLE_WINPR_FALSE@	compat/guacdr_client_la-winpr-stream.lo
+am__objects_2 = guac_rdpdr/guacdr_client_la-rdpdr_fs_messages.lo \
+	guac_rdpdr/guacdr_client_la-rdpdr_fs_messages_dir_info.lo \
+	guac_rdpdr/guacdr_client_la-rdpdr_fs_messages_file_info.lo \
+	guac_rdpdr/guacdr_client_la-rdpdr_fs_messages_vol_info.lo \
+	guac_rdpdr/guacdr_client_la-rdpdr_fs_service.lo \
+	guac_rdpdr/guacdr_client_la-rdpdr_messages.lo \
+	guac_rdpdr/guacdr_client_la-rdpdr_printer.lo \
+	guac_rdpdr/guacdr_client_la-rdpdr_service.lo \
+	guacdr_client_la-rdp_fs.lo guacdr_client_la-rdp_stream.lo \
+	guacdr_client_la-unicode.lo $(am__objects_1)
 @LEGACY_FREERDP_EXTENSIONS_FALSE at am_guacdr_client_la_OBJECTS =  \
 @LEGACY_FREERDP_EXTENSIONS_FALSE@	$(am__objects_2)
 guacdr_client_la_OBJECTS = $(am_guacdr_client_la_OBJECTS)
-guacdr_client_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
-	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+AM_V_lt = $(am__v_lt_ at AM_V@)
+am__v_lt_ = $(am__v_lt_ at AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+guacdr_client_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(guacdr_client_la_CFLAGS) $(CFLAGS) \
 	$(guacdr_client_la_LDFLAGS) $(LDFLAGS) -o $@
 @LEGACY_FREERDP_EXTENSIONS_FALSE at am_guacdr_client_la_rpath = -rpath \
 @LEGACY_FREERDP_EXTENSIONS_FALSE@	$(freerdpdir)
 @LEGACY_FREERDP_EXTENSIONS_TRUE at guacdr_la_DEPENDENCIES =  \
 @LEGACY_FREERDP_EXTENSIONS_TRUE@	$(am__DEPENDENCIES_1)
-am__guacdr_la_SOURCES_DIST = guac_rdpdr/rdpdr_messages.c \
-	guac_rdpdr/rdpdr_printer.c guac_rdpdr/rdpdr_service.c \
-	compat/winpr-stream.c
+am__guacdr_la_SOURCES_DIST = guac_rdpdr/rdpdr_fs_messages.c \
+	guac_rdpdr/rdpdr_fs_messages_dir_info.c \
+	guac_rdpdr/rdpdr_fs_messages_file_info.c \
+	guac_rdpdr/rdpdr_fs_messages_vol_info.c \
+	guac_rdpdr/rdpdr_fs_service.c guac_rdpdr/rdpdr_messages.c \
+	guac_rdpdr/rdpdr_printer.c guac_rdpdr/rdpdr_service.c rdp_fs.c \
+	rdp_stream.c unicode.c compat/winpr-stream.c
+ at ENABLE_WINPR_FALSE@am__objects_3 = compat/guacdr_la-winpr-stream.lo
+am__objects_4 = guac_rdpdr/guacdr_la-rdpdr_fs_messages.lo \
+	guac_rdpdr/guacdr_la-rdpdr_fs_messages_dir_info.lo \
+	guac_rdpdr/guacdr_la-rdpdr_fs_messages_file_info.lo \
+	guac_rdpdr/guacdr_la-rdpdr_fs_messages_vol_info.lo \
+	guac_rdpdr/guacdr_la-rdpdr_fs_service.lo \
+	guac_rdpdr/guacdr_la-rdpdr_messages.lo \
+	guac_rdpdr/guacdr_la-rdpdr_printer.lo \
+	guac_rdpdr/guacdr_la-rdpdr_service.lo guacdr_la-rdp_fs.lo \
+	guacdr_la-rdp_stream.lo guacdr_la-unicode.lo $(am__objects_3)
 @LEGACY_FREERDP_EXTENSIONS_TRUE at am_guacdr_la_OBJECTS =  \
- at LEGACY_FREERDP_EXTENSIONS_TRUE@	$(am__objects_2)
+ at LEGACY_FREERDP_EXTENSIONS_TRUE@	$(am__objects_4)
 guacdr_la_OBJECTS = $(am_guacdr_la_OBJECTS)
-guacdr_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
-	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
-	$(guacdr_la_LDFLAGS) $(LDFLAGS) -o $@
+guacdr_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(guacdr_la_CFLAGS) \
+	$(CFLAGS) $(guacdr_la_LDFLAGS) $(LDFLAGS) -o $@
 @LEGACY_FREERDP_EXTENSIONS_TRUE at am_guacdr_la_rpath = -rpath \
 @LEGACY_FREERDP_EXTENSIONS_TRUE@	$(freerdpdir)
 @LEGACY_FREERDP_EXTENSIONS_FALSE at guacsnd_client_la_DEPENDENCIES =  \
 @LEGACY_FREERDP_EXTENSIONS_FALSE@	$(am__DEPENDENCIES_1)
 am__guacsnd_client_la_SOURCES_DIST = guac_rdpsnd/rdpsnd_messages.c \
-	guac_rdpsnd/rdpsnd_service.c audio.c compat/winpr-stream.c
-am__objects_3 = rdpsnd_messages.lo rdpsnd_service.lo audio.lo \
-	$(am__objects_1)
+	guac_rdpsnd/rdpsnd_service.c compat/winpr-stream.c
+ at ENABLE_WINPR_FALSE@am__objects_5 =  \
+ at ENABLE_WINPR_FALSE@	compat/guacsnd_client_la-winpr-stream.lo
+am__objects_6 = guac_rdpsnd/guacsnd_client_la-rdpsnd_messages.lo \
+	guac_rdpsnd/guacsnd_client_la-rdpsnd_service.lo \
+	$(am__objects_5)
 @LEGACY_FREERDP_EXTENSIONS_FALSE at am_guacsnd_client_la_OBJECTS =  \
- at LEGACY_FREERDP_EXTENSIONS_FALSE@	$(am__objects_3)
+ at LEGACY_FREERDP_EXTENSIONS_FALSE@	$(am__objects_6)
 guacsnd_client_la_OBJECTS = $(am_guacsnd_client_la_OBJECTS)
-guacsnd_client_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
-	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+guacsnd_client_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(guacsnd_client_la_CFLAGS) $(CFLAGS) \
 	$(guacsnd_client_la_LDFLAGS) $(LDFLAGS) -o $@
 @LEGACY_FREERDP_EXTENSIONS_FALSE at am_guacsnd_client_la_rpath = -rpath \
 @LEGACY_FREERDP_EXTENSIONS_FALSE@	$(freerdpdir)
 @LEGACY_FREERDP_EXTENSIONS_TRUE at guacsnd_la_DEPENDENCIES =  \
 @LEGACY_FREERDP_EXTENSIONS_TRUE@	$(am__DEPENDENCIES_1)
 am__guacsnd_la_SOURCES_DIST = guac_rdpsnd/rdpsnd_messages.c \
-	guac_rdpsnd/rdpsnd_service.c audio.c compat/winpr-stream.c
+	guac_rdpsnd/rdpsnd_service.c compat/winpr-stream.c
+ at ENABLE_WINPR_FALSE@am__objects_7 = compat/guacsnd_la-winpr-stream.lo
+am__objects_8 = guac_rdpsnd/guacsnd_la-rdpsnd_messages.lo \
+	guac_rdpsnd/guacsnd_la-rdpsnd_service.lo $(am__objects_7)
 @LEGACY_FREERDP_EXTENSIONS_TRUE at am_guacsnd_la_OBJECTS =  \
- at LEGACY_FREERDP_EXTENSIONS_TRUE@	$(am__objects_3)
+ at LEGACY_FREERDP_EXTENSIONS_TRUE@	$(am__objects_8)
 guacsnd_la_OBJECTS = $(am_guacsnd_la_OBJECTS)
-guacsnd_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
-	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
-	$(guacsnd_la_LDFLAGS) $(LDFLAGS) -o $@
+guacsnd_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(guacsnd_la_CFLAGS) \
+	$(CFLAGS) $(guacsnd_la_LDFLAGS) $(LDFLAGS) -o $@
 @LEGACY_FREERDP_EXTENSIONS_TRUE at am_guacsnd_la_rpath = -rpath \
 @LEGACY_FREERDP_EXTENSIONS_TRUE@	$(freerdpdir)
-libguac_client_rdp_la_DEPENDENCIES =
-am__libguac_client_rdp_la_SOURCES_DIST = audio.c client.c \
-	default_pointer.c guac_handlers.c rdp_bitmap.c rdp_cliprdr.c \
-	rdp_gdi.c rdp_glyph.c rdp_keymap_base.c rdp_keymap.c \
-	rdp_keymap_de_de.c rdp_keymap_failsafe.c rdp_keymap_fr_fr.c \
-	rdp_keymap_en_us.c rdp_pointer.c rdp_settings.c wav_encoder.c \
-	ogg_encoder.c
- at ENABLE_OGG_TRUE@am__objects_4 = ogg_encoder.lo
-am_libguac_client_rdp_la_OBJECTS = audio.lo client.lo \
-	default_pointer.lo guac_handlers.lo rdp_bitmap.lo \
-	rdp_cliprdr.lo rdp_gdi.lo rdp_glyph.lo rdp_keymap_base.lo \
-	rdp_keymap.lo rdp_keymap_de_de.lo rdp_keymap_failsafe.lo \
-	rdp_keymap_fr_fr.lo rdp_keymap_en_us.lo rdp_pointer.lo \
-	rdp_settings.lo wav_encoder.lo $(am__objects_4)
+ at LEGACY_FREERDP_EXTENSIONS_FALSE@guacsvc_client_la_DEPENDENCIES =  \
+ at LEGACY_FREERDP_EXTENSIONS_FALSE@	$(am__DEPENDENCIES_1)
+am__guacsvc_client_la_SOURCES_DIST = guac_svc/svc_service.c rdp_svc.c \
+	compat/winpr-stream.c
+ at ENABLE_WINPR_FALSE@am__objects_9 =  \
+ at ENABLE_WINPR_FALSE@	compat/guacsvc_client_la-winpr-stream.lo
+am__objects_10 = guac_svc/guacsvc_client_la-svc_service.lo \
+	guacsvc_client_la-rdp_svc.lo $(am__objects_9)
+ at LEGACY_FREERDP_EXTENSIONS_FALSE@am_guacsvc_client_la_OBJECTS =  \
+ at LEGACY_FREERDP_EXTENSIONS_FALSE@	$(am__objects_10)
+guacsvc_client_la_OBJECTS = $(am_guacsvc_client_la_OBJECTS)
+guacsvc_client_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(guacsvc_client_la_CFLAGS) $(CFLAGS) \
+	$(guacsvc_client_la_LDFLAGS) $(LDFLAGS) -o $@
+ at LEGACY_FREERDP_EXTENSIONS_FALSE@am_guacsvc_client_la_rpath = -rpath \
+ at LEGACY_FREERDP_EXTENSIONS_FALSE@	$(freerdpdir)
+ at LEGACY_FREERDP_EXTENSIONS_TRUE@guacsvc_la_DEPENDENCIES =  \
+ at LEGACY_FREERDP_EXTENSIONS_TRUE@	$(am__DEPENDENCIES_1)
+am__guacsvc_la_SOURCES_DIST = guac_svc/svc_service.c rdp_svc.c \
+	compat/winpr-stream.c
+ at ENABLE_WINPR_FALSE@am__objects_11 =  \
+ at ENABLE_WINPR_FALSE@	compat/guacsvc_la-winpr-stream.lo
+am__objects_12 = guac_svc/guacsvc_la-svc_service.lo \
+	guacsvc_la-rdp_svc.lo $(am__objects_11)
+ at LEGACY_FREERDP_EXTENSIONS_TRUE@am_guacsvc_la_OBJECTS =  \
+ at LEGACY_FREERDP_EXTENSIONS_TRUE@	$(am__objects_12)
+guacsvc_la_OBJECTS = $(am_guacsvc_la_OBJECTS)
+guacsvc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(guacsvc_la_CFLAGS) \
+	$(CFLAGS) $(guacsvc_la_LDFLAGS) $(LDFLAGS) -o $@
+ at LEGACY_FREERDP_EXTENSIONS_TRUE@am_guacsvc_la_rpath = -rpath \
+ at LEGACY_FREERDP_EXTENSIONS_TRUE@	$(freerdpdir)
+libguac_client_rdp_la_DEPENDENCIES = $(am__DEPENDENCIES_1)
+am__libguac_client_rdp_la_SOURCES_DIST = _generated_keymaps.c client.c \
+	guac_handlers.c rdp_bitmap.c rdp_cliprdr.c rdp_color.c \
+	rdp_fs.c rdp_gdi.c rdp_glyph.c rdp_keymap.c rdp_pointer.c \
+	rdp_rail.c rdp_settings.c rdp_stream.c rdp_svc.c resolution.c \
+	unicode.c compat/winpr-stream.c rdp_disp.c sftp.c
+ at ENABLE_WINPR_FALSE@am__objects_13 = compat/libguac_client_rdp_la-winpr-stream.lo
+ at ENABLE_DISPLAY_UPDATE_TRUE@am__objects_14 =  \
+ at ENABLE_DISPLAY_UPDATE_TRUE@	libguac_client_rdp_la-rdp_disp.lo
+ at ENABLE_COMMON_SSH_TRUE@am__objects_15 =  \
+ at ENABLE_COMMON_SSH_TRUE@	libguac_client_rdp_la-sftp.lo
+am_libguac_client_rdp_la_OBJECTS =  \
+	libguac_client_rdp_la-_generated_keymaps.lo \
+	libguac_client_rdp_la-client.lo \
+	libguac_client_rdp_la-guac_handlers.lo \
+	libguac_client_rdp_la-rdp_bitmap.lo \
+	libguac_client_rdp_la-rdp_cliprdr.lo \
+	libguac_client_rdp_la-rdp_color.lo \
+	libguac_client_rdp_la-rdp_fs.lo \
+	libguac_client_rdp_la-rdp_gdi.lo \
+	libguac_client_rdp_la-rdp_glyph.lo \
+	libguac_client_rdp_la-rdp_keymap.lo \
+	libguac_client_rdp_la-rdp_pointer.lo \
+	libguac_client_rdp_la-rdp_rail.lo \
+	libguac_client_rdp_la-rdp_settings.lo \
+	libguac_client_rdp_la-rdp_stream.lo \
+	libguac_client_rdp_la-rdp_svc.lo \
+	libguac_client_rdp_la-resolution.lo \
+	libguac_client_rdp_la-unicode.lo $(am__objects_13) \
+	$(am__objects_14) $(am__objects_15)
 libguac_client_rdp_la_OBJECTS = $(am_libguac_client_rdp_la_OBJECTS)
-libguac_client_rdp_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
-	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+libguac_client_rdp_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(libguac_client_rdp_la_CFLAGS) $(CFLAGS) \
 	$(libguac_client_rdp_la_LDFLAGS) $(LDFLAGS) -o $@
-DEFAULT_INCLUDES = -I. at am__isrc@
+AM_V_P = $(am__v_P_ at AM_V@)
+am__v_P_ = $(am__v_P_ at AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_ at AM_V@)
+am__v_GEN_ = $(am__v_GEN_ at AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_ at AM_V@)
+am__v_at_ = $(am__v_at_ at AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I. at am__isrc@ -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/depcomp
 am__depfiles_maybe = depfiles
 am__mv = mv -f
 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
 	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
-	--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
-	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_ at AM_V@)
+am__v_CC_ = $(am__v_CC_ at AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
 CCLD = $(CC)
-LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
-	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
-	$(LDFLAGS) -o $@
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_ at AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_ at AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
 SOURCES = $(guacdr_client_la_SOURCES) $(guacdr_la_SOURCES) \
 	$(guacsnd_client_la_SOURCES) $(guacsnd_la_SOURCES) \
+	$(guacsvc_client_la_SOURCES) $(guacsvc_la_SOURCES) \
 	$(libguac_client_rdp_la_SOURCES)
 DIST_SOURCES = $(am__guacdr_client_la_SOURCES_DIST) \
 	$(am__guacdr_la_SOURCES_DIST) \
 	$(am__guacsnd_client_la_SOURCES_DIST) \
 	$(am__guacsnd_la_SOURCES_DIST) \
+	$(am__guacsvc_client_la_SOURCES_DIST) \
+	$(am__guacsvc_la_SOURCES_DIST) \
 	$(am__libguac_client_rdp_la_SOURCES_DIST)
 am__can_run_installinfo = \
   case $$AM_UPDATE_INFO_DIR in \
     n|no|NO) false;; \
     *) (install-info --version) >/dev/null 2>&1;; \
   esac
-am__noinst_HEADERS_DIST = compat/client-cliprdr.h \
-	guac_rdpdr/rdpdr_messages.h guac_rdpdr/rdpdr_printer.h \
-	guac_rdpdr/rdpdr_service.h guac_rdpsnd/rdpsnd_messages.h \
-	guac_rdpsnd/rdpsnd_service.h audio.h client.h config.h \
-	default_pointer.h guac_handlers.h rdp_bitmap.h rdp_cliprdr.h \
-	rdp_gdi.h rdp_glyph.h rdp_keymap.h rdp_pointer.h \
-	rdp_settings.h wav_encoder.h compat/winpr-stream.h \
-	compat/winpr-wtypes.h ogg_encoder.h
+am__noinst_HEADERS_DIST = compat/client-cliprdr.h compat/rail.h \
+	guac_rdpdr/rdpdr_fs_messages.h \
+	guac_rdpdr/rdpdr_fs_messages_dir_info.h \
+	guac_rdpdr/rdpdr_fs_messages_file_info.h \
+	guac_rdpdr/rdpdr_fs_messages_vol_info.h \
+	guac_rdpdr/rdpdr_fs_service.h guac_rdpdr/rdpdr_messages.h \
+	guac_rdpdr/rdpdr_printer.h guac_rdpdr/rdpdr_service.h \
+	guac_rdpsnd/rdpsnd_messages.h guac_rdpsnd/rdpsnd_service.h \
+	guac_svc/svc_service.h client.h guac_handlers.h rdp_bitmap.h \
+	rdp_cliprdr.h rdp_color.h rdp_fs.h rdp_gdi.h rdp_glyph.h \
+	rdp_keymap.h rdp_pointer.h rdp_rail.h rdp_settings.h \
+	rdp_status.h rdp_stream.h rdp_svc.h resolution.h unicode.h \
+	compat/winpr-stream.h compat/winpr-wtypes.h rdp_disp.h sftp.h
 HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
 ETAGS = etags
 CTAGS = ctags
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 ACLOCAL = @ACLOCAL@
 AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -262,6 +422,10 @@ CAIRO_LIBS = @CAIRO_LIBS@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+COMMON_INCLUDE = @COMMON_INCLUDE@
+COMMON_LTLIB = @COMMON_LTLIB@
+COMMON_SSH_INCLUDE = @COMMON_SSH_INCLUDE@
+COMMON_SSH_LTLIB = @COMMON_SSH_LTLIB@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
 CUNIT_LIBS = @CUNIT_LIBS@
@@ -269,7 +433,6 @@ CYGPATH_W = @CYGPATH_W@
 DEFS = @DEFS@
 DEPDIR = @DEPDIR@
 DLLTOOL = @DLLTOOL@
-DL_LIBS = @DL_LIBS@
 DSYMUTIL = @DSYMUTIL@
 DUMPBIN = @DUMPBIN@
 ECHO_C = @ECHO_C@
@@ -284,8 +447,10 @@ INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
 INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+JPEG_LIBS = @JPEG_LIBS@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
+LIBADD_DLOPEN = @LIBADD_DLOPEN@
 LIBGUAC_INCLUDE = @LIBGUAC_INCLUDE@
 LIBGUAC_LTLIB = @LIBGUAC_LTLIB@
 LIBOBJS = @LIBOBJS@
@@ -296,6 +461,7 @@ LN_S = @LN_S@
 LTLIBOBJS = @LTLIBOBJS@
 MAKEINFO = @MAKEINFO@
 MANIFEST_TOOL = @MANIFEST_TOOL@
+MATH_LIBS = @MATH_LIBS@
 MKDIR_P = @MKDIR_P@
 NM = @NM@
 NMEDIT = @NMEDIT@
@@ -329,9 +495,14 @@ SHELL = @SHELL@
 SSH_LIBS = @SSH_LIBS@
 SSL_LIBS = @SSL_LIBS@
 STRIP = @STRIP@
+TELNET_LIBS = @TELNET_LIBS@
+TERMINAL_INCLUDE = @TERMINAL_INCLUDE@
+TERMINAL_LTLIB = @TERMINAL_LTLIB@
+UUID_LIBS = @UUID_LIBS@
 VERSION = @VERSION@
 VNC_LIBS = @VNC_LIBS@
 VORBIS_LIBS = @VORBIS_LIBS@
+WEBP_LIBS = @WEBP_LIBS@
 abs_builddir = @abs_builddir@
 abs_srcdir = @abs_srcdir@
 abs_top_builddir = @abs_top_builddir@
@@ -387,51 +558,170 @@ top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
 AUTOMAKE_OPTIONS = foreign
 ACLOCAL_AMFLAGS = -I m4
-AM_CFLAGS = -Werror -Wall -Iinclude @LIBGUAC_INCLUDE@
 lib_LTLIBRARIES = libguac-client-rdp.la
-libguac_client_rdp_la_SOURCES = audio.c client.c default_pointer.c \
-	guac_handlers.c rdp_bitmap.c rdp_cliprdr.c rdp_gdi.c \
-	rdp_glyph.c rdp_keymap_base.c rdp_keymap.c rdp_keymap_de_de.c \
-	rdp_keymap_failsafe.c rdp_keymap_fr_fr.c rdp_keymap_en_us.c \
-	rdp_pointer.c rdp_settings.c wav_encoder.c $(am__append_4)
+libguac_client_rdp_la_SOURCES = _generated_keymaps.c client.c \
+	guac_handlers.c rdp_bitmap.c rdp_cliprdr.c rdp_color.c \
+	rdp_fs.c rdp_gdi.c rdp_glyph.c rdp_keymap.c rdp_pointer.c \
+	rdp_rail.c rdp_settings.c rdp_stream.c rdp_svc.c resolution.c \
+	unicode.c $(am__append_2) $(am__append_7) $(am__append_8)
+guacsvc_sources = guac_svc/svc_service.c rdp_svc.c $(am__append_3)
 guacsnd_sources = guac_rdpsnd/rdpsnd_messages.c \
-	guac_rdpsnd/rdpsnd_service.c audio.c $(am__append_2)
-guacdr_sources = guac_rdpdr/rdpdr_messages.c \
-	guac_rdpdr/rdpdr_printer.c guac_rdpdr/rdpdr_service.c \
-	$(am__append_3)
-noinst_HEADERS = compat/client-cliprdr.h guac_rdpdr/rdpdr_messages.h \
+	guac_rdpsnd/rdpsnd_service.c $(am__append_4)
+guacdr_sources = guac_rdpdr/rdpdr_fs_messages.c \
+	guac_rdpdr/rdpdr_fs_messages_dir_info.c \
+	guac_rdpdr/rdpdr_fs_messages_file_info.c \
+	guac_rdpdr/rdpdr_fs_messages_vol_info.c \
+	guac_rdpdr/rdpdr_fs_service.c guac_rdpdr/rdpdr_messages.c \
+	guac_rdpdr/rdpdr_printer.c guac_rdpdr/rdpdr_service.c rdp_fs.c \
+	rdp_stream.c unicode.c $(am__append_5)
+noinst_HEADERS = compat/client-cliprdr.h compat/rail.h \
+	guac_rdpdr/rdpdr_fs_messages.h \
+	guac_rdpdr/rdpdr_fs_messages_dir_info.h \
+	guac_rdpdr/rdpdr_fs_messages_file_info.h \
+	guac_rdpdr/rdpdr_fs_messages_vol_info.h \
+	guac_rdpdr/rdpdr_fs_service.h guac_rdpdr/rdpdr_messages.h \
 	guac_rdpdr/rdpdr_printer.h guac_rdpdr/rdpdr_service.h \
 	guac_rdpsnd/rdpsnd_messages.h guac_rdpsnd/rdpsnd_service.h \
-	audio.h client.h config.h default_pointer.h guac_handlers.h \
-	rdp_bitmap.h rdp_cliprdr.h rdp_gdi.h rdp_glyph.h rdp_keymap.h \
-	rdp_pointer.h rdp_settings.h wav_encoder.h $(am__append_1) \
-	$(am__append_5)
-libguac_client_rdp_la_LDFLAGS = -version-info 0:0:0 @RDP_LIBS@ @VORBIS_LIBS@ @PTHREAD_LIBS@ @CAIRO_LIBS@
-guacsnd_ldflags = -module -avoid-version -shared @RDP_LIBS@ @VORBIS_LIBS@ @PTHREAD_LIBS@
-guacdr_ldflags = -module -avoid-version -shared @RDP_LIBS@ @PTHREAD_LIBS@
-libguac_client_rdp_la_LIBADD = @LIBGUAC_LTLIB@
-guacsnd_libadd = @LIBGUAC_LTLIB@
-guacdr_libadd = @LIBGUAC_LTLIB@
+	guac_svc/svc_service.h client.h guac_handlers.h rdp_bitmap.h \
+	rdp_cliprdr.h rdp_color.h rdp_fs.h rdp_gdi.h rdp_glyph.h \
+	rdp_keymap.h rdp_pointer.h rdp_rail.h rdp_settings.h \
+	rdp_status.h rdp_stream.h rdp_svc.h resolution.h unicode.h \
+	$(am__append_1) $(am__append_6) $(am__append_9)
+
+#
+# Main RDP client library
+#
+libguac_client_rdp_la_CFLAGS = \
+    -Werror -Wall -Iinclude    \
+    @COMMON_INCLUDE@           \
+    @COMMON_SSH_INCLUDE@       \
+    @LIBGUAC_INCLUDE@
+
+libguac_client_rdp_la_LDFLAGS = \
+    -version-info 0:0:0         \
+    @CAIRO_LIBS@                \
+    @PTHREAD_LIBS@              \
+    @RDP_LIBS@
+
+libguac_client_rdp_la_LIBADD = @COMMON_LTLIB@ @LIBGUAC_LTLIB@ \
+	$(am__append_10)
+
+#
+# RDPDR
+#
+guacdr_cflags = \
+    -Werror -Wall -Iinclude    \
+    @COMMON_INCLUDE@           \
+    @COMMON_SSH_INCLUDE@       \
+    @LIBGUAC_INCLUDE@
+
+guacdr_ldflags = \
+    -module -avoid-version -shared \
+    @PTHREAD_LIBS@                 \
+    @RDP_LIBS@
+
+guacdr_libadd = \
+    @COMMON_LTLIB@  \
+    @LIBGUAC_LTLIB@
+
+
+#
+# RDPSND
+#
+guacsnd_cflags = \
+    -Werror -Wall -Iinclude    \
+    @COMMON_INCLUDE@           \
+    @COMMON_SSH_INCLUDE@       \
+    @LIBGUAC_INCLUDE@
+
+guacsnd_ldflags = \
+    -module -avoid-version -shared \
+    @PTHREAD_LIBS@                 \
+    @RDP_LIBS@
+
+guacsnd_libadd = \
+    @COMMON_LTLIB@  \
+    @LIBGUAC_LTLIB@
+
+
+#
+# Static Virtual Channels
+#
+guacsvc_cflags = \
+    -Werror -Wall -Iinclude    \
+    @COMMON_INCLUDE@           \
+    @COMMON_SSH_INCLUDE@       \
+    @LIBGUAC_INCLUDE@
+
+guacsvc_ldflags = \
+    -module -avoid-version -shared \
+    @PTHREAD_LIBS@                 \
+    @RDP_LIBS@
+
+guacsvc_libadd = \
+    @COMMON_LTLIB@  \
+    @LIBGUAC_LTLIB@
+
+
+#
+# Autogenerate keymaps
+#
+CLEANFILES = _generated_keymaps.c
+BUILT_SOURCES = _generated_keymaps.c
+rdp_keymaps = \
+    keymaps/base.keymap         \
+    keymaps/failsafe.keymap     \
+    keymaps/de_de_qwertz.keymap \
+    keymaps/en_us_qwerty.keymap \
+    keymaps/fr_fr_azerty.keymap \
+    keymaps/it_it_qwerty.keymap \
+    keymaps/sv_se_qwerty.keymap
+
+EXTRA_DIST = \
+    $(rdp_keymaps)      \
+    keymaps/generate.pl
+
 
 # FreeRDP 1.1 (and hopefully onward) extensions
- at LEGACY_FREERDP_EXTENSIONS_FALSE@freerdp_LTLIBRARIES = guacsnd-client.la guacdr-client.la
+ at LEGACY_FREERDP_EXTENSIONS_FALSE@freerdp_LTLIBRARIES = \
+ at LEGACY_FREERDP_EXTENSIONS_FALSE@    guacdr-client.la  \
+ at LEGACY_FREERDP_EXTENSIONS_FALSE@    guacsnd-client.la \
+ at LEGACY_FREERDP_EXTENSIONS_FALSE@    guacsvc-client.la
+
 
 # FreeRDP 1.0-style extensions
- at LEGACY_FREERDP_EXTENSIONS_TRUE@freerdp_LTLIBRARIES = guacsnd.la guacdr.la
- at LEGACY_FREERDP_EXTENSIONS_TRUE@guacsnd_la_SOURCES = ${guacsnd_sources}
- at LEGACY_FREERDP_EXTENSIONS_TRUE@guacsnd_la_LDFLAGS = ${guacsnd_ldflags}
- at LEGACY_FREERDP_EXTENSIONS_TRUE@guacsnd_la_LIBADD = ${guacsnd_libadd}
+ at LEGACY_FREERDP_EXTENSIONS_TRUE@freerdp_LTLIBRARIES = \
+ at LEGACY_FREERDP_EXTENSIONS_TRUE@    guacdr.la         \
+ at LEGACY_FREERDP_EXTENSIONS_TRUE@    guacsnd.la        \
+ at LEGACY_FREERDP_EXTENSIONS_TRUE@    guacsvc.la
+
 @LEGACY_FREERDP_EXTENSIONS_TRUE at guacdr_la_SOURCES = ${guacdr_sources}
+ at LEGACY_FREERDP_EXTENSIONS_TRUE@guacdr_la_CFLAGS = ${guacdr_cflags}
 @LEGACY_FREERDP_EXTENSIONS_TRUE at guacdr_la_LDFLAGS = ${guacdr_ldflags}
 @LEGACY_FREERDP_EXTENSIONS_TRUE at guacdr_la_LIBADD = ${guacdr_libadd}
- at LEGACY_FREERDP_EXTENSIONS_FALSE@guacsnd_client_la_SOURCES = ${guacsnd_sources}
- at LEGACY_FREERDP_EXTENSIONS_FALSE@guacsnd_client_la_LDFLAGS = ${guacsnd_ldflags}
- at LEGACY_FREERDP_EXTENSIONS_FALSE@guacsnd_client_la_LIBADD = ${guacsnd_libadd}
+ at LEGACY_FREERDP_EXTENSIONS_TRUE@guacsnd_la_SOURCES = ${guacsnd_sources}
+ at LEGACY_FREERDP_EXTENSIONS_TRUE@guacsnd_la_CFLAGS = ${guacsnd_cflags}
+ at LEGACY_FREERDP_EXTENSIONS_TRUE@guacsnd_la_LDFLAGS = ${guacsnd_ldflags}
+ at LEGACY_FREERDP_EXTENSIONS_TRUE@guacsnd_la_LIBADD = ${guacsnd_libadd}
+ at LEGACY_FREERDP_EXTENSIONS_TRUE@guacsvc_la_SOURCES = ${guacsvc_sources}
+ at LEGACY_FREERDP_EXTENSIONS_TRUE@guacsvc_la_CFLAGS = ${guacsvc_cflags}
+ at LEGACY_FREERDP_EXTENSIONS_TRUE@guacsvc_la_LDFLAGS = ${guacsvc_ldflags}
+ at LEGACY_FREERDP_EXTENSIONS_TRUE@guacsvc_la_LIBADD = ${guacsvc_libadd}
 @LEGACY_FREERDP_EXTENSIONS_FALSE at guacdr_client_la_SOURCES = ${guacdr_sources}
+ at LEGACY_FREERDP_EXTENSIONS_FALSE@guacdr_client_la_CFLAGS = ${guacdr_cflags}
 @LEGACY_FREERDP_EXTENSIONS_FALSE at guacdr_client_la_LDFLAGS = ${guacdr_ldflags}
 @LEGACY_FREERDP_EXTENSIONS_FALSE at guacdr_client_la_LIBADD = ${guacdr_libadd}
+ at LEGACY_FREERDP_EXTENSIONS_FALSE@guacsnd_client_la_SOURCES = ${guacsnd_sources}
+ at LEGACY_FREERDP_EXTENSIONS_FALSE@guacsnd_client_la_CFLAGS = ${guacsnd_cflags}
+ at LEGACY_FREERDP_EXTENSIONS_FALSE@guacsnd_client_la_LDFLAGS = ${guacsnd_ldflags}
+ at LEGACY_FREERDP_EXTENSIONS_FALSE@guacsnd_client_la_LIBADD = ${guacsnd_libadd}
+ at LEGACY_FREERDP_EXTENSIONS_FALSE@guacsvc_client_la_SOURCES = ${guacsvc_sources}
+ at LEGACY_FREERDP_EXTENSIONS_FALSE@guacsvc_client_la_CFLAGS = ${guacsvc_cflags}
+ at LEGACY_FREERDP_EXTENSIONS_FALSE@guacsvc_client_la_LDFLAGS = ${guacsvc_ldflags}
+ at LEGACY_FREERDP_EXTENSIONS_FALSE@guacsvc_client_la_LIBADD = ${guacsvc_libadd}
 freerdpdir = ${libdir}/freerdp
-all: all-am
+all: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) all-am
 
 .SUFFIXES:
 .SUFFIXES: .c .lo .o .obj
@@ -465,6 +755,7 @@ $(top_srcdir)/configure:  $(am__configure_deps)
 $(ACLOCAL_M4):  $(am__aclocal_m4_deps)
 	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
 $(am__aclocal_m4_deps):
+
 install-freerdpLTLIBRARIES: $(freerdp_LTLIBRARIES)
 	@$(NORMAL_INSTALL)
 	@list='$(freerdp_LTLIBRARIES)'; test -n "$(freerdpdir)" || list=; \
@@ -491,12 +782,15 @@ uninstall-freerdpLTLIBRARIES:
 
 clean-freerdpLTLIBRARIES:
 	-test -z "$(freerdp_LTLIBRARIES)" || rm -f $(freerdp_LTLIBRARIES)
-	@list='$(freerdp_LTLIBRARIES)'; for p in $$list; do \
-	  dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
-	  test "$$dir" != "$$p" || dir=.; \
-	  echo "rm -f \"$${dir}/so_locations\""; \
-	  rm -f "$${dir}/so_locations"; \
-	done
+	@list='$(freerdp_LTLIBRARIES)'; \
+	locs=`for p in $$list; do echo $$p; done | \
+	      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+	      sort -u`; \
+	test -z "$$locs" || { \
+	  echo rm -f $${locs}; \
+	  rm -f $${locs}; \
+	}
+
 install-libLTLIBRARIES: $(lib_LTLIBRARIES)
 	@$(NORMAL_INSTALL)
 	@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
@@ -523,143 +817,638 @@ uninstall-libLTLIBRARIES:
 
 clean-libLTLIBRARIES:
 	-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
-	@list='$(lib_LTLIBRARIES)'; for p in $$list; do \
-	  dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
-	  test "$$dir" != "$$p" || dir=.; \
-	  echo "rm -f \"$${dir}/so_locations\""; \
-	  rm -f "$${dir}/so_locations"; \
-	done
+	@list='$(lib_LTLIBRARIES)'; \
+	locs=`for p in $$list; do echo $$p; done | \
+	      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+	      sort -u`; \
+	test -z "$$locs" || { \
+	  echo rm -f $${locs}; \
+	  rm -f $${locs}; \
+	}
+guac_rdpdr/$(am__dirstamp):
+	@$(MKDIR_P) guac_rdpdr
+	@: > guac_rdpdr/$(am__dirstamp)
+guac_rdpdr/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) guac_rdpdr/$(DEPDIR)
+	@: > guac_rdpdr/$(DEPDIR)/$(am__dirstamp)
+guac_rdpdr/guacdr_client_la-rdpdr_fs_messages.lo:  \
+	guac_rdpdr/$(am__dirstamp) \
+	guac_rdpdr/$(DEPDIR)/$(am__dirstamp)
+guac_rdpdr/guacdr_client_la-rdpdr_fs_messages_dir_info.lo:  \
+	guac_rdpdr/$(am__dirstamp) \
+	guac_rdpdr/$(DEPDIR)/$(am__dirstamp)
+guac_rdpdr/guacdr_client_la-rdpdr_fs_messages_file_info.lo:  \
+	guac_rdpdr/$(am__dirstamp) \
+	guac_rdpdr/$(DEPDIR)/$(am__dirstamp)
+guac_rdpdr/guacdr_client_la-rdpdr_fs_messages_vol_info.lo:  \
+	guac_rdpdr/$(am__dirstamp) \
+	guac_rdpdr/$(DEPDIR)/$(am__dirstamp)
+guac_rdpdr/guacdr_client_la-rdpdr_fs_service.lo:  \
+	guac_rdpdr/$(am__dirstamp) \
+	guac_rdpdr/$(DEPDIR)/$(am__dirstamp)
+guac_rdpdr/guacdr_client_la-rdpdr_messages.lo:  \
+	guac_rdpdr/$(am__dirstamp) \
+	guac_rdpdr/$(DEPDIR)/$(am__dirstamp)
+guac_rdpdr/guacdr_client_la-rdpdr_printer.lo:  \
+	guac_rdpdr/$(am__dirstamp) \
+	guac_rdpdr/$(DEPDIR)/$(am__dirstamp)
+guac_rdpdr/guacdr_client_la-rdpdr_service.lo:  \
+	guac_rdpdr/$(am__dirstamp) \
+	guac_rdpdr/$(DEPDIR)/$(am__dirstamp)
+compat/$(am__dirstamp):
+	@$(MKDIR_P) compat
+	@: > compat/$(am__dirstamp)
+compat/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) compat/$(DEPDIR)
+	@: > compat/$(DEPDIR)/$(am__dirstamp)
+compat/guacdr_client_la-winpr-stream.lo: compat/$(am__dirstamp) \
+	compat/$(DEPDIR)/$(am__dirstamp)
+
 guacdr-client.la: $(guacdr_client_la_OBJECTS) $(guacdr_client_la_DEPENDENCIES) $(EXTRA_guacdr_client_la_DEPENDENCIES) 
-	$(guacdr_client_la_LINK) $(am_guacdr_client_la_rpath) $(guacdr_client_la_OBJECTS) $(guacdr_client_la_LIBADD) $(LIBS)
+	$(AM_V_CCLD)$(guacdr_client_la_LINK) $(am_guacdr_client_la_rpath) $(guacdr_client_la_OBJECTS) $(guacdr_client_la_LIBADD) $(LIBS)
+guac_rdpdr/guacdr_la-rdpdr_fs_messages.lo: guac_rdpdr/$(am__dirstamp) \
+	guac_rdpdr/$(DEPDIR)/$(am__dirstamp)
+guac_rdpdr/guacdr_la-rdpdr_fs_messages_dir_info.lo:  \
+	guac_rdpdr/$(am__dirstamp) \
+	guac_rdpdr/$(DEPDIR)/$(am__dirstamp)
+guac_rdpdr/guacdr_la-rdpdr_fs_messages_file_info.lo:  \
+	guac_rdpdr/$(am__dirstamp) \
+	guac_rdpdr/$(DEPDIR)/$(am__dirstamp)
+guac_rdpdr/guacdr_la-rdpdr_fs_messages_vol_info.lo:  \
+	guac_rdpdr/$(am__dirstamp) \
+	guac_rdpdr/$(DEPDIR)/$(am__dirstamp)
+guac_rdpdr/guacdr_la-rdpdr_fs_service.lo: guac_rdpdr/$(am__dirstamp) \
+	guac_rdpdr/$(DEPDIR)/$(am__dirstamp)
+guac_rdpdr/guacdr_la-rdpdr_messages.lo: guac_rdpdr/$(am__dirstamp) \
+	guac_rdpdr/$(DEPDIR)/$(am__dirstamp)
+guac_rdpdr/guacdr_la-rdpdr_printer.lo: guac_rdpdr/$(am__dirstamp) \
+	guac_rdpdr/$(DEPDIR)/$(am__dirstamp)
+guac_rdpdr/guacdr_la-rdpdr_service.lo: guac_rdpdr/$(am__dirstamp) \
+	guac_rdpdr/$(DEPDIR)/$(am__dirstamp)
+compat/guacdr_la-winpr-stream.lo: compat/$(am__dirstamp) \
+	compat/$(DEPDIR)/$(am__dirstamp)
+
 guacdr.la: $(guacdr_la_OBJECTS) $(guacdr_la_DEPENDENCIES) $(EXTRA_guacdr_la_DEPENDENCIES) 
-	$(guacdr_la_LINK) $(am_guacdr_la_rpath) $(guacdr_la_OBJECTS) $(guacdr_la_LIBADD) $(LIBS)
+	$(AM_V_CCLD)$(guacdr_la_LINK) $(am_guacdr_la_rpath) $(guacdr_la_OBJECTS) $(guacdr_la_LIBADD) $(LIBS)
+guac_rdpsnd/$(am__dirstamp):
+	@$(MKDIR_P) guac_rdpsnd
+	@: > guac_rdpsnd/$(am__dirstamp)
+guac_rdpsnd/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) guac_rdpsnd/$(DEPDIR)
+	@: > guac_rdpsnd/$(DEPDIR)/$(am__dirstamp)
+guac_rdpsnd/guacsnd_client_la-rdpsnd_messages.lo:  \
+	guac_rdpsnd/$(am__dirstamp) \
+	guac_rdpsnd/$(DEPDIR)/$(am__dirstamp)
+guac_rdpsnd/guacsnd_client_la-rdpsnd_service.lo:  \
+	guac_rdpsnd/$(am__dirstamp) \
+	guac_rdpsnd/$(DEPDIR)/$(am__dirstamp)
+compat/guacsnd_client_la-winpr-stream.lo: compat/$(am__dirstamp) \
+	compat/$(DEPDIR)/$(am__dirstamp)
+
 guacsnd-client.la: $(guacsnd_client_la_OBJECTS) $(guacsnd_client_la_DEPENDENCIES) $(EXTRA_guacsnd_client_la_DEPENDENCIES) 
-	$(guacsnd_client_la_LINK) $(am_guacsnd_client_la_rpath) $(guacsnd_client_la_OBJECTS) $(guacsnd_client_la_LIBADD) $(LIBS)
+	$(AM_V_CCLD)$(guacsnd_client_la_LINK) $(am_guacsnd_client_la_rpath) $(guacsnd_client_la_OBJECTS) $(guacsnd_client_la_LIBADD) $(LIBS)
+guac_rdpsnd/guacsnd_la-rdpsnd_messages.lo:  \
+	guac_rdpsnd/$(am__dirstamp) \
+	guac_rdpsnd/$(DEPDIR)/$(am__dirstamp)
+guac_rdpsnd/guacsnd_la-rdpsnd_service.lo: guac_rdpsnd/$(am__dirstamp) \
+	guac_rdpsnd/$(DEPDIR)/$(am__dirstamp)
+compat/guacsnd_la-winpr-stream.lo: compat/$(am__dirstamp) \
+	compat/$(DEPDIR)/$(am__dirstamp)
+
 guacsnd.la: $(guacsnd_la_OBJECTS) $(guacsnd_la_DEPENDENCIES) $(EXTRA_guacsnd_la_DEPENDENCIES) 
-	$(guacsnd_la_LINK) $(am_guacsnd_la_rpath) $(guacsnd_la_OBJECTS) $(guacsnd_la_LIBADD) $(LIBS)
+	$(AM_V_CCLD)$(guacsnd_la_LINK) $(am_guacsnd_la_rpath) $(guacsnd_la_OBJECTS) $(guacsnd_la_LIBADD) $(LIBS)
+guac_svc/$(am__dirstamp):
+	@$(MKDIR_P) guac_svc
+	@: > guac_svc/$(am__dirstamp)
+guac_svc/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) guac_svc/$(DEPDIR)
+	@: > guac_svc/$(DEPDIR)/$(am__dirstamp)
+guac_svc/guacsvc_client_la-svc_service.lo: guac_svc/$(am__dirstamp) \
+	guac_svc/$(DEPDIR)/$(am__dirstamp)
+compat/guacsvc_client_la-winpr-stream.lo: compat/$(am__dirstamp) \
+	compat/$(DEPDIR)/$(am__dirstamp)
+
+guacsvc-client.la: $(guacsvc_client_la_OBJECTS) $(guacsvc_client_la_DEPENDENCIES) $(EXTRA_guacsvc_client_la_DEPENDENCIES) 
+	$(AM_V_CCLD)$(guacsvc_client_la_LINK) $(am_guacsvc_client_la_rpath) $(guacsvc_client_la_OBJECTS) $(guacsvc_client_la_LIBADD) $(LIBS)
+guac_svc/guacsvc_la-svc_service.lo: guac_svc/$(am__dirstamp) \
+	guac_svc/$(DEPDIR)/$(am__dirstamp)
+compat/guacsvc_la-winpr-stream.lo: compat/$(am__dirstamp) \
+	compat/$(DEPDIR)/$(am__dirstamp)
+
+guacsvc.la: $(guacsvc_la_OBJECTS) $(guacsvc_la_DEPENDENCIES) $(EXTRA_guacsvc_la_DEPENDENCIES) 
+	$(AM_V_CCLD)$(guacsvc_la_LINK) $(am_guacsvc_la_rpath) $(guacsvc_la_OBJECTS) $(guacsvc_la_LIBADD) $(LIBS)
+compat/libguac_client_rdp_la-winpr-stream.lo: compat/$(am__dirstamp) \
+	compat/$(DEPDIR)/$(am__dirstamp)
+
 libguac-client-rdp.la: $(libguac_client_rdp_la_OBJECTS) $(libguac_client_rdp_la_DEPENDENCIES) $(EXTRA_libguac_client_rdp_la_DEPENDENCIES) 
-	$(libguac_client_rdp_la_LINK) -rpath $(libdir) $(libguac_client_rdp_la_OBJECTS) $(libguac_client_rdp_la_LIBADD) $(LIBS)
+	$(AM_V_CCLD)$(libguac_client_rdp_la_LINK) -rpath $(libdir) $(libguac_client_rdp_la_OBJECTS) $(libguac_client_rdp_la_LIBADD) $(LIBS)
 
 mostlyclean-compile:
 	-rm -f *.$(OBJEXT)
+	-rm -f compat/*.$(OBJEXT)
+	-rm -f compat/*.lo
+	-rm -f guac_rdpdr/*.$(OBJEXT)
+	-rm -f guac_rdpdr/*.lo
+	-rm -f guac_rdpsnd/*.$(OBJEXT)
+	-rm -f guac_rdpsnd/*.lo
+	-rm -f guac_svc/*.$(OBJEXT)
+	-rm -f guac_svc/*.lo
 
 distclean-compile:
 	-rm -f *.tab.c
 
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/audio.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/client.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/default_pointer.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/guac_handlers.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/ogg_encoder.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/rdp_bitmap.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/rdp_cliprdr.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/rdp_gdi.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/rdp_glyph.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/rdp_keymap.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/rdp_keymap_base.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/rdp_keymap_de_de.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/rdp_keymap_en_us.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/rdp_keymap_failsafe.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/rdp_keymap_fr_fr.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/rdp_pointer.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/rdp_settings.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/rdpdr_messages.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/rdpdr_printer.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/rdpdr_service.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/rdpsnd_messages.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/rdpsnd_service.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/wav_encoder.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/winpr-stream.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/guacdr_client_la-rdp_fs.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/guacdr_client_la-rdp_stream.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/guacdr_client_la-unicode.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/guacdr_la-rdp_fs.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/guacdr_la-rdp_stream.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/guacdr_la-unicode.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/guacsvc_client_la-rdp_svc.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/guacsvc_la-rdp_svc.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_rdp_la-_generated_keymaps.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_rdp_la-client.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_rdp_la-guac_handlers.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_rdp_la-rdp_bitmap.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_rdp_la-rdp_cliprdr.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_rdp_la-rdp_color.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_rdp_la-rdp_disp.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_rdp_la-rdp_fs.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_rdp_la-rdp_gdi.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_rdp_la-rdp_glyph.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_rdp_la-rdp_keymap.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_rdp_la-rdp_pointer.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_rdp_la-rdp_rail.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_rdp_la-rdp_settings.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_rdp_la-rdp_stream.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_rdp_la-rdp_svc.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_rdp_la-resolution.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_rdp_la-sftp.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_rdp_la-unicode.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at compat/$(DEPDIR)/guacdr_client_la-winpr-stream.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at compat/$(DEPDIR)/guacdr_la-winpr-stream.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at compat/$(DEPDIR)/guacsnd_client_la-winpr-stream.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at compat/$(DEPDIR)/guacsnd_la-winpr-stream.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at compat/$(DEPDIR)/guacsvc_client_la-winpr-stream.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at compat/$(DEPDIR)/guacsvc_la-winpr-stream.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at compat/$(DEPDIR)/libguac_client_rdp_la-winpr-stream.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_fs_messages.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_fs_messages_dir_info.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_fs_messages_file_info.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_fs_messages_vol_info.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_fs_service.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_messages.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_printer.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_service.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_fs_messages.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_fs_messages_dir_info.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_fs_messages_file_info.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_fs_messages_vol_info.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_fs_service.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_messages.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_printer.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_service.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at guac_rdpsnd/$(DEPDIR)/guacsnd_client_la-rdpsnd_messages.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at guac_rdpsnd/$(DEPDIR)/guacsnd_client_la-rdpsnd_service.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at guac_rdpsnd/$(DEPDIR)/guacsnd_la-rdpsnd_messages.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at guac_rdpsnd/$(DEPDIR)/guacsnd_la-rdpsnd_service.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at guac_svc/$(DEPDIR)/guacsvc_client_la-svc_service.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at guac_svc/$(DEPDIR)/guacsvc_la-svc_service.Plo at am__quote@
 
 .c.o:
- at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+ at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(COMPILE) -c $<
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(COMPILE) -c -o $@ $<
 
 .c.obj:
- at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+ at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(COMPILE) -c `$(CYGPATH_W) '$<'`
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
 
 .c.lo:
- at am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+ at am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+guac_rdpdr/guacdr_client_la-rdpdr_fs_messages.lo: guac_rdpdr/rdpdr_fs_messages.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_client_la_CFLAGS) $(CFLAGS) -MT guac_rdpdr/guacdr_client_la-rdpdr_fs_messages.lo -MD -MP -MF guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_fs_messages.Tpo -c -o guac_rdpdr/guacdr_client_la-rdpdr_fs_messages.lo `test -f 'guac_rdpdr/rdpdr_fs_messages.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_fs_messages.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_fs_messages.Tpo guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_fs_messages.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_rdpdr/rdpdr_fs_messages.c' object='guac_rdpdr/guacdr_client_la-rdpdr_fs_messages.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_client_la_CFLAGS) $(CFLAGS) -c -o guac_rdpdr/guacdr_client_la-rdpdr_fs_messages.lo `test -f 'guac_rdpdr/rdpdr_fs_messages.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_fs_messages.c
+
+guac_rdpdr/guacdr_client_la-rdpdr_fs_messages_dir_info.lo: guac_rdpdr/rdpdr_fs_messages_dir_info.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_client_la_CFLAGS) $(CFLAGS) -MT guac_rdpdr/guacdr_client_la-rdpdr_fs_messages_dir_info.lo -MD -MP -MF guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_fs_messages_dir_info.Tpo -c -o guac_rdpdr/guacdr_client_la-rdpdr_fs_messages_dir_info.lo `test -f 'guac_rdpdr/rdpdr_fs_messages_dir_info.c' || echo '$(s [...]
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_fs_messages_dir_info.Tpo guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_fs_messages_dir_info.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_rdpdr/rdpdr_fs_messages_dir_info.c' object='guac_rdpdr/guacdr_client_la-rdpdr_fs_messages_dir_info.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_client_la_CFLAGS) $(CFLAGS) -c -o guac_rdpdr/guacdr_client_la-rdpdr_fs_messages_dir_info.lo `test -f 'guac_rdpdr/rdpdr_fs_messages_dir_info.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_fs_messages_dir_info.c
+
+guac_rdpdr/guacdr_client_la-rdpdr_fs_messages_file_info.lo: guac_rdpdr/rdpdr_fs_messages_file_info.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_client_la_CFLAGS) $(CFLAGS) -MT guac_rdpdr/guacdr_client_la-rdpdr_fs_messages_file_info.lo -MD -MP -MF guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_fs_messages_file_info.Tpo -c -o guac_rdpdr/guacdr_client_la-rdpdr_fs_messages_file_info.lo `test -f 'guac_rdpdr/rdpdr_fs_messages_file_info.c' || echo  [...]
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_fs_messages_file_info.Tpo guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_fs_messages_file_info.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_rdpdr/rdpdr_fs_messages_file_info.c' object='guac_rdpdr/guacdr_client_la-rdpdr_fs_messages_file_info.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_client_la_CFLAGS) $(CFLAGS) -c -o guac_rdpdr/guacdr_client_la-rdpdr_fs_messages_file_info.lo `test -f 'guac_rdpdr/rdpdr_fs_messages_file_info.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_fs_messages_file_info.c
+
+guac_rdpdr/guacdr_client_la-rdpdr_fs_messages_vol_info.lo: guac_rdpdr/rdpdr_fs_messages_vol_info.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_client_la_CFLAGS) $(CFLAGS) -MT guac_rdpdr/guacdr_client_la-rdpdr_fs_messages_vol_info.lo -MD -MP -MF guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_fs_messages_vol_info.Tpo -c -o guac_rdpdr/guacdr_client_la-rdpdr_fs_messages_vol_info.lo `test -f 'guac_rdpdr/rdpdr_fs_messages_vol_info.c' || echo '$(s [...]
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_fs_messages_vol_info.Tpo guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_fs_messages_vol_info.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_rdpdr/rdpdr_fs_messages_vol_info.c' object='guac_rdpdr/guacdr_client_la-rdpdr_fs_messages_vol_info.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_client_la_CFLAGS) $(CFLAGS) -c -o guac_rdpdr/guacdr_client_la-rdpdr_fs_messages_vol_info.lo `test -f 'guac_rdpdr/rdpdr_fs_messages_vol_info.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_fs_messages_vol_info.c
+
+guac_rdpdr/guacdr_client_la-rdpdr_fs_service.lo: guac_rdpdr/rdpdr_fs_service.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_client_la_CFLAGS) $(CFLAGS) -MT guac_rdpdr/guacdr_client_la-rdpdr_fs_service.lo -MD -MP -MF guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_fs_service.Tpo -c -o guac_rdpdr/guacdr_client_la-rdpdr_fs_service.lo `test -f 'guac_rdpdr/rdpdr_fs_service.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_fs_service.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_fs_service.Tpo guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_fs_service.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_rdpdr/rdpdr_fs_service.c' object='guac_rdpdr/guacdr_client_la-rdpdr_fs_service.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_client_la_CFLAGS) $(CFLAGS) -c -o guac_rdpdr/guacdr_client_la-rdpdr_fs_service.lo `test -f 'guac_rdpdr/rdpdr_fs_service.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_fs_service.c
+
+guac_rdpdr/guacdr_client_la-rdpdr_messages.lo: guac_rdpdr/rdpdr_messages.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_client_la_CFLAGS) $(CFLAGS) -MT guac_rdpdr/guacdr_client_la-rdpdr_messages.lo -MD -MP -MF guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_messages.Tpo -c -o guac_rdpdr/guacdr_client_la-rdpdr_messages.lo `test -f 'guac_rdpdr/rdpdr_messages.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_messages.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_messages.Tpo guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_messages.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_rdpdr/rdpdr_messages.c' object='guac_rdpdr/guacdr_client_la-rdpdr_messages.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_client_la_CFLAGS) $(CFLAGS) -c -o guac_rdpdr/guacdr_client_la-rdpdr_messages.lo `test -f 'guac_rdpdr/rdpdr_messages.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_messages.c
+
+guac_rdpdr/guacdr_client_la-rdpdr_printer.lo: guac_rdpdr/rdpdr_printer.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_client_la_CFLAGS) $(CFLAGS) -MT guac_rdpdr/guacdr_client_la-rdpdr_printer.lo -MD -MP -MF guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_printer.Tpo -c -o guac_rdpdr/guacdr_client_la-rdpdr_printer.lo `test -f 'guac_rdpdr/rdpdr_printer.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_printer.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_printer.Tpo guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_printer.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_rdpdr/rdpdr_printer.c' object='guac_rdpdr/guacdr_client_la-rdpdr_printer.lo' libtool=yes @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_client_la_CFLAGS) $(CFLAGS) -c -o guac_rdpdr/guacdr_client_la-rdpdr_printer.lo `test -f 'guac_rdpdr/rdpdr_printer.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_printer.c
 
-rdpdr_messages.lo: guac_rdpdr/rdpdr_messages.c
- at am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rdpdr_messages.lo -MD -MP -MF $(DEPDIR)/rdpdr_messages.Tpo -c -o rdpdr_messages.lo `test -f 'guac_rdpdr/rdpdr_messages.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_messages.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/rdpdr_messages.Tpo $(DEPDIR)/rdpdr_messages.Plo
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='guac_rdpdr/rdpdr_messages.c' object='rdpdr_messages.lo' libtool=yes @AMDEPBACKSLASH@
+guac_rdpdr/guacdr_client_la-rdpdr_service.lo: guac_rdpdr/rdpdr_service.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_client_la_CFLAGS) $(CFLAGS) -MT guac_rdpdr/guacdr_client_la-rdpdr_service.lo -MD -MP -MF guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_service.Tpo -c -o guac_rdpdr/guacdr_client_la-rdpdr_service.lo `test -f 'guac_rdpdr/rdpdr_service.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_service.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_service.Tpo guac_rdpdr/$(DEPDIR)/guacdr_client_la-rdpdr_service.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_rdpdr/rdpdr_service.c' object='guac_rdpdr/guacdr_client_la-rdpdr_service.lo' libtool=yes @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rdpdr_messages.lo `test -f 'guac_rdpdr/rdpdr_messages.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_messages.c
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_client_la_CFLAGS) $(CFLAGS) -c -o guac_rdpdr/guacdr_client_la-rdpdr_service.lo `test -f 'guac_rdpdr/rdpdr_service.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_service.c
 
-rdpdr_printer.lo: guac_rdpdr/rdpdr_printer.c
- at am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rdpdr_printer.lo -MD -MP -MF $(DEPDIR)/rdpdr_printer.Tpo -c -o rdpdr_printer.lo `test -f 'guac_rdpdr/rdpdr_printer.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_printer.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/rdpdr_printer.Tpo $(DEPDIR)/rdpdr_printer.Plo
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='guac_rdpdr/rdpdr_printer.c' object='rdpdr_printer.lo' libtool=yes @AMDEPBACKSLASH@
+guacdr_client_la-rdp_fs.lo: rdp_fs.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_client_la_CFLAGS) $(CFLAGS) -MT guacdr_client_la-rdp_fs.lo -MD -MP -MF $(DEPDIR)/guacdr_client_la-rdp_fs.Tpo -c -o guacdr_client_la-rdp_fs.lo `test -f 'rdp_fs.c' || echo '$(srcdir)/'`rdp_fs.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/guacdr_client_la-rdp_fs.Tpo $(DEPDIR)/guacdr_client_la-rdp_fs.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='rdp_fs.c' object='guacdr_client_la-rdp_fs.lo' libtool=yes @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rdpdr_printer.lo `test -f 'guac_rdpdr/rdpdr_printer.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_printer.c
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_client_la_CFLAGS) $(CFLAGS) -c -o guacdr_client_la-rdp_fs.lo `test -f 'rdp_fs.c' || echo '$(srcdir)/'`rdp_fs.c
 
-rdpdr_service.lo: guac_rdpdr/rdpdr_service.c
- at am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rdpdr_service.lo -MD -MP -MF $(DEPDIR)/rdpdr_service.Tpo -c -o rdpdr_service.lo `test -f 'guac_rdpdr/rdpdr_service.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_service.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/rdpdr_service.Tpo $(DEPDIR)/rdpdr_service.Plo
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='guac_rdpdr/rdpdr_service.c' object='rdpdr_service.lo' libtool=yes @AMDEPBACKSLASH@
+guacdr_client_la-rdp_stream.lo: rdp_stream.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_client_la_CFLAGS) $(CFLAGS) -MT guacdr_client_la-rdp_stream.lo -MD -MP -MF $(DEPDIR)/guacdr_client_la-rdp_stream.Tpo -c -o guacdr_client_la-rdp_stream.lo `test -f 'rdp_stream.c' || echo '$(srcdir)/'`rdp_stream.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/guacdr_client_la-rdp_stream.Tpo $(DEPDIR)/guacdr_client_la-rdp_stream.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='rdp_stream.c' object='guacdr_client_la-rdp_stream.lo' libtool=yes @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rdpdr_service.lo `test -f 'guac_rdpdr/rdpdr_service.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_service.c
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_client_la_CFLAGS) $(CFLAGS) -c -o guacdr_client_la-rdp_stream.lo `test -f 'rdp_stream.c' || echo '$(srcdir)/'`rdp_stream.c
 
-winpr-stream.lo: compat/winpr-stream.c
- at am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT winpr-stream.lo -MD -MP -MF $(DEPDIR)/winpr-stream.Tpo -c -o winpr-stream.lo `test -f 'compat/winpr-stream.c' || echo '$(srcdir)/'`compat/winpr-stream.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/winpr-stream.Tpo $(DEPDIR)/winpr-stream.Plo
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='compat/winpr-stream.c' object='winpr-stream.lo' libtool=yes @AMDEPBACKSLASH@
+guacdr_client_la-unicode.lo: unicode.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_client_la_CFLAGS) $(CFLAGS) -MT guacdr_client_la-unicode.lo -MD -MP -MF $(DEPDIR)/guacdr_client_la-unicode.Tpo -c -o guacdr_client_la-unicode.lo `test -f 'unicode.c' || echo '$(srcdir)/'`unicode.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/guacdr_client_la-unicode.Tpo $(DEPDIR)/guacdr_client_la-unicode.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='unicode.c' object='guacdr_client_la-unicode.lo' libtool=yes @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o winpr-stream.lo `test -f 'compat/winpr-stream.c' || echo '$(srcdir)/'`compat/winpr-stream.c
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_client_la_CFLAGS) $(CFLAGS) -c -o guacdr_client_la-unicode.lo `test -f 'unicode.c' || echo '$(srcdir)/'`unicode.c
 
-rdpsnd_messages.lo: guac_rdpsnd/rdpsnd_messages.c
- at am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rdpsnd_messages.lo -MD -MP -MF $(DEPDIR)/rdpsnd_messages.Tpo -c -o rdpsnd_messages.lo `test -f 'guac_rdpsnd/rdpsnd_messages.c' || echo '$(srcdir)/'`guac_rdpsnd/rdpsnd_messages.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/rdpsnd_messages.Tpo $(DEPDIR)/rdpsnd_messages.Plo
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='guac_rdpsnd/rdpsnd_messages.c' object='rdpsnd_messages.lo' libtool=yes @AMDEPBACKSLASH@
+compat/guacdr_client_la-winpr-stream.lo: compat/winpr-stream.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_client_la_CFLAGS) $(CFLAGS) -MT compat/guacdr_client_la-winpr-stream.lo -MD -MP -MF compat/$(DEPDIR)/guacdr_client_la-winpr-stream.Tpo -c -o compat/guacdr_client_la-winpr-stream.lo `test -f 'compat/winpr-stream.c' || echo '$(srcdir)/'`compat/winpr-stream.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) compat/$(DEPDIR)/guacdr_client_la-winpr-stream.Tpo compat/$(DEPDIR)/guacdr_client_la-winpr-stream.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='compat/winpr-stream.c' object='compat/guacdr_client_la-winpr-stream.lo' libtool=yes @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rdpsnd_messages.lo `test -f 'guac_rdpsnd/rdpsnd_messages.c' || echo '$(srcdir)/'`guac_rdpsnd/rdpsnd_messages.c
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_client_la_CFLAGS) $(CFLAGS) -c -o compat/guacdr_client_la-winpr-stream.lo `test -f 'compat/winpr-stream.c' || echo '$(srcdir)/'`compat/winpr-stream.c
 
-rdpsnd_service.lo: guac_rdpsnd/rdpsnd_service.c
- at am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rdpsnd_service.lo -MD -MP -MF $(DEPDIR)/rdpsnd_service.Tpo -c -o rdpsnd_service.lo `test -f 'guac_rdpsnd/rdpsnd_service.c' || echo '$(srcdir)/'`guac_rdpsnd/rdpsnd_service.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/rdpsnd_service.Tpo $(DEPDIR)/rdpsnd_service.Plo
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='guac_rdpsnd/rdpsnd_service.c' object='rdpsnd_service.lo' libtool=yes @AMDEPBACKSLASH@
+guac_rdpdr/guacdr_la-rdpdr_fs_messages.lo: guac_rdpdr/rdpdr_fs_messages.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_la_CFLAGS) $(CFLAGS) -MT guac_rdpdr/guacdr_la-rdpdr_fs_messages.lo -MD -MP -MF guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_fs_messages.Tpo -c -o guac_rdpdr/guacdr_la-rdpdr_fs_messages.lo `test -f 'guac_rdpdr/rdpdr_fs_messages.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_fs_messages.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_fs_messages.Tpo guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_fs_messages.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_rdpdr/rdpdr_fs_messages.c' object='guac_rdpdr/guacdr_la-rdpdr_fs_messages.lo' libtool=yes @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rdpsnd_service.lo `test -f 'guac_rdpsnd/rdpsnd_service.c' || echo '$(srcdir)/'`guac_rdpsnd/rdpsnd_service.c
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_la_CFLAGS) $(CFLAGS) -c -o guac_rdpdr/guacdr_la-rdpdr_fs_messages.lo `test -f 'guac_rdpdr/rdpdr_fs_messages.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_fs_messages.c
+
+guac_rdpdr/guacdr_la-rdpdr_fs_messages_dir_info.lo: guac_rdpdr/rdpdr_fs_messages_dir_info.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_la_CFLAGS) $(CFLAGS) -MT guac_rdpdr/guacdr_la-rdpdr_fs_messages_dir_info.lo -MD -MP -MF guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_fs_messages_dir_info.Tpo -c -o guac_rdpdr/guacdr_la-rdpdr_fs_messages_dir_info.lo `test -f 'guac_rdpdr/rdpdr_fs_messages_dir_info.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_fs [...]
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_fs_messages_dir_info.Tpo guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_fs_messages_dir_info.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_rdpdr/rdpdr_fs_messages_dir_info.c' object='guac_rdpdr/guacdr_la-rdpdr_fs_messages_dir_info.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_la_CFLAGS) $(CFLAGS) -c -o guac_rdpdr/guacdr_la-rdpdr_fs_messages_dir_info.lo `test -f 'guac_rdpdr/rdpdr_fs_messages_dir_info.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_fs_messages_dir_info.c
+
+guac_rdpdr/guacdr_la-rdpdr_fs_messages_file_info.lo: guac_rdpdr/rdpdr_fs_messages_file_info.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_la_CFLAGS) $(CFLAGS) -MT guac_rdpdr/guacdr_la-rdpdr_fs_messages_file_info.lo -MD -MP -MF guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_fs_messages_file_info.Tpo -c -o guac_rdpdr/guacdr_la-rdpdr_fs_messages_file_info.lo `test -f 'guac_rdpdr/rdpdr_fs_messages_file_info.c' || echo '$(srcdir)/'`guac_rdpdr/rdpd [...]
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_fs_messages_file_info.Tpo guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_fs_messages_file_info.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_rdpdr/rdpdr_fs_messages_file_info.c' object='guac_rdpdr/guacdr_la-rdpdr_fs_messages_file_info.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_la_CFLAGS) $(CFLAGS) -c -o guac_rdpdr/guacdr_la-rdpdr_fs_messages_file_info.lo `test -f 'guac_rdpdr/rdpdr_fs_messages_file_info.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_fs_messages_file_info.c
+
+guac_rdpdr/guacdr_la-rdpdr_fs_messages_vol_info.lo: guac_rdpdr/rdpdr_fs_messages_vol_info.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_la_CFLAGS) $(CFLAGS) -MT guac_rdpdr/guacdr_la-rdpdr_fs_messages_vol_info.lo -MD -MP -MF guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_fs_messages_vol_info.Tpo -c -o guac_rdpdr/guacdr_la-rdpdr_fs_messages_vol_info.lo `test -f 'guac_rdpdr/rdpdr_fs_messages_vol_info.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_fs [...]
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_fs_messages_vol_info.Tpo guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_fs_messages_vol_info.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_rdpdr/rdpdr_fs_messages_vol_info.c' object='guac_rdpdr/guacdr_la-rdpdr_fs_messages_vol_info.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_la_CFLAGS) $(CFLAGS) -c -o guac_rdpdr/guacdr_la-rdpdr_fs_messages_vol_info.lo `test -f 'guac_rdpdr/rdpdr_fs_messages_vol_info.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_fs_messages_vol_info.c
+
+guac_rdpdr/guacdr_la-rdpdr_fs_service.lo: guac_rdpdr/rdpdr_fs_service.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_la_CFLAGS) $(CFLAGS) -MT guac_rdpdr/guacdr_la-rdpdr_fs_service.lo -MD -MP -MF guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_fs_service.Tpo -c -o guac_rdpdr/guacdr_la-rdpdr_fs_service.lo `test -f 'guac_rdpdr/rdpdr_fs_service.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_fs_service.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_fs_service.Tpo guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_fs_service.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_rdpdr/rdpdr_fs_service.c' object='guac_rdpdr/guacdr_la-rdpdr_fs_service.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_la_CFLAGS) $(CFLAGS) -c -o guac_rdpdr/guacdr_la-rdpdr_fs_service.lo `test -f 'guac_rdpdr/rdpdr_fs_service.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_fs_service.c
+
+guac_rdpdr/guacdr_la-rdpdr_messages.lo: guac_rdpdr/rdpdr_messages.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_la_CFLAGS) $(CFLAGS) -MT guac_rdpdr/guacdr_la-rdpdr_messages.lo -MD -MP -MF guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_messages.Tpo -c -o guac_rdpdr/guacdr_la-rdpdr_messages.lo `test -f 'guac_rdpdr/rdpdr_messages.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_messages.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_messages.Tpo guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_messages.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_rdpdr/rdpdr_messages.c' object='guac_rdpdr/guacdr_la-rdpdr_messages.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_la_CFLAGS) $(CFLAGS) -c -o guac_rdpdr/guacdr_la-rdpdr_messages.lo `test -f 'guac_rdpdr/rdpdr_messages.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_messages.c
+
+guac_rdpdr/guacdr_la-rdpdr_printer.lo: guac_rdpdr/rdpdr_printer.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_la_CFLAGS) $(CFLAGS) -MT guac_rdpdr/guacdr_la-rdpdr_printer.lo -MD -MP -MF guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_printer.Tpo -c -o guac_rdpdr/guacdr_la-rdpdr_printer.lo `test -f 'guac_rdpdr/rdpdr_printer.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_printer.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_printer.Tpo guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_printer.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_rdpdr/rdpdr_printer.c' object='guac_rdpdr/guacdr_la-rdpdr_printer.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_la_CFLAGS) $(CFLAGS) -c -o guac_rdpdr/guacdr_la-rdpdr_printer.lo `test -f 'guac_rdpdr/rdpdr_printer.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_printer.c
+
+guac_rdpdr/guacdr_la-rdpdr_service.lo: guac_rdpdr/rdpdr_service.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_la_CFLAGS) $(CFLAGS) -MT guac_rdpdr/guacdr_la-rdpdr_service.lo -MD -MP -MF guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_service.Tpo -c -o guac_rdpdr/guacdr_la-rdpdr_service.lo `test -f 'guac_rdpdr/rdpdr_service.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_service.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_service.Tpo guac_rdpdr/$(DEPDIR)/guacdr_la-rdpdr_service.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_rdpdr/rdpdr_service.c' object='guac_rdpdr/guacdr_la-rdpdr_service.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_la_CFLAGS) $(CFLAGS) -c -o guac_rdpdr/guacdr_la-rdpdr_service.lo `test -f 'guac_rdpdr/rdpdr_service.c' || echo '$(srcdir)/'`guac_rdpdr/rdpdr_service.c
+
+guacdr_la-rdp_fs.lo: rdp_fs.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_la_CFLAGS) $(CFLAGS) -MT guacdr_la-rdp_fs.lo -MD -MP -MF $(DEPDIR)/guacdr_la-rdp_fs.Tpo -c -o guacdr_la-rdp_fs.lo `test -f 'rdp_fs.c' || echo '$(srcdir)/'`rdp_fs.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/guacdr_la-rdp_fs.Tpo $(DEPDIR)/guacdr_la-rdp_fs.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='rdp_fs.c' object='guacdr_la-rdp_fs.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_la_CFLAGS) $(CFLAGS) -c -o guacdr_la-rdp_fs.lo `test -f 'rdp_fs.c' || echo '$(srcdir)/'`rdp_fs.c
+
+guacdr_la-rdp_stream.lo: rdp_stream.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_la_CFLAGS) $(CFLAGS) -MT guacdr_la-rdp_stream.lo -MD -MP -MF $(DEPDIR)/guacdr_la-rdp_stream.Tpo -c -o guacdr_la-rdp_stream.lo `test -f 'rdp_stream.c' || echo '$(srcdir)/'`rdp_stream.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/guacdr_la-rdp_stream.Tpo $(DEPDIR)/guacdr_la-rdp_stream.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='rdp_stream.c' object='guacdr_la-rdp_stream.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_la_CFLAGS) $(CFLAGS) -c -o guacdr_la-rdp_stream.lo `test -f 'rdp_stream.c' || echo '$(srcdir)/'`rdp_stream.c
+
+guacdr_la-unicode.lo: unicode.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_la_CFLAGS) $(CFLAGS) -MT guacdr_la-unicode.lo -MD -MP -MF $(DEPDIR)/guacdr_la-unicode.Tpo -c -o guacdr_la-unicode.lo `test -f 'unicode.c' || echo '$(srcdir)/'`unicode.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/guacdr_la-unicode.Tpo $(DEPDIR)/guacdr_la-unicode.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='unicode.c' object='guacdr_la-unicode.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_la_CFLAGS) $(CFLAGS) -c -o guacdr_la-unicode.lo `test -f 'unicode.c' || echo '$(srcdir)/'`unicode.c
+
+compat/guacdr_la-winpr-stream.lo: compat/winpr-stream.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_la_CFLAGS) $(CFLAGS) -MT compat/guacdr_la-winpr-stream.lo -MD -MP -MF compat/$(DEPDIR)/guacdr_la-winpr-stream.Tpo -c -o compat/guacdr_la-winpr-stream.lo `test -f 'compat/winpr-stream.c' || echo '$(srcdir)/'`compat/winpr-stream.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) compat/$(DEPDIR)/guacdr_la-winpr-stream.Tpo compat/$(DEPDIR)/guacdr_la-winpr-stream.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='compat/winpr-stream.c' object='compat/guacdr_la-winpr-stream.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacdr_la_CFLAGS) $(CFLAGS) -c -o compat/guacdr_la-winpr-stream.lo `test -f 'compat/winpr-stream.c' || echo '$(srcdir)/'`compat/winpr-stream.c
+
+guac_rdpsnd/guacsnd_client_la-rdpsnd_messages.lo: guac_rdpsnd/rdpsnd_messages.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacsnd_client_la_CFLAGS) $(CFLAGS) -MT guac_rdpsnd/guacsnd_client_la-rdpsnd_messages.lo -MD -MP -MF guac_rdpsnd/$(DEPDIR)/guacsnd_client_la-rdpsnd_messages.Tpo -c -o guac_rdpsnd/guacsnd_client_la-rdpsnd_messages.lo `test -f 'guac_rdpsnd/rdpsnd_messages.c' || echo '$(srcdir)/'`guac_rdpsnd/rdpsnd_messages.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) guac_rdpsnd/$(DEPDIR)/guacsnd_client_la-rdpsnd_messages.Tpo guac_rdpsnd/$(DEPDIR)/guacsnd_client_la-rdpsnd_messages.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_rdpsnd/rdpsnd_messages.c' object='guac_rdpsnd/guacsnd_client_la-rdpsnd_messages.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacsnd_client_la_CFLAGS) $(CFLAGS) -c -o guac_rdpsnd/guacsnd_client_la-rdpsnd_messages.lo `test -f 'guac_rdpsnd/rdpsnd_messages.c' || echo '$(srcdir)/'`guac_rdpsnd/rdpsnd_messages.c
+
+guac_rdpsnd/guacsnd_client_la-rdpsnd_service.lo: guac_rdpsnd/rdpsnd_service.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacsnd_client_la_CFLAGS) $(CFLAGS) -MT guac_rdpsnd/guacsnd_client_la-rdpsnd_service.lo -MD -MP -MF guac_rdpsnd/$(DEPDIR)/guacsnd_client_la-rdpsnd_service.Tpo -c -o guac_rdpsnd/guacsnd_client_la-rdpsnd_service.lo `test -f 'guac_rdpsnd/rdpsnd_service.c' || echo '$(srcdir)/'`guac_rdpsnd/rdpsnd_service.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) guac_rdpsnd/$(DEPDIR)/guacsnd_client_la-rdpsnd_service.Tpo guac_rdpsnd/$(DEPDIR)/guacsnd_client_la-rdpsnd_service.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_rdpsnd/rdpsnd_service.c' object='guac_rdpsnd/guacsnd_client_la-rdpsnd_service.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacsnd_client_la_CFLAGS) $(CFLAGS) -c -o guac_rdpsnd/guacsnd_client_la-rdpsnd_service.lo `test -f 'guac_rdpsnd/rdpsnd_service.c' || echo '$(srcdir)/'`guac_rdpsnd/rdpsnd_service.c
+
+compat/guacsnd_client_la-winpr-stream.lo: compat/winpr-stream.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacsnd_client_la_CFLAGS) $(CFLAGS) -MT compat/guacsnd_client_la-winpr-stream.lo -MD -MP -MF compat/$(DEPDIR)/guacsnd_client_la-winpr-stream.Tpo -c -o compat/guacsnd_client_la-winpr-stream.lo `test -f 'compat/winpr-stream.c' || echo '$(srcdir)/'`compat/winpr-stream.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) compat/$(DEPDIR)/guacsnd_client_la-winpr-stream.Tpo compat/$(DEPDIR)/guacsnd_client_la-winpr-stream.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='compat/winpr-stream.c' object='compat/guacsnd_client_la-winpr-stream.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacsnd_client_la_CFLAGS) $(CFLAGS) -c -o compat/guacsnd_client_la-winpr-stream.lo `test -f 'compat/winpr-stream.c' || echo '$(srcdir)/'`compat/winpr-stream.c
+
+guac_rdpsnd/guacsnd_la-rdpsnd_messages.lo: guac_rdpsnd/rdpsnd_messages.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacsnd_la_CFLAGS) $(CFLAGS) -MT guac_rdpsnd/guacsnd_la-rdpsnd_messages.lo -MD -MP -MF guac_rdpsnd/$(DEPDIR)/guacsnd_la-rdpsnd_messages.Tpo -c -o guac_rdpsnd/guacsnd_la-rdpsnd_messages.lo `test -f 'guac_rdpsnd/rdpsnd_messages.c' || echo '$(srcdir)/'`guac_rdpsnd/rdpsnd_messages.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) guac_rdpsnd/$(DEPDIR)/guacsnd_la-rdpsnd_messages.Tpo guac_rdpsnd/$(DEPDIR)/guacsnd_la-rdpsnd_messages.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_rdpsnd/rdpsnd_messages.c' object='guac_rdpsnd/guacsnd_la-rdpsnd_messages.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacsnd_la_CFLAGS) $(CFLAGS) -c -o guac_rdpsnd/guacsnd_la-rdpsnd_messages.lo `test -f 'guac_rdpsnd/rdpsnd_messages.c' || echo '$(srcdir)/'`guac_rdpsnd/rdpsnd_messages.c
+
+guac_rdpsnd/guacsnd_la-rdpsnd_service.lo: guac_rdpsnd/rdpsnd_service.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacsnd_la_CFLAGS) $(CFLAGS) -MT guac_rdpsnd/guacsnd_la-rdpsnd_service.lo -MD -MP -MF guac_rdpsnd/$(DEPDIR)/guacsnd_la-rdpsnd_service.Tpo -c -o guac_rdpsnd/guacsnd_la-rdpsnd_service.lo `test -f 'guac_rdpsnd/rdpsnd_service.c' || echo '$(srcdir)/'`guac_rdpsnd/rdpsnd_service.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) guac_rdpsnd/$(DEPDIR)/guacsnd_la-rdpsnd_service.Tpo guac_rdpsnd/$(DEPDIR)/guacsnd_la-rdpsnd_service.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_rdpsnd/rdpsnd_service.c' object='guac_rdpsnd/guacsnd_la-rdpsnd_service.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacsnd_la_CFLAGS) $(CFLAGS) -c -o guac_rdpsnd/guacsnd_la-rdpsnd_service.lo `test -f 'guac_rdpsnd/rdpsnd_service.c' || echo '$(srcdir)/'`guac_rdpsnd/rdpsnd_service.c
+
+compat/guacsnd_la-winpr-stream.lo: compat/winpr-stream.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacsnd_la_CFLAGS) $(CFLAGS) -MT compat/guacsnd_la-winpr-stream.lo -MD -MP -MF compat/$(DEPDIR)/guacsnd_la-winpr-stream.Tpo -c -o compat/guacsnd_la-winpr-stream.lo `test -f 'compat/winpr-stream.c' || echo '$(srcdir)/'`compat/winpr-stream.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) compat/$(DEPDIR)/guacsnd_la-winpr-stream.Tpo compat/$(DEPDIR)/guacsnd_la-winpr-stream.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='compat/winpr-stream.c' object='compat/guacsnd_la-winpr-stream.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacsnd_la_CFLAGS) $(CFLAGS) -c -o compat/guacsnd_la-winpr-stream.lo `test -f 'compat/winpr-stream.c' || echo '$(srcdir)/'`compat/winpr-stream.c
+
+guac_svc/guacsvc_client_la-svc_service.lo: guac_svc/svc_service.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacsvc_client_la_CFLAGS) $(CFLAGS) -MT guac_svc/guacsvc_client_la-svc_service.lo -MD -MP -MF guac_svc/$(DEPDIR)/guacsvc_client_la-svc_service.Tpo -c -o guac_svc/guacsvc_client_la-svc_service.lo `test -f 'guac_svc/svc_service.c' || echo '$(srcdir)/'`guac_svc/svc_service.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) guac_svc/$(DEPDIR)/guacsvc_client_la-svc_service.Tpo guac_svc/$(DEPDIR)/guacsvc_client_la-svc_service.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_svc/svc_service.c' object='guac_svc/guacsvc_client_la-svc_service.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacsvc_client_la_CFLAGS) $(CFLAGS) -c -o guac_svc/guacsvc_client_la-svc_service.lo `test -f 'guac_svc/svc_service.c' || echo '$(srcdir)/'`guac_svc/svc_service.c
+
+guacsvc_client_la-rdp_svc.lo: rdp_svc.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacsvc_client_la_CFLAGS) $(CFLAGS) -MT guacsvc_client_la-rdp_svc.lo -MD -MP -MF $(DEPDIR)/guacsvc_client_la-rdp_svc.Tpo -c -o guacsvc_client_la-rdp_svc.lo `test -f 'rdp_svc.c' || echo '$(srcdir)/'`rdp_svc.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/guacsvc_client_la-rdp_svc.Tpo $(DEPDIR)/guacsvc_client_la-rdp_svc.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='rdp_svc.c' object='guacsvc_client_la-rdp_svc.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacsvc_client_la_CFLAGS) $(CFLAGS) -c -o guacsvc_client_la-rdp_svc.lo `test -f 'rdp_svc.c' || echo '$(srcdir)/'`rdp_svc.c
+
+compat/guacsvc_client_la-winpr-stream.lo: compat/winpr-stream.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacsvc_client_la_CFLAGS) $(CFLAGS) -MT compat/guacsvc_client_la-winpr-stream.lo -MD -MP -MF compat/$(DEPDIR)/guacsvc_client_la-winpr-stream.Tpo -c -o compat/guacsvc_client_la-winpr-stream.lo `test -f 'compat/winpr-stream.c' || echo '$(srcdir)/'`compat/winpr-stream.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) compat/$(DEPDIR)/guacsvc_client_la-winpr-stream.Tpo compat/$(DEPDIR)/guacsvc_client_la-winpr-stream.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='compat/winpr-stream.c' object='compat/guacsvc_client_la-winpr-stream.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacsvc_client_la_CFLAGS) $(CFLAGS) -c -o compat/guacsvc_client_la-winpr-stream.lo `test -f 'compat/winpr-stream.c' || echo '$(srcdir)/'`compat/winpr-stream.c
+
+guac_svc/guacsvc_la-svc_service.lo: guac_svc/svc_service.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacsvc_la_CFLAGS) $(CFLAGS) -MT guac_svc/guacsvc_la-svc_service.lo -MD -MP -MF guac_svc/$(DEPDIR)/guacsvc_la-svc_service.Tpo -c -o guac_svc/guacsvc_la-svc_service.lo `test -f 'guac_svc/svc_service.c' || echo '$(srcdir)/'`guac_svc/svc_service.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) guac_svc/$(DEPDIR)/guacsvc_la-svc_service.Tpo guac_svc/$(DEPDIR)/guacsvc_la-svc_service.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_svc/svc_service.c' object='guac_svc/guacsvc_la-svc_service.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacsvc_la_CFLAGS) $(CFLAGS) -c -o guac_svc/guacsvc_la-svc_service.lo `test -f 'guac_svc/svc_service.c' || echo '$(srcdir)/'`guac_svc/svc_service.c
+
+guacsvc_la-rdp_svc.lo: rdp_svc.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacsvc_la_CFLAGS) $(CFLAGS) -MT guacsvc_la-rdp_svc.lo -MD -MP -MF $(DEPDIR)/guacsvc_la-rdp_svc.Tpo -c -o guacsvc_la-rdp_svc.lo `test -f 'rdp_svc.c' || echo '$(srcdir)/'`rdp_svc.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/guacsvc_la-rdp_svc.Tpo $(DEPDIR)/guacsvc_la-rdp_svc.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='rdp_svc.c' object='guacsvc_la-rdp_svc.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacsvc_la_CFLAGS) $(CFLAGS) -c -o guacsvc_la-rdp_svc.lo `test -f 'rdp_svc.c' || echo '$(srcdir)/'`rdp_svc.c
+
+compat/guacsvc_la-winpr-stream.lo: compat/winpr-stream.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacsvc_la_CFLAGS) $(CFLAGS) -MT compat/guacsvc_la-winpr-stream.lo -MD -MP -MF compat/$(DEPDIR)/guacsvc_la-winpr-stream.Tpo -c -o compat/guacsvc_la-winpr-stream.lo `test -f 'compat/winpr-stream.c' || echo '$(srcdir)/'`compat/winpr-stream.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) compat/$(DEPDIR)/guacsvc_la-winpr-stream.Tpo compat/$(DEPDIR)/guacsvc_la-winpr-stream.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='compat/winpr-stream.c' object='compat/guacsvc_la-winpr-stream.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(guacsvc_la_CFLAGS) $(CFLAGS) -c -o compat/guacsvc_la-winpr-stream.lo `test -f 'compat/winpr-stream.c' || echo '$(srcdir)/'`compat/winpr-stream.c
+
+libguac_client_rdp_la-_generated_keymaps.lo: _generated_keymaps.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -MT libguac_client_rdp_la-_generated_keymaps.lo -MD -MP -MF $(DEPDIR)/libguac_client_rdp_la-_generated_keymaps.Tpo -c -o libguac_client_rdp_la-_generated_keymaps.lo `test -f '_generated_keymaps.c' || echo '$(srcdir)/'`_generated_keymaps.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_rdp_la-_generated_keymaps.Tpo $(DEPDIR)/libguac_client_rdp_la-_generated_keymaps.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='_generated_keymaps.c' object='libguac_client_rdp_la-_generated_keymaps.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -c -o libguac_client_rdp_la-_generated_keymaps.lo `test -f '_generated_keymaps.c' || echo '$(srcdir)/'`_generated_keymaps.c
+
+libguac_client_rdp_la-client.lo: client.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -MT libguac_client_rdp_la-client.lo -MD -MP -MF $(DEPDIR)/libguac_client_rdp_la-client.Tpo -c -o libguac_client_rdp_la-client.lo `test -f 'client.c' || echo '$(srcdir)/'`client.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_rdp_la-client.Tpo $(DEPDIR)/libguac_client_rdp_la-client.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='client.c' object='libguac_client_rdp_la-client.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -c -o libguac_client_rdp_la-client.lo `test -f 'client.c' || echo '$(srcdir)/'`client.c
+
+libguac_client_rdp_la-guac_handlers.lo: guac_handlers.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -MT libguac_client_rdp_la-guac_handlers.lo -MD -MP -MF $(DEPDIR)/libguac_client_rdp_la-guac_handlers.Tpo -c -o libguac_client_rdp_la-guac_handlers.lo `test -f 'guac_handlers.c' || echo '$(srcdir)/'`guac_handlers.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_rdp_la-guac_handlers.Tpo $(DEPDIR)/libguac_client_rdp_la-guac_handlers.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_handlers.c' object='libguac_client_rdp_la-guac_handlers.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -c -o libguac_client_rdp_la-guac_handlers.lo `test -f 'guac_handlers.c' || echo '$(srcdir)/'`guac_handlers.c
+
+libguac_client_rdp_la-rdp_bitmap.lo: rdp_bitmap.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -MT libguac_client_rdp_la-rdp_bitmap.lo -MD -MP -MF $(DEPDIR)/libguac_client_rdp_la-rdp_bitmap.Tpo -c -o libguac_client_rdp_la-rdp_bitmap.lo `test -f 'rdp_bitmap.c' || echo '$(srcdir)/'`rdp_bitmap.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_rdp_la-rdp_bitmap.Tpo $(DEPDIR)/libguac_client_rdp_la-rdp_bitmap.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='rdp_bitmap.c' object='libguac_client_rdp_la-rdp_bitmap.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -c -o libguac_client_rdp_la-rdp_bitmap.lo `test -f 'rdp_bitmap.c' || echo '$(srcdir)/'`rdp_bitmap.c
+
+libguac_client_rdp_la-rdp_cliprdr.lo: rdp_cliprdr.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -MT libguac_client_rdp_la-rdp_cliprdr.lo -MD -MP -MF $(DEPDIR)/libguac_client_rdp_la-rdp_cliprdr.Tpo -c -o libguac_client_rdp_la-rdp_cliprdr.lo `test -f 'rdp_cliprdr.c' || echo '$(srcdir)/'`rdp_cliprdr.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_rdp_la-rdp_cliprdr.Tpo $(DEPDIR)/libguac_client_rdp_la-rdp_cliprdr.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='rdp_cliprdr.c' object='libguac_client_rdp_la-rdp_cliprdr.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -c -o libguac_client_rdp_la-rdp_cliprdr.lo `test -f 'rdp_cliprdr.c' || echo '$(srcdir)/'`rdp_cliprdr.c
+
+libguac_client_rdp_la-rdp_color.lo: rdp_color.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -MT libguac_client_rdp_la-rdp_color.lo -MD -MP -MF $(DEPDIR)/libguac_client_rdp_la-rdp_color.Tpo -c -o libguac_client_rdp_la-rdp_color.lo `test -f 'rdp_color.c' || echo '$(srcdir)/'`rdp_color.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_rdp_la-rdp_color.Tpo $(DEPDIR)/libguac_client_rdp_la-rdp_color.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='rdp_color.c' object='libguac_client_rdp_la-rdp_color.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -c -o libguac_client_rdp_la-rdp_color.lo `test -f 'rdp_color.c' || echo '$(srcdir)/'`rdp_color.c
+
+libguac_client_rdp_la-rdp_fs.lo: rdp_fs.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -MT libguac_client_rdp_la-rdp_fs.lo -MD -MP -MF $(DEPDIR)/libguac_client_rdp_la-rdp_fs.Tpo -c -o libguac_client_rdp_la-rdp_fs.lo `test -f 'rdp_fs.c' || echo '$(srcdir)/'`rdp_fs.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_rdp_la-rdp_fs.Tpo $(DEPDIR)/libguac_client_rdp_la-rdp_fs.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='rdp_fs.c' object='libguac_client_rdp_la-rdp_fs.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -c -o libguac_client_rdp_la-rdp_fs.lo `test -f 'rdp_fs.c' || echo '$(srcdir)/'`rdp_fs.c
+
+libguac_client_rdp_la-rdp_gdi.lo: rdp_gdi.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -MT libguac_client_rdp_la-rdp_gdi.lo -MD -MP -MF $(DEPDIR)/libguac_client_rdp_la-rdp_gdi.Tpo -c -o libguac_client_rdp_la-rdp_gdi.lo `test -f 'rdp_gdi.c' || echo '$(srcdir)/'`rdp_gdi.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_rdp_la-rdp_gdi.Tpo $(DEPDIR)/libguac_client_rdp_la-rdp_gdi.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='rdp_gdi.c' object='libguac_client_rdp_la-rdp_gdi.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -c -o libguac_client_rdp_la-rdp_gdi.lo `test -f 'rdp_gdi.c' || echo '$(srcdir)/'`rdp_gdi.c
+
+libguac_client_rdp_la-rdp_glyph.lo: rdp_glyph.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -MT libguac_client_rdp_la-rdp_glyph.lo -MD -MP -MF $(DEPDIR)/libguac_client_rdp_la-rdp_glyph.Tpo -c -o libguac_client_rdp_la-rdp_glyph.lo `test -f 'rdp_glyph.c' || echo '$(srcdir)/'`rdp_glyph.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_rdp_la-rdp_glyph.Tpo $(DEPDIR)/libguac_client_rdp_la-rdp_glyph.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='rdp_glyph.c' object='libguac_client_rdp_la-rdp_glyph.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -c -o libguac_client_rdp_la-rdp_glyph.lo `test -f 'rdp_glyph.c' || echo '$(srcdir)/'`rdp_glyph.c
+
+libguac_client_rdp_la-rdp_keymap.lo: rdp_keymap.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -MT libguac_client_rdp_la-rdp_keymap.lo -MD -MP -MF $(DEPDIR)/libguac_client_rdp_la-rdp_keymap.Tpo -c -o libguac_client_rdp_la-rdp_keymap.lo `test -f 'rdp_keymap.c' || echo '$(srcdir)/'`rdp_keymap.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_rdp_la-rdp_keymap.Tpo $(DEPDIR)/libguac_client_rdp_la-rdp_keymap.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='rdp_keymap.c' object='libguac_client_rdp_la-rdp_keymap.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -c -o libguac_client_rdp_la-rdp_keymap.lo `test -f 'rdp_keymap.c' || echo '$(srcdir)/'`rdp_keymap.c
+
+libguac_client_rdp_la-rdp_pointer.lo: rdp_pointer.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -MT libguac_client_rdp_la-rdp_pointer.lo -MD -MP -MF $(DEPDIR)/libguac_client_rdp_la-rdp_pointer.Tpo -c -o libguac_client_rdp_la-rdp_pointer.lo `test -f 'rdp_pointer.c' || echo '$(srcdir)/'`rdp_pointer.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_rdp_la-rdp_pointer.Tpo $(DEPDIR)/libguac_client_rdp_la-rdp_pointer.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='rdp_pointer.c' object='libguac_client_rdp_la-rdp_pointer.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -c -o libguac_client_rdp_la-rdp_pointer.lo `test -f 'rdp_pointer.c' || echo '$(srcdir)/'`rdp_pointer.c
+
+libguac_client_rdp_la-rdp_rail.lo: rdp_rail.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -MT libguac_client_rdp_la-rdp_rail.lo -MD -MP -MF $(DEPDIR)/libguac_client_rdp_la-rdp_rail.Tpo -c -o libguac_client_rdp_la-rdp_rail.lo `test -f 'rdp_rail.c' || echo '$(srcdir)/'`rdp_rail.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_rdp_la-rdp_rail.Tpo $(DEPDIR)/libguac_client_rdp_la-rdp_rail.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='rdp_rail.c' object='libguac_client_rdp_la-rdp_rail.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -c -o libguac_client_rdp_la-rdp_rail.lo `test -f 'rdp_rail.c' || echo '$(srcdir)/'`rdp_rail.c
+
+libguac_client_rdp_la-rdp_settings.lo: rdp_settings.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -MT libguac_client_rdp_la-rdp_settings.lo -MD -MP -MF $(DEPDIR)/libguac_client_rdp_la-rdp_settings.Tpo -c -o libguac_client_rdp_la-rdp_settings.lo `test -f 'rdp_settings.c' || echo '$(srcdir)/'`rdp_settings.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_rdp_la-rdp_settings.Tpo $(DEPDIR)/libguac_client_rdp_la-rdp_settings.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='rdp_settings.c' object='libguac_client_rdp_la-rdp_settings.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -c -o libguac_client_rdp_la-rdp_settings.lo `test -f 'rdp_settings.c' || echo '$(srcdir)/'`rdp_settings.c
+
+libguac_client_rdp_la-rdp_stream.lo: rdp_stream.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -MT libguac_client_rdp_la-rdp_stream.lo -MD -MP -MF $(DEPDIR)/libguac_client_rdp_la-rdp_stream.Tpo -c -o libguac_client_rdp_la-rdp_stream.lo `test -f 'rdp_stream.c' || echo '$(srcdir)/'`rdp_stream.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_rdp_la-rdp_stream.Tpo $(DEPDIR)/libguac_client_rdp_la-rdp_stream.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='rdp_stream.c' object='libguac_client_rdp_la-rdp_stream.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -c -o libguac_client_rdp_la-rdp_stream.lo `test -f 'rdp_stream.c' || echo '$(srcdir)/'`rdp_stream.c
+
+libguac_client_rdp_la-rdp_svc.lo: rdp_svc.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -MT libguac_client_rdp_la-rdp_svc.lo -MD -MP -MF $(DEPDIR)/libguac_client_rdp_la-rdp_svc.Tpo -c -o libguac_client_rdp_la-rdp_svc.lo `test -f 'rdp_svc.c' || echo '$(srcdir)/'`rdp_svc.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_rdp_la-rdp_svc.Tpo $(DEPDIR)/libguac_client_rdp_la-rdp_svc.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='rdp_svc.c' object='libguac_client_rdp_la-rdp_svc.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -c -o libguac_client_rdp_la-rdp_svc.lo `test -f 'rdp_svc.c' || echo '$(srcdir)/'`rdp_svc.c
+
+libguac_client_rdp_la-resolution.lo: resolution.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -MT libguac_client_rdp_la-resolution.lo -MD -MP -MF $(DEPDIR)/libguac_client_rdp_la-resolution.Tpo -c -o libguac_client_rdp_la-resolution.lo `test -f 'resolution.c' || echo '$(srcdir)/'`resolution.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_rdp_la-resolution.Tpo $(DEPDIR)/libguac_client_rdp_la-resolution.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='resolution.c' object='libguac_client_rdp_la-resolution.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -c -o libguac_client_rdp_la-resolution.lo `test -f 'resolution.c' || echo '$(srcdir)/'`resolution.c
+
+libguac_client_rdp_la-unicode.lo: unicode.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -MT libguac_client_rdp_la-unicode.lo -MD -MP -MF $(DEPDIR)/libguac_client_rdp_la-unicode.Tpo -c -o libguac_client_rdp_la-unicode.lo `test -f 'unicode.c' || echo '$(srcdir)/'`unicode.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_rdp_la-unicode.Tpo $(DEPDIR)/libguac_client_rdp_la-unicode.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='unicode.c' object='libguac_client_rdp_la-unicode.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -c -o libguac_client_rdp_la-unicode.lo `test -f 'unicode.c' || echo '$(srcdir)/'`unicode.c
+
+compat/libguac_client_rdp_la-winpr-stream.lo: compat/winpr-stream.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -MT compat/libguac_client_rdp_la-winpr-stream.lo -MD -MP -MF compat/$(DEPDIR)/libguac_client_rdp_la-winpr-stream.Tpo -c -o compat/libguac_client_rdp_la-winpr-stream.lo `test -f 'compat/winpr-stream.c' || echo '$(srcdir)/'`compat/winpr-stream.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) compat/$(DEPDIR)/libguac_client_rdp_la-winpr-stream.Tpo compat/$(DEPDIR)/libguac_client_rdp_la-winpr-stream.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='compat/winpr-stream.c' object='compat/libguac_client_rdp_la-winpr-stream.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -c -o compat/libguac_client_rdp_la-winpr-stream.lo `test -f 'compat/winpr-stream.c' || echo '$(srcdir)/'`compat/winpr-stream.c
+
+libguac_client_rdp_la-rdp_disp.lo: rdp_disp.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -MT libguac_client_rdp_la-rdp_disp.lo -MD -MP -MF $(DEPDIR)/libguac_client_rdp_la-rdp_disp.Tpo -c -o libguac_client_rdp_la-rdp_disp.lo `test -f 'rdp_disp.c' || echo '$(srcdir)/'`rdp_disp.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_rdp_la-rdp_disp.Tpo $(DEPDIR)/libguac_client_rdp_la-rdp_disp.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='rdp_disp.c' object='libguac_client_rdp_la-rdp_disp.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -c -o libguac_client_rdp_la-rdp_disp.lo `test -f 'rdp_disp.c' || echo '$(srcdir)/'`rdp_disp.c
+
+libguac_client_rdp_la-sftp.lo: sftp.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -MT libguac_client_rdp_la-sftp.lo -MD -MP -MF $(DEPDIR)/libguac_client_rdp_la-sftp.Tpo -c -o libguac_client_rdp_la-sftp.lo `test -f 'sftp.c' || echo '$(srcdir)/'`sftp.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_rdp_la-sftp.Tpo $(DEPDIR)/libguac_client_rdp_la-sftp.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='sftp.c' object='libguac_client_rdp_la-sftp.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_rdp_la_CFLAGS) $(CFLAGS) -c -o libguac_client_rdp_la-sftp.lo `test -f 'sftp.c' || echo '$(srcdir)/'`sftp.c
 
 mostlyclean-libtool:
 	-rm -f *.lo
 
 clean-libtool:
 	-rm -rf .libs _libs
+	-rm -rf compat/.libs compat/_libs
+	-rm -rf guac_rdpdr/.libs guac_rdpdr/_libs
+	-rm -rf guac_rdpsnd/.libs guac_rdpsnd/_libs
+	-rm -rf guac_svc/.libs guac_svc/_libs
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
 
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
-	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
-	unique=`for i in $$list; do \
-	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-	  done | \
-	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
-	      END { if (nonempty) { for (i in files) print i; }; }'`; \
-	mkid -fID $$unique
-tags: TAGS
-
-TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
-		$(TAGS_FILES) $(LISP)
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
 	set x; \
 	here=`pwd`; \
-	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
-	unique=`for i in $$list; do \
-	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-	  done | \
-	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
-	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	$(am__define_uniq_tagged_files); \
 	shift; \
 	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
 	  test -n "$$unique" || unique=$$empty_fix; \
@@ -671,15 +1460,11 @@ TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
 	      $$unique; \
 	  fi; \
 	fi
-ctags: CTAGS
-CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
-		$(TAGS_FILES) $(LISP)
-	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
-	unique=`for i in $$list; do \
-	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-	  done | \
-	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
-	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
 	test -z "$(CTAGS_ARGS)$$unique" \
 	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
 	     $$unique
@@ -688,6 +1473,21 @@ GTAGS:
 	here=`$(am__cd) $(top_builddir) && pwd` \
 	  && $(am__cd) $(top_srcdir) \
 	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
 
 distclean-tags:
 	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
@@ -723,13 +1523,15 @@ distdir: $(DISTFILES)
 	  fi; \
 	done
 check-am: all-am
-check: check-am
+check: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) check-am
 all-am: Makefile $(LTLIBRARIES) $(HEADERS)
 installdirs:
 	for dir in "$(DESTDIR)$(freerdpdir)" "$(DESTDIR)$(libdir)"; do \
 	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
 	done
-install: install-am
+install: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) install-am
 install-exec: install-exec-am
 install-data: install-data-am
 uninstall: uninstall-am
@@ -751,21 +1553,31 @@ install-strip:
 mostlyclean-generic:
 
 clean-generic:
+	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
 
 distclean-generic:
 	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
 	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+	-rm -f compat/$(DEPDIR)/$(am__dirstamp)
+	-rm -f compat/$(am__dirstamp)
+	-rm -f guac_rdpdr/$(DEPDIR)/$(am__dirstamp)
+	-rm -f guac_rdpdr/$(am__dirstamp)
+	-rm -f guac_rdpsnd/$(DEPDIR)/$(am__dirstamp)
+	-rm -f guac_rdpsnd/$(am__dirstamp)
+	-rm -f guac_svc/$(DEPDIR)/$(am__dirstamp)
+	-rm -f guac_svc/$(am__dirstamp)
 
 maintainer-clean-generic:
 	@echo "This command is intended for maintainers to use"
 	@echo "it deletes files that may require special tools to rebuild."
+	-test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
 clean: clean-am
 
 clean-am: clean-freerdpLTLIBRARIES clean-generic clean-libLTLIBRARIES \
 	clean-libtool mostlyclean-am
 
 distclean: distclean-am
-	-rm -rf ./$(DEPDIR)
+	-rm -rf ./$(DEPDIR) compat/$(DEPDIR) guac_rdpdr/$(DEPDIR) guac_rdpsnd/$(DEPDIR) guac_svc/$(DEPDIR)
 	-rm -f Makefile
 distclean-am: clean-am distclean-compile distclean-generic \
 	distclean-tags
@@ -811,7 +1623,7 @@ install-ps-am:
 installcheck-am:
 
 maintainer-clean: maintainer-clean-am
-	-rm -rf ./$(DEPDIR)
+	-rm -rf ./$(DEPDIR) compat/$(DEPDIR) guac_rdpdr/$(DEPDIR) guac_rdpsnd/$(DEPDIR) guac_svc/$(DEPDIR)
 	-rm -f Makefile
 maintainer-clean-am: distclean-am maintainer-clean-generic
 
@@ -830,25 +1642,29 @@ ps-am:
 
 uninstall-am: uninstall-freerdpLTLIBRARIES uninstall-libLTLIBRARIES
 
-.MAKE: install-am install-strip
+.MAKE: all check install install-am install-strip
 
-.PHONY: CTAGS GTAGS all all-am check check-am clean \
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \
 	clean-freerdpLTLIBRARIES clean-generic clean-libLTLIBRARIES \
-	clean-libtool ctags distclean distclean-compile \
-	distclean-generic distclean-libtool distclean-tags distdir dvi \
-	dvi-am html html-am info info-am install install-am \
-	install-data install-data-am install-dvi install-dvi-am \
-	install-exec install-exec-am install-freerdpLTLIBRARIES \
-	install-html install-html-am install-info install-info-am \
-	install-libLTLIBRARIES install-man install-pdf install-pdf-am \
-	install-ps install-ps-am install-strip installcheck \
-	installcheck-am installdirs maintainer-clean \
-	maintainer-clean-generic mostlyclean mostlyclean-compile \
-	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
-	tags uninstall uninstall-am uninstall-freerdpLTLIBRARIES \
+	clean-libtool cscopelist-am ctags ctags-am distclean \
+	distclean-compile distclean-generic distclean-libtool \
+	distclean-tags distdir dvi dvi-am html html-am info info-am \
+	install install-am install-data install-data-am install-dvi \
+	install-dvi-am install-exec install-exec-am \
+	install-freerdpLTLIBRARIES install-html install-html-am \
+	install-info install-info-am install-libLTLIBRARIES \
+	install-man install-pdf install-pdf-am install-ps \
+	install-ps-am install-strip installcheck installcheck-am \
+	installdirs maintainer-clean maintainer-clean-generic \
+	mostlyclean mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+	uninstall-am uninstall-freerdpLTLIBRARIES \
 	uninstall-libLTLIBRARIES
 
 
+_generated_keymaps.c: $(rdp_keymaps)
+	keymaps/generate.pl $(rdp_keymaps)
+
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
 .NOEXPORT:
diff --git a/src/protocols/rdp/_generated_keymaps.c b/src/protocols/rdp/_generated_keymaps.c
new file mode 100644
index 0000000..cbee432
--- /dev/null
+++ b/src/protocols/rdp/_generated_keymaps.c
@@ -0,0 +1,680 @@
+#include "config.h"
+#include "rdp_keymap.h"
+#include <freerdp/input.h>
+
+#ifdef HAVE_FREERDP_LOCALE_KEYBOARD_H
+#include <freerdp/locale/keyboard.h>
+#else
+#include <freerdp/kbd/layouts.h>
+#endif
+
+#include <stddef.h>
+
+
+/* Autogenerated from keymaps/base.keymap */
+static guac_rdp_keysym_desc __guac_rdp_keymap_base[] = {
+    { .keysym = 32, .scancode = 57 },
+    { .keysym = 65289, .scancode = 15 },
+    { .keysym = 65288, .scancode = 14 },
+    { .keysym = 65293, .scancode = 28 },
+    { .keysym = 65307, .scancode = 1 },
+    { .keysym = 65379, .scancode = 82, .flags = KBD_FLAGS_EXTENDED },
+    { .keysym = 65535, .scancode = 83, .flags = KBD_FLAGS_EXTENDED },
+    { .keysym = 65360, .scancode = 71, .flags = KBD_FLAGS_EXTENDED },
+    { .keysym = 65367, .scancode = 79, .flags = KBD_FLAGS_EXTENDED },
+    { .keysym = 65361, .scancode = 75, .flags = KBD_FLAGS_EXTENDED },
+    { .keysym = 65362, .scancode = 72, .flags = KBD_FLAGS_EXTENDED },
+    { .keysym = 65363, .scancode = 77, .flags = KBD_FLAGS_EXTENDED },
+    { .keysym = 65364, .scancode = 80, .flags = KBD_FLAGS_EXTENDED },
+    { .keysym = 65365, .scancode = 73, .flags = KBD_FLAGS_EXTENDED },
+    { .keysym = 65366, .scancode = 81, .flags = KBD_FLAGS_EXTENDED },
+    { .keysym = 65407, .scancode = 69 },
+    { .keysym = 65300, .scancode = 70 },
+    { .keysym = 65509, .scancode = 58, .flags = KBD_FLAGS_EXTENDED },
+    { .keysym = 65456, .scancode = 82 },
+    { .keysym = 65457, .scancode = 79 },
+    { .keysym = 65458, .scancode = 80 },
+    { .keysym = 65459, .scancode = 81 },
+    { .keysym = 65460, .scancode = 75 },
+    { .keysym = 65461, .scancode = 76 },
+    { .keysym = 65462, .scancode = 77 },
+    { .keysym = 65463, .scancode = 71 },
+    { .keysym = 65464, .scancode = 72 },
+    { .keysym = 65465, .scancode = 73 },
+    { .keysym = 65450, .scancode = 55 },
+    { .keysym = 65451, .scancode = 78 },
+    { .keysym = 65453, .scancode = 74 },
+    { .keysym = 65454, .scancode = 83 },
+    { .keysym = 65455, .scancode = 53, .flags = KBD_FLAGS_EXTENDED },
+    { .keysym = 65470, .scancode = 59 },
+    { .keysym = 65471, .scancode = 60 },
+    { .keysym = 65472, .scancode = 61 },
+    { .keysym = 65473, .scancode = 62 },
+    { .keysym = 65474, .scancode = 63 },
+    { .keysym = 65475, .scancode = 64 },
+    { .keysym = 65476, .scancode = 65 },
+    { .keysym = 65477, .scancode = 66 },
+    { .keysym = 65478, .scancode = 67 },
+    { .keysym = 65479, .scancode = 68 },
+    { .keysym = 65480, .scancode = 87 },
+    { .keysym = 65481, .scancode = 88 },
+    { .keysym = 65505, .scancode = 42 },
+    { .keysym = 65506, .scancode = 54 },
+    { .keysym = 65507, .scancode = 29 },
+    { .keysym = 65508, .scancode = 157 },
+    { .keysym = 65513, .scancode = 56 },
+    { .keysym = 65514, .scancode = 56, .flags = KBD_FLAGS_EXTENDED },
+    { .keysym = 65515, .scancode = 91, .flags = KBD_FLAGS_EXTENDED },
+    { .keysym = 65516, .scancode = 92, .flags = KBD_FLAGS_EXTENDED },
+    { .keysym = 65383, .scancode = 93, .flags = KBD_FLAGS_EXTENDED },
+    {0}
+};
+
+static const guac_rdp_keymap guac_rdp_keymap_base = { 
+    .name = "base",
+    .mapping = __guac_rdp_keymap_base
+};
+
+/* Autogenerated from keymaps/failsafe.keymap */
+static guac_rdp_keysym_desc __guac_rdp_keymap_failsafe[] = {
+    {0}
+};
+
+static const guac_rdp_keymap guac_rdp_keymap_failsafe = { 
+    .name = "failsafe",
+    .parent = &guac_rdp_keymap_base,
+    .freerdp_keyboard_layout = KBD_US,
+    .mapping = __guac_rdp_keymap_failsafe
+};
+
+/* Autogenerated from keymaps/de_de_qwertz.keymap */
+static guac_rdp_keysym_desc __guac_rdp_keymap_de_de_qwertz[] = {
+    { .keysym = 49, .scancode = 2, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 50, .scancode = 3, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 51, .scancode = 4, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 52, .scancode = 5, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 53, .scancode = 6, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 54, .scancode = 7, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 55, .scancode = 8, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 56, .scancode = 9, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 57, .scancode = 10, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 48, .scancode = 11, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 223, .scancode = 12, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 113, .scancode = 16, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 119, .scancode = 17, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 101, .scancode = 18, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 114, .scancode = 19, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 116, .scancode = 20, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 122, .scancode = 21, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 117, .scancode = 22, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 105, .scancode = 23, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 111, .scancode = 24, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 112, .scancode = 25, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 252, .scancode = 26, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 43, .scancode = 27, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 97, .scancode = 30, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 115, .scancode = 31, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 100, .scancode = 32, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 102, .scancode = 33, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 103, .scancode = 34, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 104, .scancode = 35, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 106, .scancode = 36, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 107, .scancode = 37, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 108, .scancode = 38, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 246, .scancode = 39, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 228, .scancode = 40, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 35, .scancode = 43, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 60, .scancode = 86, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 121, .scancode = 44, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 120, .scancode = 45, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 99, .scancode = 46, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 118, .scancode = 47, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 98, .scancode = 48, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 110, .scancode = 49, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 109, .scancode = 50, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 44, .scancode = 51, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 46, .scancode = 52, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 45, .scancode = 53, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 176, .scancode = 41, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 33, .scancode = 2, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 34, .scancode = 3, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 167, .scancode = 4, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 36, .scancode = 5, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 37, .scancode = 6, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 38, .scancode = 7, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 47, .scancode = 8, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 40, .scancode = 9, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 41, .scancode = 10, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 61, .scancode = 11, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 63, .scancode = 12, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 81, .scancode = 16, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 87, .scancode = 17, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 69, .scancode = 18, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 82, .scancode = 19, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 84, .scancode = 20, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 90, .scancode = 21, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 85, .scancode = 22, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 73, .scancode = 23, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 79, .scancode = 24, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 80, .scancode = 25, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 220, .scancode = 26, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 42, .scancode = 27, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 65, .scancode = 30, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 83, .scancode = 31, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 68, .scancode = 32, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 70, .scancode = 33, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 71, .scancode = 34, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 72, .scancode = 35, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 74, .scancode = 36, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 75, .scancode = 37, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 76, .scancode = 38, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 214, .scancode = 39, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 196, .scancode = 40, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 39, .scancode = 43, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 62, .scancode = 86, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 89, .scancode = 44, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 88, .scancode = 45, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 67, .scancode = 46, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 86, .scancode = 47, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 66, .scancode = 48, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 78, .scancode = 49, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 77, .scancode = 50, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 59, .scancode = 51, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 58, .scancode = 52, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 95, .scancode = 53, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 178, .scancode = 3, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 179, .scancode = 4, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 123, .scancode = 8, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 91, .scancode = 9, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 93, .scancode = 10, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 125, .scancode = 11, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 92, .scancode = 12, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 64, .scancode = 16, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 16785580, .scancode = 18, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 124, .scancode = 86, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 181, .scancode = 50, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 65104, .scancode = 13, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 65105, .scancode = 13, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 65106, .scancode = 41, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 65107, .scancode = 12, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    {0}
+};
+
+static const guac_rdp_keymap guac_rdp_keymap_de_de_qwertz = { 
+    .name = "de-de-qwertz",
+    .parent = &guac_rdp_keymap_base,
+    .freerdp_keyboard_layout = KBD_GERMAN,
+    .mapping = __guac_rdp_keymap_de_de_qwertz
+};
+
+/* Autogenerated from keymaps/en_us_qwerty.keymap */
+static guac_rdp_keysym_desc __guac_rdp_keymap_en_us_qwerty[] = {
+    { .keysym = 96, .scancode = 41, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 49, .scancode = 2, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 50, .scancode = 3, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 51, .scancode = 4, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 52, .scancode = 5, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 53, .scancode = 6, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 54, .scancode = 7, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 55, .scancode = 8, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 56, .scancode = 9, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 57, .scancode = 10, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 48, .scancode = 11, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 45, .scancode = 12, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 61, .scancode = 13, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 113, .scancode = 16, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 119, .scancode = 17, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 101, .scancode = 18, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 114, .scancode = 19, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 116, .scancode = 20, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 121, .scancode = 21, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 117, .scancode = 22, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 105, .scancode = 23, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 111, .scancode = 24, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 112, .scancode = 25, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 91, .scancode = 26, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 93, .scancode = 27, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 92, .scancode = 43, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 97, .scancode = 30, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 115, .scancode = 31, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 100, .scancode = 32, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 102, .scancode = 33, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 103, .scancode = 34, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 104, .scancode = 35, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 106, .scancode = 36, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 107, .scancode = 37, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 108, .scancode = 38, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 59, .scancode = 39, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 39, .scancode = 40, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 122, .scancode = 44, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 120, .scancode = 45, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 99, .scancode = 46, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 118, .scancode = 47, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 98, .scancode = 48, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 110, .scancode = 49, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 109, .scancode = 50, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 44, .scancode = 51, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 46, .scancode = 52, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 47, .scancode = 53, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 126, .scancode = 41, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 33, .scancode = 2, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 64, .scancode = 3, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 35, .scancode = 4, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 36, .scancode = 5, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 37, .scancode = 6, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 94, .scancode = 7, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 38, .scancode = 8, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 42, .scancode = 9, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 40, .scancode = 10, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 41, .scancode = 11, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 95, .scancode = 12, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 43, .scancode = 13, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 81, .scancode = 16, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 87, .scancode = 17, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 69, .scancode = 18, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 82, .scancode = 19, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 84, .scancode = 20, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 89, .scancode = 21, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 85, .scancode = 22, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 73, .scancode = 23, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 79, .scancode = 24, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 80, .scancode = 25, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 123, .scancode = 26, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 125, .scancode = 27, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 124, .scancode = 43, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 65, .scancode = 30, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 83, .scancode = 31, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 68, .scancode = 32, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 70, .scancode = 33, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 71, .scancode = 34, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 72, .scancode = 35, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 74, .scancode = 36, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 75, .scancode = 37, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 76, .scancode = 38, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 58, .scancode = 39, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 34, .scancode = 40, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 90, .scancode = 44, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 88, .scancode = 45, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 67, .scancode = 46, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 86, .scancode = 47, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 66, .scancode = 48, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 78, .scancode = 49, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 77, .scancode = 50, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 60, .scancode = 51, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 62, .scancode = 52, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    { .keysym = 63, .scancode = 53, .set_keysyms = GUAC_KEYSYMS_SHIFT },
+    {0}
+};
+
+static const guac_rdp_keymap guac_rdp_keymap_en_us_qwerty = { 
+    .name = "en-us-qwerty",
+    .parent = &guac_rdp_keymap_base,
+    .freerdp_keyboard_layout = KBD_US,
+    .mapping = __guac_rdp_keymap_en_us_qwerty
+};
+
+/* Autogenerated from keymaps/fr_fr_azerty.keymap */
+static guac_rdp_keysym_desc __guac_rdp_keymap_fr_fr_azerty[] = {
+    { .keysym = 178, .scancode = 41, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 38, .scancode = 2, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 233, .scancode = 3, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 34, .scancode = 4, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 39, .scancode = 5, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 40, .scancode = 6, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 45, .scancode = 7, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 232, .scancode = 8, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 95, .scancode = 9, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 231, .scancode = 10, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 224, .scancode = 11, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 41, .scancode = 12, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 61, .scancode = 13, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 97, .scancode = 16, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 122, .scancode = 17, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 101, .scancode = 18, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 114, .scancode = 19, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 116, .scancode = 20, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 121, .scancode = 21, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 117, .scancode = 22, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 105, .scancode = 23, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 111, .scancode = 24, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 112, .scancode = 25, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 36, .scancode = 27, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 113, .scancode = 30, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 115, .scancode = 31, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 100, .scancode = 32, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 102, .scancode = 33, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 103, .scancode = 34, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 104, .scancode = 35, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 106, .scancode = 36, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 107, .scancode = 37, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 108, .scancode = 38, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 109, .scancode = 39, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 249, .scancode = 40, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 42, .scancode = 43, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 60, .scancode = 86, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 119, .scancode = 44, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 120, .scancode = 45, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 99, .scancode = 46, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 118, .scancode = 47, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 98, .scancode = 48, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 110, .scancode = 49, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 44, .scancode = 50, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 59, .scancode = 51, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 58, .scancode = 52, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 33, .scancode = 53, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 49, .scancode = 2, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 50, .scancode = 3, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 51, .scancode = 4, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 52, .scancode = 5, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 53, .scancode = 6, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 54, .scancode = 7, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 55, .scancode = 8, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 56, .scancode = 9, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 57, .scancode = 10, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 48, .scancode = 11, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 176, .scancode = 12, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 43, .scancode = 13, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 65, .scancode = 16, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 90, .scancode = 17, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 69, .scancode = 18, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 82, .scancode = 19, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 84, .scancode = 20, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 89, .scancode = 21, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 85, .scancode = 22, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 73, .scancode = 23, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 79, .scancode = 24, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 80, .scancode = 25, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 163, .scancode = 27, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 81, .scancode = 30, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 83, .scancode = 31, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 68, .scancode = 32, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 70, .scancode = 33, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 71, .scancode = 34, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 72, .scancode = 35, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 74, .scancode = 36, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 75, .scancode = 37, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 76, .scancode = 38, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 77, .scancode = 39, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 37, .scancode = 40, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 181, .scancode = 43, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 62, .scancode = 86, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 87, .scancode = 44, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 88, .scancode = 45, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 67, .scancode = 46, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 86, .scancode = 47, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 66, .scancode = 48, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 78, .scancode = 49, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 63, .scancode = 50, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 46, .scancode = 51, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 47, .scancode = 52, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 167, .scancode = 53, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 126, .scancode = 3, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 35, .scancode = 4, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 123, .scancode = 5, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 91, .scancode = 6, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 124, .scancode = 7, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 96, .scancode = 8, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 92, .scancode = 9, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 94, .scancode = 10, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 64, .scancode = 11, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 93, .scancode = 12, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 125, .scancode = 13, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 16785580, .scancode = 18, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 164, .scancode = 27, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 65106, .scancode = 26, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 65111, .scancode = 26, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    {0}
+};
+
+static const guac_rdp_keymap guac_rdp_keymap_fr_fr_azerty = { 
+    .name = "fr-fr-azerty",
+    .parent = &guac_rdp_keymap_base,
+    .freerdp_keyboard_layout = KBD_FRENCH,
+    .mapping = __guac_rdp_keymap_fr_fr_azerty
+};
+
+/* Autogenerated from keymaps/it_it_qwerty.keymap */
+static guac_rdp_keysym_desc __guac_rdp_keymap_it_it_qwerty[] = {
+    { .keysym = 92, .scancode = 41, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 49, .scancode = 2, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 50, .scancode = 3, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 51, .scancode = 4, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 52, .scancode = 5, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 53, .scancode = 6, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 54, .scancode = 7, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 55, .scancode = 8, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 56, .scancode = 9, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 57, .scancode = 10, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 48, .scancode = 11, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 39, .scancode = 12, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 236, .scancode = 13, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 113, .scancode = 16, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 119, .scancode = 17, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 101, .scancode = 18, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 114, .scancode = 19, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 116, .scancode = 20, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 121, .scancode = 21, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 117, .scancode = 22, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 105, .scancode = 23, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 111, .scancode = 24, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 112, .scancode = 25, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 232, .scancode = 26, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 43, .scancode = 27, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 97, .scancode = 30, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 115, .scancode = 31, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 100, .scancode = 32, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 102, .scancode = 33, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 103, .scancode = 34, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 104, .scancode = 35, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 106, .scancode = 36, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 107, .scancode = 37, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 108, .scancode = 38, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 242, .scancode = 39, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 224, .scancode = 40, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 249, .scancode = 43, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 60, .scancode = 86, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 122, .scancode = 44, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 120, .scancode = 45, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 99, .scancode = 46, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 118, .scancode = 47, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 98, .scancode = 48, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 110, .scancode = 49, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 109, .scancode = 50, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 44, .scancode = 51, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 46, .scancode = 52, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 45, .scancode = 53, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 124, .scancode = 41, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 33, .scancode = 2, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 34, .scancode = 3, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 163, .scancode = 4, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 36, .scancode = 5, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 37, .scancode = 6, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 38, .scancode = 7, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 47, .scancode = 8, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 40, .scancode = 9, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 41, .scancode = 10, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 61, .scancode = 11, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 63, .scancode = 12, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 94, .scancode = 13, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 81, .scancode = 16, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 87, .scancode = 17, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 69, .scancode = 18, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 82, .scancode = 19, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 84, .scancode = 20, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 89, .scancode = 21, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 85, .scancode = 22, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 73, .scancode = 23, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 79, .scancode = 24, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 80, .scancode = 25, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 233, .scancode = 26, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 42, .scancode = 27, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 65, .scancode = 30, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 83, .scancode = 31, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 68, .scancode = 32, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 70, .scancode = 33, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 71, .scancode = 34, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 72, .scancode = 35, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 74, .scancode = 36, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 75, .scancode = 37, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 76, .scancode = 38, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 231, .scancode = 39, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 176, .scancode = 40, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 167, .scancode = 43, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 62, .scancode = 86, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 90, .scancode = 44, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 88, .scancode = 45, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 67, .scancode = 46, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 86, .scancode = 47, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 66, .scancode = 48, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 78, .scancode = 49, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 77, .scancode = 50, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 59, .scancode = 51, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 58, .scancode = 52, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 95, .scancode = 53, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 16785580, .scancode = 18, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 91, .scancode = 26, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 93, .scancode = 27, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 64, .scancode = 39, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 35, .scancode = 40, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 123, .scancode = 26, .set_keysyms = GUAC_KEYSYMS_SHIFT_ALTGR },
+    { .keysym = 125, .scancode = 27, .set_keysyms = GUAC_KEYSYMS_SHIFT_ALTGR },
+    {0}
+};
+
+static const guac_rdp_keymap guac_rdp_keymap_it_it_qwerty = { 
+    .name = "it-it-qwerty",
+    .parent = &guac_rdp_keymap_base,
+    .freerdp_keyboard_layout = KBD_ITALIAN,
+    .mapping = __guac_rdp_keymap_it_it_qwerty
+};
+
+/* Autogenerated from keymaps/sv_se_qwerty.keymap */
+static guac_rdp_keysym_desc __guac_rdp_keymap_sv_se_qwerty[] = {
+    { .keysym = 167, .scancode = 41, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 49, .scancode = 2, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 50, .scancode = 3, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 51, .scancode = 4, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 52, .scancode = 5, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 53, .scancode = 6, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 54, .scancode = 7, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 55, .scancode = 8, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 56, .scancode = 9, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 57, .scancode = 10, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 48, .scancode = 11, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 43, .scancode = 12, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 113, .scancode = 16, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 119, .scancode = 17, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 101, .scancode = 18, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 114, .scancode = 19, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 116, .scancode = 20, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 121, .scancode = 21, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 117, .scancode = 22, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 105, .scancode = 23, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 111, .scancode = 24, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 112, .scancode = 25, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 229, .scancode = 26, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 97, .scancode = 30, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 115, .scancode = 31, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 100, .scancode = 32, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 102, .scancode = 33, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 103, .scancode = 34, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 104, .scancode = 35, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 106, .scancode = 36, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 107, .scancode = 37, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 108, .scancode = 38, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 246, .scancode = 39, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 228, .scancode = 40, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 39, .scancode = 43, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 60, .scancode = 86, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 122, .scancode = 44, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 120, .scancode = 45, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 99, .scancode = 46, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 118, .scancode = 47, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 98, .scancode = 48, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 110, .scancode = 49, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 109, .scancode = 50, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 44, .scancode = 51, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 46, .scancode = 52, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 45, .scancode = 53, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 189, .scancode = 41, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 33, .scancode = 2, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 34, .scancode = 3, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 35, .scancode = 4, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 164, .scancode = 5, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 37, .scancode = 6, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 38, .scancode = 7, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 47, .scancode = 8, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 40, .scancode = 9, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 41, .scancode = 10, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 61, .scancode = 11, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 63, .scancode = 12, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 81, .scancode = 16, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 87, .scancode = 17, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 69, .scancode = 18, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 82, .scancode = 19, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 84, .scancode = 20, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 89, .scancode = 21, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 85, .scancode = 22, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 73, .scancode = 23, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 79, .scancode = 24, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 80, .scancode = 25, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 197, .scancode = 26, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 65, .scancode = 30, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 83, .scancode = 31, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 68, .scancode = 32, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 70, .scancode = 33, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 71, .scancode = 34, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 72, .scancode = 35, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 74, .scancode = 36, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 75, .scancode = 37, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 76, .scancode = 38, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 214, .scancode = 39, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 196, .scancode = 40, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 42, .scancode = 43, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 62, .scancode = 86, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 90, .scancode = 44, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 88, .scancode = 45, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 67, .scancode = 46, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 86, .scancode = 47, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 66, .scancode = 48, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 78, .scancode = 49, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 77, .scancode = 50, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 59, .scancode = 51, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 58, .scancode = 52, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 95, .scancode = 53, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 64, .scancode = 3, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 163, .scancode = 4, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 36, .scancode = 5, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 123, .scancode = 8, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 91, .scancode = 9, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 93, .scancode = 10, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 125, .scancode = 11, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 92, .scancode = 12, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 16785580, .scancode = 18, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 124, .scancode = 86, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 181, .scancode = 50, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    { .keysym = 65105, .scancode = 13, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 65104, .scancode = 13, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 65111, .scancode = 27, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
+    { .keysym = 65106, .scancode = 27, .set_keysyms = GUAC_KEYSYMS_SHIFT, .clear_keysyms = GUAC_KEYSYMS_ALTGR },
+    { .keysym = 65107, .scancode = 27, .set_keysyms = GUAC_KEYSYMS_ALTGR, .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
+    {0}
+};
+
+static const guac_rdp_keymap guac_rdp_keymap_sv_se_qwerty = { 
+    .name = "sv-se-qwerty",
+    .parent = &guac_rdp_keymap_base,
+    .freerdp_keyboard_layout = KBD_SWEDISH,
+    .mapping = __guac_rdp_keymap_sv_se_qwerty
+};
+
+const guac_rdp_keymap* GUAC_KEYMAPS[] = {
+    &guac_rdp_keymap_base,
+    &guac_rdp_keymap_failsafe,
+    &guac_rdp_keymap_de_de_qwertz,
+    &guac_rdp_keymap_en_us_qwerty,
+    &guac_rdp_keymap_fr_fr_azerty,
+    &guac_rdp_keymap_it_it_qwerty,
+    &guac_rdp_keymap_sv_se_qwerty,
+    NULL
+};
diff --git a/src/protocols/rdp/audio.c b/src/protocols/rdp/audio.c
deleted file mode 100644
index 5eb8a1f..0000000
--- a/src/protocols/rdp/audio.c
+++ /dev/null
@@ -1,176 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include <stdlib.h>
-#include <string.h>
-#include <pthread.h>
-#include <guacamole/protocol.h>
-#include <guacamole/client.h>
-#include <guacamole/stream.h>
-
-#include "audio.h"
-#include "client.h"
-
-audio_stream* audio_stream_alloc(guac_client* client, audio_encoder* encoder) {
-
-    /* Allocate stream */
-    audio_stream* audio = (audio_stream*) malloc(sizeof(audio_stream));
-    audio->client = client;
-
-    /* Reset buffer stats */
-    audio->used = 0;
-    audio->length = 0x40000;
-
-    audio->encoded_data_used = 0;
-    audio->encoded_data_length = 0x40000;
-
-    /* Allocate buffers */
-    audio->pcm_data = malloc(audio->length);
-    audio->encoded_data = malloc(audio->encoded_data_length);
-
-    /* Assign encoder */
-    audio->encoder = encoder;
-    audio->stream = guac_client_alloc_stream(client);
-
-    /* Ensure socket within new stream is threadsafe */
-    guac_socket_require_threadsafe(audio->stream->socket);
-
-    return audio;
-}
-
-void audio_stream_begin(audio_stream* audio, int rate, int channels, int bps) {
-
-    /* Load PCM properties */
-    audio->rate = rate;
-    audio->channels = channels;
-    audio->bps = bps;
-
-    /* Reset write counter */
-    audio->pcm_bytes_written = 0;
-
-    /* Call handler */
-    audio->encoder->begin_handler(audio);
-
-}
-
-void audio_stream_end(audio_stream* audio) {
-
-    double duration;
-
-    /* Flush stream and finish encoding */
-    audio_stream_flush(audio);
-    audio->encoder->end_handler(audio);
-
-    /* Calculate duration of PCM data */
-    duration = ((double) (audio->pcm_bytes_written * 1000 * 8))
-                / audio->rate / audio->channels / audio->bps;
-
-    /* Send audio */
-    guac_protocol_send_audio(audio->stream->socket,
-            0, audio->encoder->mimetype,
-            duration, audio->encoded_data, audio->encoded_data_used);
-
-    /* Clear data */
-    audio->encoded_data_used = 0;
-
-}
-
-void audio_stream_free(audio_stream* audio) {
-    free(audio->pcm_data);
-    free(audio);
-}
-
-void audio_stream_write_pcm(audio_stream* audio, 
-        unsigned char* data, int length) {
-
-    /* Update counter */
-    audio->pcm_bytes_written += length;
-
-    /* Resize audio buffer if necessary */
-    if (length > audio->length) {
-
-        /* Resize to double provided length */
-        audio->length = length*2;
-        audio->pcm_data = realloc(audio->pcm_data, audio->length);
-
-    }
-
-    /* Flush if necessary */
-    if (audio->used + length > audio->length)
-        audio_stream_flush(audio);
-
-    /* Append to buffer */
-    memcpy(&(audio->pcm_data[audio->used]), data, length);
-    audio->used += length;
-
-}
-
-void audio_stream_flush(audio_stream* audio) {
-
-    /* If data in buffer */
-    if (audio->used != 0) {
-
-        /* Write data */
-        audio->encoder->write_handler(audio,
-                audio->pcm_data, audio->used);
-
-        /* Reset buffer */
-        audio->used = 0;
-
-    }
-
-}
-
-void audio_stream_write_encoded(audio_stream* audio,
-        unsigned char* data, int length) {
-
-    /* Resize audio buffer if necessary */
-    if (audio->encoded_data_used + length > audio->encoded_data_length) {
-
-        /* Increase to double concatenated size to accomodate */
-        audio->encoded_data_length = (audio->encoded_data_length + length)*2;
-        audio->encoded_data = realloc(audio->encoded_data,
-                audio->encoded_data_length);
-
-    }
-
-    /* Append to buffer */
-    memcpy(&(audio->encoded_data[audio->encoded_data_used]), data, length);
-    audio->encoded_data_used += length;
-
-}
-
diff --git a/src/protocols/rdp/audio.h b/src/protocols/rdp/audio.h
deleted file mode 100644
index 441856d..0000000
--- a/src/protocols/rdp/audio.h
+++ /dev/null
@@ -1,212 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef __GUAC_TEST_AUDIO_H
-#define __GUAC_TEST_AUDIO_H
-
-#include <guacamole/client.h>
-#include <guacamole/stream.h>
-
-typedef struct audio_stream audio_stream;
-
-/**
- * Handler which is called when the audio stream is opened.
- */
-typedef void audio_encoder_begin_handler(audio_stream* audio);
-
-/**
- * Handler which is called when the audio stream is closed.
- */
-typedef void audio_encoder_end_handler(audio_stream* audio);
-
-/**
- * Handler which is called when the audio stream is flushed.
- */
-typedef void audio_encoder_write_handler(audio_stream* audio,
-        unsigned char* pcm_data, int length);
-
-/**
- * Arbitrary audio codec encoder.
- */
-typedef struct audio_encoder {
-
-    /**
-     * The mimetype of the audio data encoded by this audio
-     * encoder.
-     */
-    const char* mimetype;
-
-    /**
-     * Handler which will be called when the audio stream is opened.
-     */
-    audio_encoder_begin_handler* begin_handler;
-
-    /**
-     * Handler which will be called when the audio stream is flushed.
-     */
-    audio_encoder_write_handler* write_handler;
-
-    /**
-     * Handler which will be called when the audio stream is closed.
-     */
-    audio_encoder_end_handler* end_handler;
-
-} audio_encoder;
-
-/**
- * Basic audio stream. PCM data is added to the stream. When the stream is
- * flushed, a write handler receives PCM data packets and, presumably, streams
- * them to the guac_stream provided.
- */
-struct audio_stream {
-
-    /**
-     * PCM data buffer, 16-bit samples, 2-channel, 44100 Hz.
-     */
-    unsigned char* pcm_data;
-
-    /**
-     * Number of bytes in buffer.
-     */
-    int used;
-
-    /**
-     * Maximum number of bytes in buffer.
-     */
-    int length;
-
-    /**
-     * Encoded audio data buffer, as written by the encoder.
-     */
-    unsigned char* encoded_data;
-
-    /**
-     * Number of bytes in the encoded data buffer.
-     */
-    int encoded_data_used;
-
-    /**
-     * Maximum number of bytes in the encoded data buffer.
-     */
-    int encoded_data_length;
-
-    /**
-     * Arbitrary codec encoder. When the PCM buffer is flushed, PCM data will
-     * be sent to this encoder.
-     */
-    audio_encoder* encoder;
-
-    /**
-     * The client associated with this audio stream.
-     */
-    guac_client* client;
-
-    /**
-     * The actual stream associated with this audio stream.
-     */
-    guac_stream* stream;
-
-    /**
-     * The number of samples per second of PCM data sent to this stream.
-     */
-    int rate;
-
-    /**
-     * The number of audio channels per sample of PCM data. Legal values are
-     * 1 or 2.
-     */
-    int channels;
-
-    /**
-     * The number of bits per sample per channel for PCM data. Legal values are
-     * 8 or 16.
-     */
-    int bps;
-
-    /**
-     * The number of PCM bytes written since the audio chunk began.
-     */
-    int pcm_bytes_written;
-
-    /**
-     * Encoder-specific state data.
-     */
-    void* data;
-
-};
-
-/**
- * Allocates a new audio stream.
- */
-audio_stream* audio_stream_alloc(guac_client* client,
-        audio_encoder* encoder);
-
-/**
- * Frees the given audio stream.
- */
-void audio_stream_free(audio_stream* stream);
-
-/**
- * Begins a new audio stream.
- */
-void audio_stream_begin(audio_stream* stream, int rate, int channels, int bps);
-
-/**
- * Ends the current audio stream.
- */
-void audio_stream_end(audio_stream* stream);
-
-/**
- * Writes PCM data to the given audio stream.
- */
-void audio_stream_write_pcm(audio_stream* stream,
-        unsigned char* data, int length);
-
-/**
- * Flushes the given audio stream.
- */
-void audio_stream_flush(audio_stream* stream);
-
-/**
- * Appends arbitrarily-encoded data to the encoded_data buffer
- * within the given audio stream.
- */
-void audio_stream_write_encoded(audio_stream* audio,
-        unsigned char* data, int length);
-
-#endif
-
diff --git a/src/protocols/rdp/client.c b/src/protocols/rdp/client.c
index 29773fc..239f4ae 100644
--- a/src/protocols/rdp/client.c
+++ b/src/protocols/rdp/client.c
@@ -1,69 +1,75 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
+/*
+ * Copyright (C) 2015 Glyptodon LLC
  *
- * The Original Code is libguac-client-rdp.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Contributor(s):
- * Matt Hortman
- * David PHAM-VAN <d.pham-van at ulteo.com> Ulteo SAS - http://www.ulteo.com
- * Laurent Meunier <laurent at deltalima.net>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#define _XOPEN_SOURCE 500
+#include "config.h"
 
-#include <stdlib.h>
-#include <string.h>
-#include <pthread.h>
+#include "client.h"
+#include "guac_handlers.h"
+#include "guac_pointer_cursor.h"
+#include "guac_string.h"
+#include "rdp_bitmap.h"
+#include "rdp_gdi.h"
+#include "rdp_glyph.h"
+#include "rdp_keymap.h"
+#include "rdp_pointer.h"
+#include "rdp_stream.h"
+#include "rdp_svc.h"
+#include "resolution.h"
+
+#ifdef ENABLE_COMMON_SSH
+#include "guac_sftp.h"
+#include "guac_ssh.h"
+#include "sftp.h"
+#endif
 
-#include <sys/select.h>
-#include <errno.h>
+#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
+#include "rdp_disp.h"
+#endif
 
-#include <freerdp/freerdp.h>
 #include <freerdp/cache/bitmap.h>
 #include <freerdp/cache/brush.h>
 #include <freerdp/cache/glyph.h>
+#include <freerdp/cache/offscreen.h>
 #include <freerdp/cache/palette.h>
 #include <freerdp/cache/pointer.h>
-#include <freerdp/cache/offscreen.h>
 #include <freerdp/channels/channels.h>
-#include <freerdp/input.h>
-#include <freerdp/constants.h>
+#include <freerdp/freerdp.h>
+#include <guacamole/audio.h>
+#include <guacamole/client.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
 
-#ifdef HAVE_FREERDP_CLIENT_CHANNELS_H
-#include <freerdp/client/channels.h>
+#ifdef HAVE_FREERDP_CLIENT_CLIPRDR_H
+#include <freerdp/client/cliprdr.h>
+#else
+#include "compat/client-cliprdr.h"
 #endif
 
-#ifdef HAVE_FREERDP_ADDIN_H
-#include <freerdp/addin.h>
+#ifdef HAVE_FREERDP_CLIENT_DISP_H
+#include <freerdp/client/disp.h>
+#endif
+
+#ifdef HAVE_FREERDP_EVENT_PUBSUB
+#include <freerdp/event.h>
 #endif
 
 #ifdef ENABLE_WINPR
@@ -72,26 +78,22 @@
 #include "compat/winpr-wtypes.h"
 #endif
 
-#include <guacamole/socket.h>
-#include <guacamole/protocol.h>
-#include <guacamole/client.h>
-#include <guacamole/error.h>
+#ifdef HAVE_FREERDP_ADDIN_H
+#include <freerdp/addin.h>
+#endif
 
-#include "audio.h"
-#include "wav_encoder.h"
+#ifdef HAVE_FREERDP_CLIENT_CHANNELS_H
+#include <freerdp/client/channels.h>
+#endif
 
-#ifdef ENABLE_OGG
-#include "ogg_encoder.h"
+#ifdef HAVE_FREERDP_VERSION_H
+#include <freerdp/version.h>
 #endif
 
-#include "client.h"
-#include "guac_handlers.h"
-#include "rdp_keymap.h"
-#include "rdp_bitmap.h"
-#include "rdp_glyph.h"
-#include "rdp_pointer.h"
-#include "rdp_gdi.h"
-#include "default_pointer.h"
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
 
 /* Client plugin arguments */
 const char* GUAC_CLIENT_ARGS[] = {
@@ -102,16 +104,45 @@ const char* GUAC_CLIENT_ARGS[] = {
     "password",
     "width",
     "height",
+    "dpi",
     "initial-program",
     "color-depth",
     "disable-audio",
     "enable-printing",
+    "enable-drive",
+    "drive-path",
+    "create-drive-path",
     "console",
     "console-audio",
     "server-layout",
     "security",
     "ignore-cert",
     "disable-auth",
+    "remote-app",
+    "remote-app-dir",
+    "remote-app-args",
+    "static-channels",
+    "client-name",
+    "enable-wallpaper",
+    "enable-theming",
+    "enable-font-smoothing",
+    "enable-full-window-drag",
+    "enable-desktop-composition",
+    "enable-menu-animations",
+    "preconnection-id",
+    "preconnection-blob",
+
+#ifdef ENABLE_COMMON_SSH
+    "enable-sftp",
+    "sftp-hostname",
+    "sftp-port",
+    "sftp-username",
+    "sftp-password",
+    "sftp-private-key",
+    "sftp-passphrase",
+    "sftp-directory",
+#endif
+
     NULL
 };
 
@@ -124,23 +155,95 @@ enum RDP_ARGS_IDX {
     IDX_PASSWORD,
     IDX_WIDTH,
     IDX_HEIGHT,
+    IDX_DPI,
     IDX_INITIAL_PROGRAM,
     IDX_COLOR_DEPTH,
     IDX_DISABLE_AUDIO,
     IDX_ENABLE_PRINTING,
+    IDX_ENABLE_DRIVE,
+    IDX_DRIVE_PATH,
+    IDX_CREATE_DRIVE_PATH,
     IDX_CONSOLE,
     IDX_CONSOLE_AUDIO,
     IDX_SERVER_LAYOUT,
     IDX_SECURITY,
     IDX_IGNORE_CERT,
     IDX_DISABLE_AUTH,
+    IDX_REMOTE_APP,
+    IDX_REMOTE_APP_DIR,
+    IDX_REMOTE_APP_ARGS,
+    IDX_STATIC_CHANNELS,
+    IDX_CLIENT_NAME,
+    IDX_ENABLE_WALLPAPER,
+    IDX_ENABLE_THEMING,
+    IDX_ENABLE_FONT_SMOOTHING,
+    IDX_ENABLE_FULL_WINDOW_DRAG,
+    IDX_ENABLE_DESKTOP_COMPOSITION,
+    IDX_ENABLE_MENU_ANIMATIONS,
+    IDX_PRECONNECTION_ID,
+    IDX_PRECONNECTION_BLOB,
+
+#ifdef ENABLE_COMMON_SSH
+    IDX_ENABLE_SFTP,
+    IDX_SFTP_HOSTNAME,
+    IDX_SFTP_PORT,
+    IDX_SFTP_USERNAME,
+    IDX_SFTP_PASSWORD,
+    IDX_SFTP_PRIVATE_KEY,
+    IDX_SFTP_PASSPHRASE,
+    IDX_SFTP_DIRECTORY,
+#endif
+
     RDP_ARGS_COUNT
 };
 
+#if defined(FREERDP_VERSION_MAJOR) && (FREERDP_VERSION_MAJOR > 1 || FREERDP_VERSION_MINOR >= 2)
+int __guac_receive_channel_data(freerdp* rdp_inst, UINT16 channelId, BYTE* data, int size, int flags, int total_size) {
+#else
 int __guac_receive_channel_data(freerdp* rdp_inst, int channelId, UINT8* data, int size, int flags, int total_size) {
+#endif
     return freerdp_channels_data(rdp_inst, channelId, data, size, flags, total_size);
 }
 
+#ifdef HAVE_FREERDP_EVENT_PUBSUB
+/**
+ * Called whenever a channel connects via the PubSub event system within
+ * FreeRDP.
+ *
+ * @param context The rdpContext associated with the active RDP session.
+ * @param e Event-specific arguments, mainly the name of the channel, and a
+ *          reference to the associated plugin loaded for that channel by
+ *          FreeRDP.
+ */
+static void guac_rdp_channel_connected(rdpContext* context,
+        ChannelConnectedEventArgs* e) {
+
+#ifdef HAVE_RDPSETTINGS_SUPPORTDISPLAYCONTROL
+    /* Store reference to the display update plugin once it's connected */
+    if (strcmp(e->name, DISP_DVC_CHANNEL_NAME) == 0) {
+
+        DispClientContext* disp = (DispClientContext*) e->pInterface;
+
+        guac_client* client = ((rdp_freerdp_context*) context)->client;
+        rdp_guac_client_data* guac_client_data =
+            (rdp_guac_client_data*) client->data;
+
+        /* Init module with current display size */
+        guac_rdp_disp_set_size(guac_client_data->disp, context,
+                guac_rdp_get_width(context->instance),
+                guac_rdp_get_height(context->instance));
+
+        /* Store connected channel */
+        guac_rdp_disp_connect(guac_client_data->disp, disp);
+        guac_client_log(client, GUAC_LOG_DEBUG,
+                "Display update channel connected.");
+
+    }
+#endif
+
+}
+#endif
+
 BOOL rdp_freerdp_pre_connect(freerdp* instance) {
 
     rdpContext* context = instance->context;
@@ -151,7 +254,6 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
     rdpPointer* pointer;
     rdpPrimaryUpdate* primary;
     CLRCONV* clrconv;
-    int i;
 
     rdp_guac_client_data* guac_client_data =
         (rdp_guac_client_data*) client->data;
@@ -161,65 +263,137 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
     freerdp_register_addin_provider(freerdp_channels_load_static_addin_entry, 0);
 #endif
 
+#ifdef HAVE_FREERDP_EVENT_PUBSUB
+    /* Subscribe to and handle channel connected events */
+    PubSub_SubscribeChannelConnected(context->pubSub,
+            (pChannelConnectedEventHandler) guac_rdp_channel_connected);
+#endif
+
+#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
+    /* Load virtual channel management plugin */
+    if (freerdp_channels_load_plugin(channels, instance->settings,
+                "drdynvc", instance->settings))
+        guac_client_log(client, GUAC_LOG_WARNING,
+                "Failed to load drdynvc plugin.");
+
+    /* Init display update plugin */
+    guac_client_data->disp = guac_rdp_disp_alloc();
+    guac_rdp_disp_load_plugin(instance->context);
+#endif
+
     /* Load clipboard plugin */
     if (freerdp_channels_load_plugin(channels, instance->settings,
                 "cliprdr", NULL))
-        guac_client_log_error(client, "Failed to load cliprdr plugin.");
+        guac_client_log(client, GUAC_LOG_WARNING,
+                "Failed to load cliprdr plugin. Clipboard will not work.");
 
     /* If audio enabled, choose an encoder */
     if (guac_client_data->settings.audio_enabled) {
 
-        /* Choose an encoding */
-        for (i=0; client->info.audio_mimetypes[i] != NULL; i++) {
-
-            const char* mimetype = client->info.audio_mimetypes[i];
-
-#ifdef ENABLE_OGG
-            /* If Ogg is supported, done. */
-            if (strcmp(mimetype, ogg_encoder->mimetype) == 0) {
-                guac_client_log_info(client, "Loading Ogg Vorbis encoder.");
-                guac_client_data->audio = audio_stream_alloc(client,
-                        ogg_encoder);
-                break;
-            }
-#endif
+        guac_client_data->audio = guac_audio_stream_alloc(client, NULL,
+                GUAC_RDP_AUDIO_RATE,
+                GUAC_RDP_AUDIO_CHANNELS,
+                GUAC_RDP_AUDIO_BPS);
 
-            /* If wav is supported, done. */
-            if (strcmp(mimetype, wav_encoder->mimetype) == 0) {
-                guac_client_log_info(client, "Loading wav encoder.");
-                guac_client_data->audio = audio_stream_alloc(client,
-                        wav_encoder);
-                break;
-            }
+        /* Warn if no audio encoding is available */
+        if (guac_client_data->audio == NULL)
+            guac_client_log(client, GUAC_LOG_INFO,
+                    "No available audio encoding. Sound disabled.");
 
-        }
+    } /* end if audio enabled */
 
-        /* If an encoding is available, load the sound plugin */
-        if (guac_client_data->audio != NULL) {
+    /* Load filesystem if drive enabled */
+    if (guac_client_data->settings.drive_enabled) {
 
-            /* Load sound plugin */
-            if (freerdp_channels_load_plugin(channels, instance->settings,
-                        "guacsnd", guac_client_data->audio))
-                guac_client_log_error(client,
-                        "Failed to load guacsnd plugin.");
+        /* Allocate filesystem */
+        guac_client_data->filesystem =
+            guac_rdp_fs_alloc(client, guac_client_data->settings.drive_path,
+                    guac_client_data->settings.create_drive_path);
 
-        }
-        else
-            guac_client_log_info(client,
-                    "No available audio encoding. Sound disabled.");
+        /* Use for basic uploads if no other handler set */
+        if (client->file_handler == NULL)
+            client->file_handler = guac_rdp_upload_file_handler;
 
-    } /* end if audio enabled */
+    }
 
-    /* If printing enabled, load rdpdr */
-    if (guac_client_data->settings.printing_enabled) {
+    /* If RDPSND/RDPDR required, load them */
+    if (guac_client_data->settings.printing_enabled
+        || guac_client_data->settings.drive_enabled
+        || guac_client_data->settings.audio_enabled) {
 
         /* Load RDPDR plugin */
         if (freerdp_channels_load_plugin(channels, instance->settings,
                     "guacdr", client))
-            guac_client_log_error(client,
-                    "Failed to load guacdr plugin.");
+            guac_client_log(client, GUAC_LOG_WARNING,
+                    "Failed to load guacdr plugin. Drive redirection and "
+                    "printing will not work. Sound MAY not work.");
+
+        /* Load RDPSND plugin */
+        if (freerdp_channels_load_plugin(channels, instance->settings,
+                    "guacsnd", client))
+            guac_client_log(client, GUAC_LOG_WARNING,
+                    "Failed to load guacsnd alongside guacdr plugin. Sound "
+                    "will not work. Drive redirection and printing MAY not "
+                    "work.");
+
+    }
+
+    /* Load RAIL plugin if RemoteApp in use */
+    if (guac_client_data->settings.remote_app != NULL) {
+
+#ifdef LEGACY_FREERDP
+        RDP_PLUGIN_DATA* plugin_data = malloc(sizeof(RDP_PLUGIN_DATA) * 2);
+
+        plugin_data[0].size = sizeof(RDP_PLUGIN_DATA);
+        plugin_data[0].data[0] = guac_client_data->settings.remote_app;
+        plugin_data[0].data[1] = guac_client_data->settings.remote_app_dir;
+        plugin_data[0].data[2] = guac_client_data->settings.remote_app_args; 
+        plugin_data[0].data[3] = NULL;
 
-    } /* end if printing enabled */
+        plugin_data[1].size = 0;
+
+        /* Attempt to load rail */
+        if (freerdp_channels_load_plugin(channels, instance->settings,
+                    "rail", plugin_data))
+            guac_client_log(client, GUAC_LOG_WARNING,
+                    "Failed to load rail plugin. RemoteApp will not work.");
+#else
+        /* Attempt to load rail */
+        if (freerdp_channels_load_plugin(channels, instance->settings,
+                    "rail", instance->settings))
+            guac_client_log(client, GUAC_LOG_WARNING,
+                    "Failed to load rail plugin. RemoteApp will not work.");
+#endif
+
+    }
+
+    /* Load SVC plugin instances for all static channels */
+    if (guac_client_data->settings.svc_names != NULL) {
+
+        char** current = guac_client_data->settings.svc_names;
+        do {
+
+            guac_rdp_svc* svc = guac_rdp_alloc_svc(client, *current);
+
+            /* Attempt to load guacsvc plugin for new static channel */
+            if (freerdp_channels_load_plugin(channels, instance->settings,
+                        "guacsvc", svc)) {
+                guac_client_log(client, GUAC_LOG_WARNING,
+                        "Cannot create static channel \"%s\": failed to load guacsvc plugin.",
+                        svc->name);
+                guac_rdp_free_svc(svc);
+            }
+
+            /* Store and log on success */
+            else {
+                guac_rdp_add_svc(client, svc);
+                guac_client_log(client, GUAC_LOG_INFO, "Created static channel \"%s\"...",
+                        svc->name);
+            }
+
+        } while (*(++current) != NULL);
+
+    }
 
     /* Init color conversion structure */
     clrconv = calloc(1, sizeof(CLRCONV));
@@ -270,6 +444,7 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
     free(pointer);
 
     /* Set up GDI */
+    instance->update->DesktopResize = guac_rdp_gdi_desktop_resize;
     instance->update->EndPaint = guac_rdp_gdi_end_paint;
     instance->update->Palette = guac_rdp_gdi_palette_update;
     instance->update->SetBounds = guac_rdp_gdi_set_bounds;
@@ -290,8 +465,7 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
 
     /* Init channels (pre-connect) */
     if (freerdp_channels_pre_connect(channels, instance)) {
-        guac_protocol_send_error(client->socket, "Error initializing RDP client channel manager");
-        guac_socket_flush(client->socket);
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Error initializing RDP client channel manager");
         return FALSE;
     }
 
@@ -307,8 +481,7 @@ BOOL rdp_freerdp_post_connect(freerdp* instance) {
 
     /* Init channels (post-connect) */
     if (freerdp_channels_post_connect(channels, instance)) {
-        guac_protocol_send_error(client->socket, "Error initializing RDP client channel manager");
-        guac_socket_flush(client->socket);
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Error initializing RDP client channel manager");
         return FALSE;
     }
 
@@ -317,7 +490,11 @@ BOOL rdp_freerdp_post_connect(freerdp* instance) {
     client->handle_messages = rdp_guac_client_handle_messages;
     client->mouse_handler = rdp_guac_client_mouse_handler;
     client->key_handler = rdp_guac_client_key_handler;
-    client->clipboard_handler = rdp_guac_client_clipboard_handler;
+    client->size_handler = rdp_guac_client_size_handler;
+
+    /* Stream handlers */
+    client->clipboard_handler = guac_rdp_clipboard_handler;
+    client->pipe_handler = guac_rdp_svc_pipe_handler;
 
     return TRUE;
 
@@ -330,7 +507,7 @@ BOOL rdp_freerdp_authenticate(freerdp* instance, char** username,
     guac_client* client = ((rdp_freerdp_context*) context)->client;
 
     /* Warn if connection is likely to fail due to lack of credentials */
-    guac_client_log_info(client,
+    guac_client_log(client, GUAC_LOG_INFO,
             "Authentication requested but username or password not given");
     return TRUE;
 
@@ -346,11 +523,11 @@ BOOL rdp_freerdp_verify_certificate(freerdp* instance, char* subject,
 
     /* Bypass validation if ignore_certificate given */
     if (guac_client_data->settings.ignore_certificate) {
-        guac_client_log_info(client, "Certificate validation bypassed");
+        guac_client_log(client, GUAC_LOG_INFO, "Certificate validation bypassed");
         return TRUE;
     }
 
-    guac_client_log_info(client, "Certificate validation failed");
+    guac_client_log(client, GUAC_LOG_INFO, "Certificate validation failed");
     return FALSE;
 
 }
@@ -377,7 +554,7 @@ void __guac_rdp_client_load_keymap(guac_client* client,
         __guac_rdp_client_load_keymap(client, keymap->parent);
 
     /* Log load */
-    guac_client_log_info(client, "Loading keymap \"%s\"", keymap->name);
+    guac_client_log(client, GUAC_LOG_INFO, "Loading keymap \"%s\"", keymap->name);
 
     /* Load mapping into keymap */
     while (mapping->keysym != 0) {
@@ -402,14 +579,7 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
 
     /* Validate number of arguments received */
     if (argc != RDP_ARGS_COUNT) {
-
-        guac_protocol_send_error(client->socket,
-                "Wrong argument count received.");
-        guac_socket_flush(client->socket);
-
-        guac_error = GUAC_STATUS_BAD_ARGUMENT;
-        guac_error_message = "Wrong argument count received";
-
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Wrong argument count received.");
         return 1;
     }
 
@@ -420,7 +590,9 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
     srandom(time(NULL));
 
     /* Init client */
+#ifdef HAVE_FREERDP_CHANNELS_GLOBAL_INIT
     freerdp_channels_global_init();
+#endif
     rdp_inst = freerdp_new();
     rdp_inst->PreConnect = rdp_freerdp_pre_connect;
     rdp_inst->PostConnect = rdp_freerdp_post_connect;
@@ -451,31 +623,31 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
 
     /* NLA security */
     if (strcmp(argv[IDX_SECURITY], "nla") == 0) {
-        guac_client_log_info(client, "Security mode: NLA");
+        guac_client_log(client, GUAC_LOG_INFO, "Security mode: NLA");
         settings->security_mode = GUAC_SECURITY_NLA;
     }
 
     /* TLS security */
     else if (strcmp(argv[IDX_SECURITY], "tls") == 0) {
-        guac_client_log_info(client, "Security mode: TLS");
+        guac_client_log(client, GUAC_LOG_INFO, "Security mode: TLS");
         settings->security_mode = GUAC_SECURITY_TLS;
     }
 
     /* RDP security */
     else if (strcmp(argv[IDX_SECURITY], "rdp") == 0) {
-        guac_client_log_info(client, "Security mode: RDP");
+        guac_client_log(client, GUAC_LOG_INFO, "Security mode: RDP");
         settings->security_mode = GUAC_SECURITY_RDP;
     }
 
     /* ANY security (allow server to choose) */
     else if (strcmp(argv[IDX_SECURITY], "any") == 0) {
-        guac_client_log_info(client, "Security mode: ANY");
+        guac_client_log(client, GUAC_LOG_INFO, "Security mode: ANY");
         settings->security_mode = GUAC_SECURITY_ANY;
     }
 
     /* If nothing given, default to RDP */
     else {
-        guac_client_log_info(client, "No security mode specified. Defaulting to RDP.");
+        guac_client_log(client, GUAC_LOG_INFO, "No security mode specified. Defaulting to RDP.");
         settings->security_mode = GUAC_SECURITY_RDP;
     }
 
@@ -487,35 +659,58 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
     if (argv[IDX_PORT][0] != '\0')
         settings->port = atoi(argv[IDX_PORT]);
 
+    guac_client_log(client, GUAC_LOG_DEBUG,
+            "Client resolution is %ix%i at %i DPI",
+            client->info.optimal_width,
+            client->info.optimal_height,
+            client->info.optimal_resolution);
+
+    /* Use suggested resolution unless overridden */
+    settings->resolution = guac_rdp_suggest_resolution(client);
+    if (argv[IDX_DPI][0] != '\0')
+        settings->resolution = atoi(argv[IDX_DPI]);
+
     /* Use optimal width unless overridden */
-    settings->width = client->info.optimal_width;
+    settings->width = client->info.optimal_width
+                    * settings->resolution
+                    / client->info.optimal_resolution;
+
     if (argv[IDX_WIDTH][0] != '\0')
         settings->width = atoi(argv[IDX_WIDTH]);
 
     /* Use default width if given width is invalid. */
     if (settings->width <= 0) {
         settings->width = RDP_DEFAULT_WIDTH;
-        guac_client_log_error(client,
+        guac_client_log(client, GUAC_LOG_ERROR,
                 "Invalid width: \"%s\". Using default of %i.",
                 argv[IDX_WIDTH], settings->width);
     }
 
-    /* Round width up to nearest multiple of 4 */
-    settings->width = (settings->width + 3) & ~0x3;
+    /* Round width down to nearest multiple of 4 */
+    settings->width = settings->width & ~0x3;
 
     /* Use optimal height unless overridden */
-    settings->height = client->info.optimal_height;
+    settings->height = client->info.optimal_height
+                     * settings->resolution
+                     / client->info.optimal_resolution;
+
     if (argv[IDX_HEIGHT][0] != '\0')
         settings->height = atoi(argv[IDX_HEIGHT]);
 
     /* Use default height if given height is invalid. */
     if (settings->height <= 0) {
         settings->height = RDP_DEFAULT_HEIGHT;
-        guac_client_log_error(client,
+        guac_client_log(client, GUAC_LOG_ERROR,
                 "Invalid height: \"%s\". Using default of %i.",
                 argv[IDX_WIDTH], settings->height);
     }
 
+    guac_client_log(client, GUAC_LOG_DEBUG,
+            "Using resolution of %ix%i at %i DPI",
+            settings->width,
+            settings->height,
+            settings->resolution);
+
     /* Domain */
     settings->domain = NULL;
     if (argv[IDX_DOMAIN][0] != '\0')
@@ -531,11 +726,44 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
     if (argv[IDX_PASSWORD][0] != '\0')
         settings->password = strdup(argv[IDX_PASSWORD]);
 
+    /* Client name */
+    settings->client_name = NULL;
+    if (argv[IDX_CLIENT_NAME][0] != '\0')
+        settings->client_name = strdup(argv[IDX_CLIENT_NAME]);
+
     /* Initial program */
     settings->initial_program = NULL;
     if (argv[IDX_INITIAL_PROGRAM][0] != '\0')
         settings->initial_program = strdup(argv[IDX_INITIAL_PROGRAM]);
 
+    /* RemoteApp program */
+    settings->remote_app = NULL;
+    if (argv[IDX_REMOTE_APP][0] != '\0')
+        settings->remote_app = strdup(argv[IDX_REMOTE_APP]);
+
+    /* RemoteApp working directory */
+    settings->remote_app_dir = NULL;
+    if (argv[IDX_REMOTE_APP_DIR][0] != '\0')
+        settings->remote_app_dir = strdup(argv[IDX_REMOTE_APP_DIR]);
+
+    /* RemoteApp arguments */
+    settings->remote_app_args = NULL;
+    if (argv[IDX_REMOTE_APP_ARGS][0] != '\0')
+        settings->remote_app_args = strdup(argv[IDX_REMOTE_APP_ARGS]);
+
+    /* Static virtual channels */
+    settings->svc_names = NULL;
+    if (argv[IDX_STATIC_CHANNELS][0] != '\0')
+        settings->svc_names = guac_split(argv[IDX_STATIC_CHANNELS], ',');
+
+    /* Performance flags */
+    settings->wallpaper_enabled           = (strcmp(argv[IDX_ENABLE_WALLPAPER],           "true") == 0);
+    settings->theming_enabled             = (strcmp(argv[IDX_ENABLE_THEMING],             "true") == 0);
+    settings->font_smoothing_enabled      = (strcmp(argv[IDX_ENABLE_FONT_SMOOTHING],      "true") == 0);
+    settings->full_window_drag_enabled    = (strcmp(argv[IDX_ENABLE_FULL_WINDOW_DRAG],    "true") == 0);
+    settings->desktop_composition_enabled = (strcmp(argv[IDX_ENABLE_DESKTOP_COMPOSITION], "true") == 0);
+    settings->menu_animations_enabled     = (strcmp(argv[IDX_ENABLE_MENU_ANIMATIONS],     "true") == 0);
+
     /* Session color depth */
     settings->color_depth = RDP_DEFAULT_DEPTH;
     if (argv[IDX_COLOR_DEPTH][0] != '\0')
@@ -544,11 +772,50 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
     /* Use default depth if given depth is invalid. */
     if (settings->color_depth == 0) {
         settings->color_depth = RDP_DEFAULT_DEPTH;
-        guac_client_log_error(client,
+        guac_client_log(client, GUAC_LOG_ERROR,
                 "Invalid color-depth: \"%s\". Using default of %i.",
                 argv[IDX_WIDTH], settings->color_depth);
     }
 
+    /* Preconnection ID */
+    settings->preconnection_id = -1;
+    if (argv[IDX_PRECONNECTION_ID][0] != '\0') {
+
+        /* Parse preconnection ID, warn if invalid */
+        int preconnection_id = atoi(argv[IDX_PRECONNECTION_ID]);
+        if (preconnection_id < 0)
+            guac_client_log(client, GUAC_LOG_WARNING,
+                    "Ignoring invalid preconnection ID: %i",
+                    preconnection_id);
+
+        /* Otherwise, assign specified ID */
+        else {
+            settings->preconnection_id = preconnection_id;
+            guac_client_log(client, GUAC_LOG_DEBUG,
+                    "Preconnection ID: %i", settings->preconnection_id);
+        }
+
+    }
+
+    /* Preconnection BLOB */
+    settings->preconnection_blob = NULL;
+    if (argv[IDX_PRECONNECTION_BLOB][0] != '\0') {
+        settings->preconnection_blob = strdup(argv[IDX_PRECONNECTION_BLOB]);
+        guac_client_log(client, GUAC_LOG_DEBUG,
+                "Preconnection BLOB: \"%s\"", settings->preconnection_blob);
+    }
+
+#ifndef HAVE_RDPSETTINGS_SENDPRECONNECTIONPDU
+    /* Warn if support for the preconnection BLOB / ID is absent */
+    if (settings->preconnection_blob != NULL
+            || settings->preconnection_id != -1) {
+        guac_client_log(client, GUAC_LOG_WARNING,
+                "Installed version of FreeRDP lacks support for the "
+                "preconnection PDU. The specified preconnection BLOB and/or "
+                "ID will be ignored.");
+    }
+#endif
+
     /* Audio enable/disable */
     guac_client_data->settings.audio_enabled =
         (strcmp(argv[IDX_DISABLE_AUDIO], "true") != 0);
@@ -557,13 +824,23 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
     guac_client_data->settings.printing_enabled =
         (strcmp(argv[IDX_ENABLE_PRINTING], "true") == 0);
 
+    /* Drive enable/disable */
+    guac_client_data->settings.drive_enabled =
+        (strcmp(argv[IDX_ENABLE_DRIVE], "true") == 0);
+
+    guac_client_data->settings.drive_path = strdup(argv[IDX_DRIVE_PATH]);
+
+    guac_client_data->settings.create_drive_path =
+        (strcmp(argv[IDX_CREATE_DRIVE_PATH], "true") == 0);
+
     /* Store client data */
     guac_client_data->rdp_inst = rdp_inst;
-    guac_client_data->bounded = FALSE;
     guac_client_data->mouse_button_mask = 0;
-    guac_client_data->current_surface = GUAC_DEFAULT_LAYER;
-    guac_client_data->clipboard = NULL;
+    guac_client_data->clipboard = guac_common_clipboard_alloc(GUAC_RDP_CLIPBOARD_MAX_LENGTH);
+    guac_client_data->requested_clipboard_format = CB_FORMAT_TEXT;
     guac_client_data->audio = NULL;
+    guac_client_data->filesystem = NULL;
+    guac_client_data->available_svc = guac_common_list_alloc();
 
     /* Main socket needs to be threadsafe */
     guac_socket_require_threadsafe(client->socket);
@@ -588,121 +865,142 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
     ((rdp_freerdp_context*) rdp_inst->context)->client = client;
 
     /* Pick keymap based on argument */
-    if (argv[IDX_SERVER_LAYOUT][0] != '\0') {
+    settings->server_layout = NULL;
+    if (argv[IDX_SERVER_LAYOUT][0] != '\0')
+        settings->server_layout =
+            guac_rdp_keymap_find(argv[IDX_SERVER_LAYOUT]);
 
-        /* US English Qwerty */
-        if (strcmp("en-us-qwerty", argv[IDX_SERVER_LAYOUT]) == 0)
-            settings->server_layout = &guac_rdp_keymap_en_us;
+    /* If no keymap requested, use default */
+    if (settings->server_layout == NULL)
+        settings->server_layout = guac_rdp_keymap_find(GUAC_DEFAULT_KEYMAP);
 
-        /* German Qwertz */
-        else if (strcmp("de-de-qwertz", argv[IDX_SERVER_LAYOUT]) == 0)
-            settings->server_layout = &guac_rdp_keymap_de_de;
-
-        /* French Azerty */
-        else if (strcmp("fr-fr-azerty", argv[IDX_SERVER_LAYOUT]) == 0)
-            settings->server_layout = &guac_rdp_keymap_fr_fr;
-
-        /* Failsafe (Unicode) keymap */
-        else if (strcmp("failsafe", argv[IDX_SERVER_LAYOUT]) == 0)
-            settings->server_layout = &guac_rdp_keymap_failsafe;
-
-        /* If keymap unknown, resort to failsafe */
-        else {
+    /* Load keymap into client */
+    __guac_rdp_client_load_keymap(client, settings->server_layout);
 
-            guac_client_log_error(client,
-                "Unknown layout \"%s\". Using the failsafe layout instead.",
-                argv[IDX_SERVER_LAYOUT]);
+#ifdef ENABLE_COMMON_SSH
+    guac_common_ssh_init(client);
 
-            settings->server_layout = &guac_rdp_keymap_failsafe;
+    /* Connect via SSH if SFTP is enabled */
+    if (strcmp(argv[IDX_ENABLE_SFTP], "true") == 0) {
 
-        }
+        guac_client_log(client, GUAC_LOG_DEBUG,
+                "Connecting via SSH for SFTP filesystem access.");
 
-    }
+        /* Parse username - use RDP username by default */
+        const char* sftp_username = argv[IDX_SFTP_USERNAME];
+        if (sftp_username[0] == '\0' && settings->username != NULL)
+            sftp_username = settings->username;
 
-    /* If no keymap requested, assume US */
-    else
-        settings->server_layout = &guac_rdp_keymap_en_us;
+        guac_client_data->sftp_user =
+            guac_common_ssh_create_user(sftp_username);
 
-    /* Load keymap into client */
-    __guac_rdp_client_load_keymap(client, settings->server_layout);
+        /* Import private key, if given */
+        if (argv[IDX_SFTP_PRIVATE_KEY][0] != '\0') {
 
-    /* Push desired settings to FreeRDP */
-    guac_rdp_push_settings(settings, rdp_inst);
+            guac_client_log(client, GUAC_LOG_DEBUG,
+                    "Authenticating with private key.");
 
-    /* Connect to RDP server */
-    if (!freerdp_connect(rdp_inst)) {
-
-        guac_protocol_send_error(client->socket,
-                "Error connecting to RDP server");
-        guac_socket_flush(client->socket);
+            /* Abort if private key cannot be read */
+            if (guac_common_ssh_user_import_key(guac_client_data->sftp_user,
+                        argv[IDX_SFTP_PRIVATE_KEY],
+                        argv[IDX_SFTP_PASSPHRASE])) {
+                guac_common_ssh_destroy_user(guac_client_data->sftp_user);
+                return 1;
+            }
 
-        guac_error = GUAC_STATUS_BAD_STATE;
-        guac_error_message = "Error connecting to RDP server";
+        }
 
-        return 1;
-    }
+        /* Otherwise, use specified password */
+        else {
 
-    /* Pull actual settings back from FreeRDP */
-    guac_rdp_pull_settings(rdp_inst, settings);
+            guac_client_log(client, GUAC_LOG_DEBUG,
+                    "Authenticating with password.");
 
-    /* Send connection name */
-    guac_protocol_send_name(client->socket, settings->hostname);
+            /* Parse password - use RDP password by default */
+            const char* sftp_password = argv[IDX_SFTP_PASSWORD];
+            if (sftp_password[0] == '\0' && settings->password != NULL)
+                sftp_password = settings->password;
 
-    /* Send size */
-    guac_protocol_send_size(client->socket, GUAC_DEFAULT_LAYER,
-            settings->width, settings->height);
+            guac_common_ssh_user_set_password(guac_client_data->sftp_user,
+                    sftp_password);
 
-    /* Create glyph surfaces */
-    guac_client_data->opaque_glyph_surface = cairo_image_surface_create(
-            CAIRO_FORMAT_RGB24, settings->width, settings->height);
-
-    guac_client_data->trans_glyph_surface = cairo_image_surface_create(
-            CAIRO_FORMAT_ARGB32, settings->width, settings->height);
+        }
 
-    /* Set default pointer */
-    guac_rdp_set_default_pointer(client);
+        /* Parse hostname - use RDP hostname by default */
+        const char* sftp_hostname = argv[IDX_SFTP_HOSTNAME];
+        if (sftp_hostname[0] == '\0')
+            sftp_hostname = settings->hostname;
+
+        /* Parse port, defaulting to standard SSH port */
+        const char* sftp_port = argv[IDX_SFTP_PORT];
+        if (sftp_port[0] == '\0')
+            sftp_port = "22";
+
+        /* Attempt SSH connection */
+        guac_client_data->sftp_session =
+            guac_common_ssh_create_session(client, sftp_hostname, sftp_port,
+                    guac_client_data->sftp_user);
+
+        /* Fail if SSH connection does not succeed */
+        if (guac_client_data->sftp_session == NULL) {
+            /* Already aborted within guac_common_ssh_create_session() */
+            guac_common_ssh_destroy_user(guac_client_data->sftp_user);
+            return 1;
+        }
 
-    /* Success */
-    return 0;
+        /* Load and expose filesystem */
+        guac_client_data->sftp_filesystem =
+            guac_common_ssh_create_sftp_filesystem(
+                    guac_client_data->sftp_session, "/");
 
-}
+        /* Abort if SFTP connection fails */
+        if (guac_client_data->sftp_filesystem == NULL) {
+            guac_common_ssh_destroy_session(guac_client_data->sftp_session);
+            guac_common_ssh_destroy_user(guac_client_data->sftp_user);
+            return 1;
+        }
 
-int guac_rdp_clip_rect(rdp_guac_client_data* data, int* x, int* y, int* w, int* h) {
+        /* Configure destination for basic uploads, if specified */
+        if (argv[IDX_SFTP_DIRECTORY][0] != '\0') {
+            client->file_handler = guac_rdp_sftp_file_handler;
+            guac_common_ssh_sftp_set_upload_path(
+                    guac_client_data->sftp_filesystem,
+                    argv[IDX_SFTP_DIRECTORY]);
+        }
 
-    if (data->bounded) {
+        /* Otherwise, use SFTP for basic uploads only if drive not enabled */
+        else if (!settings->drive_enabled)
+            client->file_handler = guac_rdp_sftp_file_handler;
 
-        /* Get rect coordinates */
-        int clipped_left   = *x;
-        int clipped_top    = *y;
-        int clipped_right  = clipped_left + *w - 1;
-        int clipped_bottom = clipped_top  + *h - 1;
+        guac_client_log(client, GUAC_LOG_DEBUG,
+                "SFTP connection succeeded.");
 
-        /* Clip left */
-        if      (clipped_left < data->bounds_left)  clipped_left = data->bounds_left;
-        else if (clipped_left > data->bounds_right) return 1;
+    }
+#endif
 
-        /* Clip right */
-        if      (clipped_right < data->bounds_left)  return 1;
-        else if (clipped_right > data->bounds_right) clipped_right = data->bounds_right;
+    /* Create default surface */
+    guac_client_data->default_surface = guac_common_surface_alloc(client,
+            client->socket, GUAC_DEFAULT_LAYER,
+            settings->width, settings->height);
+    guac_client_data->current_surface = guac_client_data->default_surface;
 
-        /* Clip top */
-        if      (clipped_top < data->bounds_top)    clipped_top = data->bounds_top;
-        else if (clipped_top > data->bounds_bottom) return 1;
+    /* Send connection name */
+    guac_protocol_send_name(client->socket, settings->hostname);
 
-        /* Clip bottom */
-        if      (clipped_bottom < data->bounds_top)    return 1;
-        else if (clipped_bottom > data->bounds_bottom) clipped_bottom = data->bounds_bottom;
+    /* Set default pointer */
+    guac_common_set_pointer_cursor(client);
 
-        /* Store new rect dimensions */
-        *x = clipped_left;
-        *y = clipped_top;
-        *w = clipped_right  - clipped_left + 1;
-        *h = clipped_bottom - clipped_top  + 1;
+    /* Push desired settings to FreeRDP */
+    guac_rdp_push_settings(settings, rdp_inst);
 
+    /* Connect to RDP server */
+    if (!freerdp_connect(rdp_inst)) {
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Error connecting to RDP server");
+        return 1;
     }
 
+    /* Success */
     return 0;
 
 }
 
-
diff --git a/src/protocols/rdp/client.h b/src/protocols/rdp/client.h
index 41b887d..376a161 100644
--- a/src/protocols/rdp/client.h
+++ b/src/protocols/rdp/client.h
@@ -1,67 +1,112 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef _GUAC_RDP_CLIENT_H
 #define _GUAC_RDP_CLIENT_H
 
-#include <pthread.h>
+#include "config.h"
 
-#include <cairo/cairo.h>
+#include "guac_clipboard.h"
+#include "guac_list.h"
+#include "guac_surface.h"
+#include "rdp_fs.h"
+#include "rdp_keymap.h"
+#include "rdp_settings.h"
+
+#ifdef ENABLE_COMMON_SSH
+#include "guac_sftp.h"
+#include "guac_ssh.h"
+#include "guac_ssh_user.h"
+#endif
+
+#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
+#include "rdp_disp.h"
+#endif
 
 #include <freerdp/freerdp.h>
 #include <freerdp/codec/color.h>
-
+#include <guacamole/audio.h>
 #include <guacamole/client.h>
 
-#include "audio.h"
-#include "rdp_keymap.h"
-#include "rdp_settings.h"
+#include <pthread.h>
+#include <stdint.h>
 
 /**
  * The maximum duration of a frame in milliseconds.
  */
-#define GUAC_RDP_FRAME_DURATION 40
+#define GUAC_RDP_FRAME_DURATION 60
 
 /**
  * The amount of time to allow per message read within a frame, in
  * milliseconds. If the server is silent for at least this amount of time, the
  * frame will be considered finished.
  */
-#define GUAC_RDP_FRAME_TIMEOUT 0
+#define GUAC_RDP_FRAME_TIMEOUT 10
+
+/**
+ * The native resolution of most RDP connections. As Windows and other systems
+ * rely heavily on forced 96 DPI, we must assume 96 DPI.
+ */
+#define GUAC_RDP_NATIVE_RESOLUTION 96
+
+/**
+ * The resolution of an RDP connection that would be considered high, but is
+ * tolerable in the case that the client display would be unreasonably small
+ * otherwise.
+ */
+#define GUAC_RDP_HIGH_RESOLUTION 120
+
+/**
+ * The smallest area, in pixels^2, that would be considered reasonable large
+ * screen DPI needs to be adjusted.
+ */
+#define GUAC_RDP_REASONABLE_AREA (800*600)
+
+/**
+ * The maximum number of bytes to allow within the clipboard.
+ */
+#define GUAC_RDP_CLIPBOARD_MAX_LENGTH 262144
+
+/**
+ * Initial rate of audio to stream, in Hz. If the RDP server uses a different
+ * value, the Guacamole audio stream will simply be reset appropriately.
+ */
+#define GUAC_RDP_AUDIO_RATE 44100
+
+/**
+ * The number of channels to stream for audio. If the RDP server uses a
+ * different value, the Guacamole audio stream will simply be reset
+ * appropriately.
+ */
+#define GUAC_RDP_AUDIO_CHANNELS 2
+
+/**
+ * The number of bits per sample within the audio stream. If the RDP server
+ * uses a different value, the Guacamole audio stream will simply be reset
+ * appropriately.
+ */
+#define GUAC_RDP_AUDIO_BPS 16
+
 
 /**
  * Client data that will remain accessible through the guac_client.
@@ -85,94 +130,97 @@ typedef struct rdp_guac_client_data {
     int mouse_button_mask;
 
     /**
-     * Cairo surface which will receive all TRANSPARENT glyphs.
+     * Foreground color for any future glyphs.
      */
-    cairo_surface_t* trans_glyph_surface;
+    uint32_t glyph_color;
 
     /**
-     * Cairo surface which will receive all OPAQUE glyphs.
+     * The display.
      */
-    cairo_surface_t* opaque_glyph_surface;
+    guac_common_surface* default_surface;
 
     /**
-     * The current Cairo surface which will receive all drawn glyphs,
-     * depending on whether we are currently drawing transparent or
-     * opaque glyphs.
+     * The surface that GDI operations should draw to. RDP messages exist which
+     * change this surface to allow drawing to occur off-screen.
      */
-    cairo_surface_t* glyph_surface;
+    guac_common_surface* current_surface;
 
     /**
-     * Cairo instance for drawing to the current glyph surface.
+     * The keymap to use when translating keysyms into scancodes or sequences
+     * of scancodes for RDP.
      */
-    cairo_t* glyph_cairo;
+    guac_rdp_static_keymap keymap;
 
     /**
-     * The Guacamole layer that GDI operations should draw to. RDP messages
-     * exist which change this surface to allow drawing to occur off-screen.
+     * The state of all keys, based on whether events for pressing/releasing
+     * particular keysyms have been received. This is necessary in order to
+     * determine which keys must be released/pressed when a particular
+     * keysym can only be typed through a sequence of scancodes (such as
+     * an Alt-code) because the server-side keymap does not support that
+     * keysym.
      */
-    const guac_layer* current_surface;
+    guac_rdp_keysym_state_map keysym_state;
 
     /**
-     * Whether graphical operations are restricted to a specific bounding
-     * rectangle.
+     * The current clipboard contents.
      */
-    int bounded;
+    guac_common_clipboard* clipboard;
 
     /**
-     * The X coordinate of the upper-left corner of the bounding rectangle,
-     * if any.
+     * The format of the clipboard which was requested. Data received from
+     * the RDP server should conform to this format. This will be one of
+     * several legal clipboard format values defined within FreeRDP, such as
+     * CB_FORMAT_TEXT.
      */
-    int bounds_left;
+    int requested_clipboard_format;
 
     /**
-     * The Y coordinate of the upper-left corner of the bounding rectangle,
-     * if any.
+     * Audio output, if any.
      */
-    int bounds_top;
+    guac_audio_stream* audio;
 
     /**
-     * The X coordinate of the lower-right corner of the bounding rectangle,
-     * if any.
+     * The filesystem being shared, if any.
      */
-    int bounds_right;
+    guac_rdp_fs* filesystem;
 
+#ifdef ENABLE_COMMON_SSH
     /**
-     * The Y coordinate of the lower-right corner of the bounding rectangle,
-     * if any.
+     * The user and credentials used to authenticate for SFTP.
      */
-    int bounds_bottom;
+    guac_common_ssh_user* sftp_user;
 
     /**
-     * The keymap to use when translating keysyms into scancodes or sequences
-     * of scancodes for RDP.
+     * The SSH session used for SFTP.
      */
-    guac_rdp_static_keymap keymap;
+    guac_common_ssh_session* sftp_session;
 
     /**
-     * The state of all keys, based on whether events for pressing/releasing
-     * particular keysyms have been received. This is necessary in order to
-     * determine which keys must be released/pressed when a particular
-     * keysym can only be typed through a sequence of scancodes (such as
-     * an Alt-code) because the server-side keymap does not support that
-     * keysym.
+     * The exposed filesystem object, implemented with SFTP.
      */
-    guac_rdp_keysym_state_map keysym_state;
+    guac_object* sftp_filesystem;
+#endif
 
+#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
     /**
-     * The current text (NOT Unicode) clipboard contents.
+     * Display size update module.
      */
-    char* clipboard;
+    guac_rdp_disp* disp;
+#endif
 
     /**
-     * Audio output, if any.
+     * List of all available static virtual channels.
      */
-    audio_stream* audio;
+    guac_common_list* available_svc;
 
     /**
      * Lock which is locked and unlocked for each RDP message.
      */
     pthread_mutex_t rdp_lock;
 
+    /**
+     * Common attributes for locks.
+     */
     pthread_mutexattr_t attributes;
 
 } rdp_guac_client_data;
@@ -199,16 +247,12 @@ typedef struct rdp_freerdp_context {
      */
     CLRCONV* clrconv;
 
-} rdp_freerdp_context;
+    /**
+     * The current color palette, as received from the RDP server.
+     */
+    UINT32 palette[256];
 
-/**
- * Given the coordinates and dimensions of a rectangle, clips the rectangle to be
- * within the clipping bounds of the client data, if clipping is active.
- *
- * Returns 0 if the rectangle given is visible at all, and 1 if the entire
- * rectangls is outside the clipping rectangle and this invisible.
- */
-int guac_rdp_clip_rect(rdp_guac_client_data* data, int* x, int* y, int* w, int* h);
+} rdp_freerdp_context;
 
 #endif
 
diff --git a/src/protocols/rdp/compat/client-cliprdr.h b/src/protocols/rdp/compat/client-cliprdr.h
index afd32e8..f0beb0c 100644
--- a/src/protocols/rdp/compat/client-cliprdr.h
+++ b/src/protocols/rdp/compat/client-cliprdr.h
@@ -1,42 +1,31 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef __GUAC_CLIENT_CLIPRDR_COMPAT_H
 #define __GUAC_CLIENT_CLIPRDR_COMPAT_H
 
+#include "config.h"
+
 #include <freerdp/plugins/cliprdr.h>
 
 #define CliprdrChannel_Class        RDP_EVENT_CLASS_CLIPRDR
diff --git a/src/protocols/rdp/compat/rail.h b/src/protocols/rdp/compat/rail.h
new file mode 100644
index 0000000..9b89c86
--- /dev/null
+++ b/src/protocols/rdp/compat/rail.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef __GUAC_RAIL_COMPAT_H
+#define __GUAC_RAIL_COMPAT_H
+
+#include "config.h"
+
+#include <freerdp/rail.h>
+
+#define RailChannel_Class                  RDP_EVENT_CLASS_RAIL
+#define RailChannel_ClientSystemParam      RDP_EVENT_TYPE_RAIL_CLIENT_SET_SYSPARAMS
+#define RailChannel_GetSystemParam         RDP_EVENT_TYPE_RAIL_CHANNEL_GET_SYSPARAMS
+#define RailChannel_ServerExecuteResult    RDP_EVENT_TYPE_RAIL_CHANNEL_EXEC_RESULTS
+#define RailChannel_ServerSystemParam      RDP_EVENT_TYPE_RAIL_CHANNEL_SERVER_SYSPARAM
+#define RailChannel_ServerMinMaxInfo       RDP_EVENT_TYPE_RAIL_CHANNEL_SERVER_MINMAXINFO
+#define RailChannel_ServerLocalMoveSize    RDP_EVENT_TYPE_RAIL_CHANNEL_SERVER_LOCALMOVESIZE
+#define RailChannel_ServerGetAppIdResponse RDP_EVENT_TYPE_RAIL_CHANNEL_APPID_RESP
+#define RailChannel_ServerLanguageBarInfo  RDP_EVENT_TYPE_RAIL_CHANNEL_LANGBARINFO
+
+#endif
+
diff --git a/src/protocols/rdp/compat/winpr-stream.c b/src/protocols/rdp/compat/winpr-stream.c
index b28c11b..2a3154f 100644
--- a/src/protocols/rdp/compat/winpr-stream.c
+++ b/src/protocols/rdp/compat/winpr-stream.c
@@ -1,40 +1,29 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
 
 #include "winpr-stream.h"
+#include "winpr-wtypes.h"
 
 /*
  * NOTE: Because the old API did not support local allocation of the buffer
diff --git a/src/protocols/rdp/compat/winpr-stream.h b/src/protocols/rdp/compat/winpr-stream.h
index 70a6496..d4e1601 100644
--- a/src/protocols/rdp/compat/winpr-stream.h
+++ b/src/protocols/rdp/compat/winpr-stream.h
@@ -1,70 +1,68 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef __GUAC_WINPR_STREAM_COMPAT_H
 #define __GUAC_WINPR_STREAM_COMPAT_H
 
-#include <freerdp/utils/stream.h>
+#include "config.h"
+
 #include "winpr-wtypes.h"
 
+#include <freerdp/utils/stream.h>
+
+#include <stddef.h>
+
 /* FreeRDP 1.0 streams */
 
 #define Stream_Write                   stream_write
 #define Stream_Write_UINT8             stream_write_uint8
 #define Stream_Write_UINT16            stream_write_uint16
 #define Stream_Write_UINT32            stream_write_uint32
+#define Stream_Write_UINT64            stream_write_uint64
 
 #define Stream_Read                    stream_read
 #define Stream_Read_UINT8              stream_read_uint8
 #define Stream_Read_UINT16             stream_read_uint16
 #define Stream_Read_UINT32             stream_read_uint32
+#define Stream_Read_UINT64             stream_read_uint64
 
 #define Stream_Seek                    stream_seek
 #define Stream_Seek_UINT8              stream_seek_uint8
 #define Stream_Seek_UINT16             stream_seek_uint16
 #define Stream_Seek_UINT32             stream_seek_uint32
+#define Stream_Seek_UINT64             stream_seek_uint64
 
 #define Stream_GetPointer              stream_get_mark
 #define Stream_EnsureRemainingCapacity stream_check_size
 #define Stream_Write                   stream_write
+#define Stream_Zero                    stream_write_zero
+#define Stream_Fill                    stream_set_byte
 #define Stream_GetPosition             stream_get_pos
 #define Stream_SetPosition             stream_set_pos
 #define Stream_SetPointer              stream_set_mark
 #define Stream_Buffer                  stream_get_head
 #define Stream_Pointer                 stream_get_tail
+#define Stream_Length                  stream_get_size
 
 #define wStream                        STREAM
 #define wMessage                       RDP_EVENT
diff --git a/src/protocols/rdp/compat/winpr-wtypes.h b/src/protocols/rdp/compat/winpr-wtypes.h
index 69dbc44..de881ef 100644
--- a/src/protocols/rdp/compat/winpr-wtypes.h
+++ b/src/protocols/rdp/compat/winpr-wtypes.h
@@ -1,48 +1,38 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef __GUAC_WINPR_WTYPES_COMPAT_H
 #define __GUAC_WINPR_WTYPES_COMPAT_H
 
+#include "config.h"
+
 #include <freerdp/types.h>
 
 typedef uint8   BYTE;
 typedef uint8   UINT8;
 typedef uint16  UINT16;
 typedef uint32  UINT32;
+typedef uint64  UINT64;
 typedef boolean BOOL;
 
 #define TRUE  true
diff --git a/src/protocols/rdp/config.h b/src/protocols/rdp/config.h
deleted file mode 100644
index e69de29..0000000
diff --git a/src/protocols/rdp/default_pointer.c b/src/protocols/rdp/default_pointer.c
deleted file mode 100644
index e38d767..0000000
--- a/src/protocols/rdp/default_pointer.c
+++ /dev/null
@@ -1,106 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include <cairo/cairo.h>
-#include <guacamole/client.h>
-#include <guacamole/protocol.h>
-#include <guacamole/socket.h>
-
-/* Macros for prettying up the embedded image. */
-#define X 0x00,0x00,0x00,0xFF
-#define O 0xFF,0xFF,0xFF,0xFF
-#define _ 0x00,0x00,0x00,0x00
-
-/* Dimensions */
-const int guac_rdp_default_pointer_width  = 11;
-const int guac_rdp_default_pointer_height = 16;
-
-/* Format */
-const cairo_format_t guac_rdp_default_pointer_format = CAIRO_FORMAT_ARGB32;
-const int guac_rdp_default_pointer_stride = 44;
-
-/* Embedded pointer graphic */
-unsigned char guac_rdp_default_pointer[] = {
-
-        O,_,_,_,_,_,_,_,_,_,_,
-        O,O,_,_,_,_,_,_,_,_,_,
-        O,X,O,_,_,_,_,_,_,_,_,
-        O,X,X,O,_,_,_,_,_,_,_,
-        O,X,X,X,O,_,_,_,_,_,_,
-        O,X,X,X,X,O,_,_,_,_,_,
-        O,X,X,X,X,X,O,_,_,_,_,
-        O,X,X,X,X,X,X,O,_,_,_,
-        O,X,X,X,X,X,X,X,O,_,_,
-        O,X,X,X,X,X,X,X,X,O,_,
-        O,X,X,X,X,X,O,O,O,O,O,
-        O,X,X,O,X,X,O,_,_,_,_,
-        O,X,O,_,O,X,X,O,_,_,_,
-        O,O,_,_,O,X,X,O,_,_,_,
-        O,_,_,_,_,O,X,X,O,_,_,
-        _,_,_,_,_,O,O,O,O,_,_
-
-};
-
-
-void guac_rdp_set_default_pointer(guac_client* client) {
-
-    guac_socket* socket = client->socket;
-
-    /* Draw to buffer */
-    guac_layer* cursor = guac_client_alloc_buffer(client);
-
-    cairo_surface_t* graphic = cairo_image_surface_create_for_data(
-            guac_rdp_default_pointer,
-            guac_rdp_default_pointer_format,
-            guac_rdp_default_pointer_width,
-            guac_rdp_default_pointer_height,
-            guac_rdp_default_pointer_stride);
-
-    guac_protocol_send_png(socket, GUAC_COMP_SRC, cursor, 0, 0, graphic);
-    cairo_surface_destroy(graphic);
-
-    /* Set cursor */
-    guac_protocol_send_cursor(socket, 0, 0, cursor,
-            0, 0,
-            guac_rdp_default_pointer_width,
-            guac_rdp_default_pointer_height);
-
-    /* Free buffer */
-    guac_client_free_buffer(client, cursor);
-
-}
-
diff --git a/src/protocols/rdp/default_pointer.h b/src/protocols/rdp/default_pointer.h
deleted file mode 100644
index b13ff48..0000000
--- a/src/protocols/rdp/default_pointer.h
+++ /dev/null
@@ -1,76 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef _GUAC_RDP_DEFAULT_POINTER_H
-#define _GUAC_RDP_DEFAULT_POINTER_H
-
-#include <cairo/cairo.h>
-#include <guacamole/client.h>
-
-/**
- * Width of the embedded mouse cursor graphic.
- */
-extern const int guac_rdp_default_pointer_width;
-
-/**
- * Height of the embedded mouse cursor graphic.
- */
-extern const int guac_rdp_default_pointer_height;
-
-/**
- * Number of bytes in each row of the embedded mouse cursor graphic.
- */
-extern const int guac_rdp_default_pointer_stride;
-
-/**
- * The Cairo grapic format of the mouse cursor graphic.
- */
-extern const cairo_format_t guac_rdp_default_pointer_format;
-
-/**
- * Embedded mouse cursor graphic.
- */
-extern unsigned char guac_rdp_default_pointer[];
-
-/**
- * Set the cursor of the remote display to the embedded cursor graphic.
- *
- * @param client The guac_client to send the cursor to.
- */
-void guac_rdp_set_default_pointer(guac_client* client);
-
-#endif
diff --git a/src/protocols/rdp/guac_handlers.c b/src/protocols/rdp/guac_handlers.c
index 258d2ea..86eec9c 100644
--- a/src/protocols/rdp/guac_handlers.c
+++ b/src/protocols/rdp/guac_handlers.c
@@ -1,57 +1,58 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
- * Matt Hortman
- * Jocelyn DELALANDE <j.delalande at ulteo.com> Ulteo SAS - http://www.ulteo.com
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Portions created by Ulteo SAS employees are Copyright (C) 2012 Ulteo SAS
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <pthread.h>
-#include <stdlib.h>
-#include <string.h>
+#include "config.h"
 
-#include <sys/select.h>
-#include <errno.h>
+#include "client.h"
+#include "guac_clipboard.h"
+#include "guac_handlers.h"
+#include "guac_list.h"
+#include "guac_surface.h"
+#include "rdp_cliprdr.h"
+#include "rdp_keymap.h"
+#include "rdp_fs.h"
+#include "rdp_rail.h"
+#include "rdp_stream.h"
+
+#ifdef ENABLE_COMMON_SSH
+#include <guac_sftp.h>
+#include <guac_ssh.h>
+#include <guac_ssh_user.h>
+#endif
 
-#include <freerdp/freerdp.h>
+#include <freerdp/cache/cache.h>
 #include <freerdp/channels/channels.h>
-#include <freerdp/input.h>
 #include <freerdp/codec/color.h>
-#include <freerdp/cache/cache.h>
+#include <freerdp/freerdp.h>
+#include <freerdp/input.h>
 #include <freerdp/utils/event.h>
+#include <guacamole/client.h>
+#include <guacamole/error.h>
+#include <guacamole/protocol.h>
+#include <guacamole/timestamp.h>
+
+#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
+#include "rdp_disp.h"
+#endif
 
 #ifdef HAVE_FREERDP_CLIENT_CLIPRDR_H
 #include <freerdp/client/cliprdr.h>
@@ -59,26 +60,21 @@
 #include "compat/client-cliprdr.h"
 #endif
 
-#ifdef ENABLE_WINPR
-#include <winpr/wtypes.h>
+#ifdef LEGACY_FREERDP
+#include "compat/rail.h"
 #else
-#include "compat/winpr-wtypes.h"
+#include <freerdp/rail.h>
 #endif
 
-#include <guacamole/socket.h>
-#include <guacamole/protocol.h>
-#include <guacamole/client.h>
-#include <guacamole/error.h>
-
-#include "client.h"
-#include "rdp_keymap.h"
-#include "rdp_cliprdr.h"
-#include "guac_handlers.h"
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <sys/select.h>
+#include <sys/time.h>
 
 void __guac_rdp_update_keysyms(guac_client* client, const int* keysym_string, int from, int to);
 int __guac_rdp_send_keysym(guac_client* client, int keysym, int pressed);
 
-
 int rdp_guac_client_free_handler(guac_client* client) {
 
     rdp_guac_client_data* guac_client_data =
@@ -95,10 +91,37 @@ int rdp_guac_client_free_handler(guac_client* client) {
     cache_free(rdp_inst->context->cache);
     freerdp_free(rdp_inst);
 
+    /* Clean up filesystem, if allocated */
+    if (guac_client_data->filesystem != NULL)
+        guac_rdp_fs_free(guac_client_data->filesystem);
+
+#ifdef ENABLE_COMMON_SSH
+    /* Free SFTP filesystem, if loaded */
+    if (guac_client_data->sftp_filesystem)
+        guac_common_ssh_destroy_sftp_filesystem(guac_client_data->sftp_filesystem);
+
+    /* Free SFTP session */
+    if (guac_client_data->sftp_session)
+        guac_common_ssh_destroy_session(guac_client_data->sftp_session);
+
+    /* Free SFTP user */
+    if (guac_client_data->sftp_user)
+        guac_common_ssh_destroy_user(guac_client_data->sftp_user);
+
+    guac_common_ssh_uninit();
+#endif
+
+#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
+    /* Free display update module */
+    guac_rdp_disp_free(guac_client_data->disp);
+#endif
+
+    /* Free SVC list */
+    guac_common_list_free(guac_client_data->available_svc);
+
     /* Free client data */
-    cairo_surface_destroy(guac_client_data->opaque_glyph_surface);
-    cairo_surface_destroy(guac_client_data->trans_glyph_surface);
-    free(guac_client_data->clipboard);
+    guac_common_clipboard_free(guac_client_data->clipboard);
+    guac_common_surface_free(guac_client_data->default_surface);
     free(guac_client_data);
 
     return 0;
@@ -127,16 +150,14 @@ static int rdp_guac_client_wait_for_messages(guac_client* client, int timeout_us
 
     /* Get RDP fds */
     if (!freerdp_get_fds(rdp_inst, read_fds, &read_count, write_fds, &write_count)) {
-        guac_error = GUAC_STATUS_BAD_STATE;
-        guac_error_message = "Unable to read RDP file descriptors";
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Unable to read RDP file descriptors.");
         return -1;
     }
 
     /* Get channel fds */
     if (!freerdp_channels_get_fds(channels, rdp_inst, read_fds, &read_count, write_fds,
                 &write_count)) {
-        guac_error = GUAC_STATUS_BAD_STATE;
-        guac_error_message = "Unable to read RDP channel file descriptors";
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Unable to read RDP channel file descriptors.");
         return -1;
     }
 
@@ -161,8 +182,7 @@ static int rdp_guac_client_wait_for_messages(guac_client* client, int timeout_us
 
     /* If no file descriptors, error */
     if (max_fd == 0) {
-        guac_error = GUAC_STATUS_BAD_STATE;
-        guac_error_message = "No file descriptors";
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "No file descriptors associated with RDP connection.");
         return -1;
     }
 
@@ -178,8 +198,7 @@ static int rdp_guac_client_wait_for_messages(guac_client* client, int timeout_us
             return 0;
 
         /* Otherwise, return as error */
-        guac_error = GUAC_STATUS_SEE_ERRNO;
-        guac_error_message = "Error waiting for file descriptor";
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Error waiting for file descriptor.");
         return -1;
 
     }
@@ -196,6 +215,13 @@ int rdp_guac_client_handle_messages(guac_client* client) {
     rdpChannels* channels = rdp_inst->context->channels;
     wMessage* event;
 
+#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
+    /* Update remote display size */
+    pthread_mutex_lock(&(guac_client_data->rdp_lock));
+    guac_rdp_disp_update_size(guac_client_data->disp, rdp_inst->context);
+    pthread_mutex_unlock(&(guac_client_data->rdp_lock));
+#endif
+
     /* Wait for messages */
     int wait_result = rdp_guac_client_wait_for_messages(client, 250000);
     guac_timestamp frame_start = guac_timestamp_current();
@@ -208,16 +234,14 @@ int rdp_guac_client_handle_messages(guac_client* client) {
 
         /* Check the libfreerdp fds */
         if (!freerdp_check_fds(rdp_inst)) {
-            guac_error = GUAC_STATUS_BAD_STATE;
-            guac_error_message = "Error handling RDP file descriptors";
+            guac_client_log(client, GUAC_LOG_DEBUG, "Error handling RDP file descriptors");
             pthread_mutex_unlock(&(guac_client_data->rdp_lock));
             return 1;
         }
 
         /* Check channel fds */
         if (!freerdp_channels_check_fds(channels, rdp_inst)) {
-            guac_error = GUAC_STATUS_BAD_STATE;
-            guac_error_message = "Error handling RDP channel file descriptors";
+            guac_client_log(client, GUAC_LOG_DEBUG, "Error handling RDP channel file descriptors");
             pthread_mutex_unlock(&(guac_client_data->rdp_lock));
             return 1;
         }
@@ -226,13 +250,17 @@ int rdp_guac_client_handle_messages(guac_client* client) {
         event = freerdp_channels_pop_event(channels);
         if (event) {
 
-            /* Handle clipboard events */
+            /* Handle channel events (clipboard and RAIL) */
 #ifdef LEGACY_EVENT
             if (event->event_class == CliprdrChannel_Class)
                 guac_rdp_process_cliprdr_event(client, event);
+            else if (event->event_class == RailChannel_Class)
+                guac_rdp_process_rail_event(client, event);
 #else
             if (GetMessageClass(event->id) == CliprdrChannel_Class)
                 guac_rdp_process_cliprdr_event(client, event);
+            else if (GetMessageClass(event->id) == RailChannel_Class)
+                guac_rdp_process_rail_event(client, event);
 #endif
 
             freerdp_event_free(event);
@@ -241,16 +269,11 @@ int rdp_guac_client_handle_messages(guac_client* client) {
 
         /* Handle RDP disconnect */
         if (freerdp_shall_disconnect(rdp_inst)) {
-            guac_error = GUAC_STATUS_NO_INPUT;
-            guac_error_message = "RDP server closed connection";
+            guac_client_log(client, GUAC_LOG_INFO, "RDP server closed connection");
             pthread_mutex_unlock(&(guac_client_data->rdp_lock));
             return 1;
         }
 
-        /* Flush any audio */
-        if (guac_client_data->audio != NULL)
-            guac_socket_flush(guac_client_data->audio->stream->socket);
-
         pthread_mutex_unlock(&(guac_client_data->rdp_lock));
 
         /* Calculate time remaining in frame */
@@ -271,6 +294,7 @@ int rdp_guac_client_handle_messages(guac_client* client) {
         return 1;
 
     /* Success */
+    guac_common_surface_flush(guac_client_data->default_surface);
     return 0;
 
 }
@@ -343,7 +367,6 @@ int rdp_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) {
 
         }
 
-
         guac_client_data->mouse_button_mask = mask;
     }
 
@@ -352,7 +375,6 @@ int rdp_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) {
     return 0;
 }
 
-
 int __guac_rdp_send_keysym(guac_client* client, int keysym, int pressed) {
 
     rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data;
@@ -361,6 +383,8 @@ int __guac_rdp_send_keysym(guac_client* client, int keysym, int pressed) {
     /* If keysym can be in lookup table */
     if (GUAC_RDP_KEYSYM_STORABLE(keysym)) {
 
+        int pressed_flags;
+
         /* Look up scancode mapping */
         const guac_rdp_keysym_desc* keysym_desc =
             &GUAC_RDP_KEYSYM_LOOKUP(guac_client_data->keymap, keysym);
@@ -378,11 +402,14 @@ int __guac_rdp_send_keysym(guac_client* client, int keysym, int pressed) {
             if (keysym_desc->clear_keysyms != NULL)
                 __guac_rdp_update_keysyms(client, keysym_desc->clear_keysyms, 1, 0);
 
+            /* Determine proper event flag for pressed state */
+            if (pressed)
+                pressed_flags = KBD_FLAGS_DOWN;
+            else
+                pressed_flags = KBD_FLAGS_RELEASE;
+
             /* Send actual key */
-            rdp_inst->input->KeyboardEvent(
-                    rdp_inst->input,
-                    keysym_desc->flags
-                        | (pressed ? KBD_FLAGS_DOWN : KBD_FLAGS_RELEASE),
+            rdp_inst->input->KeyboardEvent(rdp_inst->input, keysym_desc->flags | pressed_flags,
                     keysym_desc->scancode);
 
             /* If defined, release any keys that were originally released */
@@ -406,6 +433,9 @@ int __guac_rdp_send_keysym(guac_client* client, int keysym, int pressed) {
      * DOWN/RELEASE flags */
     if (pressed) {
 
+        guac_client_log(client, GUAC_LOG_DEBUG,
+                "Sending keysym 0x%x as Unicode", keysym);
+
         /* Translate keysym into codepoint */
         int codepoint;
         if (keysym <= 0xFF)
@@ -413,7 +443,7 @@ int __guac_rdp_send_keysym(guac_client* client, int keysym, int pressed) {
         else if (keysym >= 0x1000000)
             codepoint = keysym & 0xFFFFFF;
         else {
-            guac_client_log_info(client,
+            guac_client_log(client, GUAC_LOG_DEBUG,
                     "Unmapped keysym has no equivalent unicode "
                     "value: 0x%x", keysym);
             return 0;
@@ -467,29 +497,27 @@ int rdp_guac_client_key_handler(guac_client* client, int keysym, int pressed) {
 
 }
 
-int rdp_guac_client_clipboard_handler(guac_client* client, char* data) {
-
-    rdpChannels* channels = 
-        ((rdp_guac_client_data*) client->data)->rdp_inst->context->channels;
+int rdp_guac_client_size_handler(guac_client* client, int width, int height) {
 
-    RDP_CB_FORMAT_LIST_EVENT* format_list =
-        (RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new(
-            CliprdrChannel_Class,
-            CliprdrChannel_FormatList,
-            NULL, NULL);
+#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
+    rdp_guac_client_data* guac_client_data =
+        (rdp_guac_client_data*) client->data;
 
-    /* Free existing data */
-    free(((rdp_guac_client_data*) client->data)->clipboard);
+    freerdp* rdp_inst = guac_client_data->rdp_inst;
 
-    /* Store data in client */
-    ((rdp_guac_client_data*) client->data)->clipboard = strdup(data);
+    /* Convert client pixels to remote pixels */
+    width  = width  * guac_client_data->settings.resolution
+                    / client->info.optimal_resolution;
 
-    /* Notify server that text data is now available */
-    format_list->formats = (UINT32*) malloc(sizeof(UINT32));
-    format_list->formats[0] = CB_FORMAT_TEXT;
-    format_list->num_formats = 1;
+    height = height * guac_client_data->settings.resolution
+                    / client->info.optimal_resolution;
 
-    freerdp_channels_send_event(channels, (wMessage*) format_list);
+    /* Send display update */
+    pthread_mutex_lock(&(guac_client_data->rdp_lock));
+    guac_rdp_disp_set_size(guac_client_data->disp, rdp_inst->context,
+            width, height);
+    pthread_mutex_unlock(&(guac_client_data->rdp_lock));
+#endif
 
     return 0;
 
diff --git a/src/protocols/rdp/guac_handlers.h b/src/protocols/rdp/guac_handlers.h
index 5e9ccee..2932834 100644
--- a/src/protocols/rdp/guac_handlers.h
+++ b/src/protocols/rdp/guac_handlers.h
@@ -1,50 +1,38 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef _GUAC_RDP_GUAC_HANDLERS_H
 #define _GUAC_RDP_GUAC_HANDLERS_H
 
+#include "config.h"
+
 #include <guacamole/client.h>
 
 int rdp_guac_client_free_handler(guac_client* client);
 int rdp_guac_client_handle_messages(guac_client* client);
 int rdp_guac_client_mouse_handler(guac_client* client, int x, int y, int mask);
 int rdp_guac_client_key_handler(guac_client* client, int keysym, int pressed);
-int rdp_guac_client_clipboard_handler(guac_client* client, char* data);
+int rdp_guac_client_size_handler(guac_client* client, int width, int height);
 
 #endif
 
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages.c b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages.c
new file mode 100644
index 0000000..bfd8ead
--- /dev/null
+++ b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages.c
@@ -0,0 +1,532 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "rdpdr_fs_messages_dir_info.h"
+#include "rdpdr_fs_messages_file_info.h"
+#include "rdpdr_fs_messages.h"
+#include "rdpdr_fs_messages_vol_info.h"
+#include "rdpdr_messages.h"
+#include "rdpdr_service.h"
+#include "rdp_fs.h"
+#include "rdp_status.h"
+#include "unicode.h"
+
+#include <freerdp/utils/svc_plugin.h>
+#include <guacamole/client.h>
+
+#ifdef ENABLE_WINPR
+#include <winpr/stream.h>
+#include <winpr/wtypes.h>
+#else
+#include "compat/winpr-stream.h"
+#include "compat/winpr-wtypes.h"
+#endif
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+void guac_rdpdr_fs_process_create(guac_rdpdr_device* device,
+        wStream* input_stream, int completion_id) {
+
+    wStream* output_stream;
+    int file_id;
+
+    int desired_access, file_attributes;
+    int create_disposition, create_options, path_length;
+    char path[GUAC_RDP_FS_MAX_PATH];
+
+    /* Read "create" information */
+    Stream_Read_UINT32(input_stream, desired_access);
+    Stream_Seek_UINT64(input_stream); /* allocation size */
+    Stream_Read_UINT32(input_stream, file_attributes);
+    Stream_Seek_UINT32(input_stream); /* shared access */
+    Stream_Read_UINT32(input_stream, create_disposition);
+    Stream_Read_UINT32(input_stream, create_options);
+    Stream_Read_UINT32(input_stream, path_length);
+
+    /* Convert path to UTF-8 */
+    guac_rdp_utf16_to_utf8(Stream_Pointer(input_stream), path_length/2 - 1,
+            path, sizeof(path));
+
+    /* Open file */
+    file_id = guac_rdp_fs_open((guac_rdp_fs*) device->data, path,
+            desired_access, file_attributes,
+            create_disposition, create_options);
+
+    guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+            "%s: [file_id=%i] "
+             "desired_access=0x%x, file_attributes=0x%x, "
+             "create_disposition=0x%x, create_options=0x%x, path=\"%s\"",
+             __func__, file_id,
+             desired_access, file_attributes,
+             create_disposition, create_options, path);
+
+    /* If an error occurred, notify server */
+    if (file_id < 0) {
+        guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR,
+                "File open refused (%i): \"%s\"", file_id, path);
+
+        output_stream = guac_rdpdr_new_io_completion(device, completion_id,
+                guac_rdp_fs_get_status(file_id), 5);
+        Stream_Write_UINT32(output_stream, 0); /* fileId */
+        Stream_Write_UINT8(output_stream,  0); /* information */
+    }
+
+    /* Otherwise, open succeeded */
+    else {
+
+        guac_rdp_fs_file* file;
+
+        output_stream = guac_rdpdr_new_io_completion(device, completion_id,
+                STATUS_SUCCESS, 5);
+        Stream_Write_UINT32(output_stream, file_id);    /* fileId */
+        Stream_Write_UINT8(output_stream,  0);          /* information */
+
+        /* Create \Download if it doesn't exist */
+        file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
+        if (file != NULL && strcmp(file->absolute_path, "\\") == 0) {
+            int download_id =
+                guac_rdp_fs_open((guac_rdp_fs*) device->data, "\\Download",
+                    ACCESS_GENERIC_READ, 0,
+                    DISP_FILE_OPEN_IF, FILE_DIRECTORY_FILE);
+
+            if (download_id >= 0)
+                guac_rdp_fs_close((guac_rdp_fs*) device->data, download_id);
+        }
+
+    }
+
+    svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_read(guac_rdpdr_device* device,
+        wStream* input_stream, int file_id, int completion_id) {
+
+    UINT32 length;
+    UINT64 offset;
+    char* buffer;
+    int bytes_read;
+
+    wStream* output_stream;
+
+    /* Read packet */
+    Stream_Read_UINT32(input_stream, length);
+    Stream_Read_UINT64(input_stream, offset);
+
+    guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+            "%s: [file_id=%i] length=%i, offset=%" PRIu64,
+             __func__, file_id, length, (uint64_t) offset);
+
+    /* Ensure buffer size does not exceed a safe maximum */
+    if (length > GUAC_RDP_MAX_READ_BUFFER)
+        length = GUAC_RDP_MAX_READ_BUFFER;
+
+    /* Allocate buffer */
+    buffer = malloc(length);
+
+    /* Attempt read */
+    bytes_read = guac_rdp_fs_read((guac_rdp_fs*) device->data, file_id, offset,
+            buffer, length);
+
+    /* If error, return invalid parameter */
+    if (bytes_read < 0) {
+        output_stream = guac_rdpdr_new_io_completion(device, completion_id,
+                guac_rdp_fs_get_status(bytes_read), 4);
+        Stream_Write_UINT32(output_stream, 0); /* Length */
+    }
+
+    /* Otherwise, send bytes read */
+    else {
+        output_stream = guac_rdpdr_new_io_completion(device, completion_id,
+                STATUS_SUCCESS, 4+bytes_read);
+        Stream_Write_UINT32(output_stream, bytes_read);  /* Length */
+        Stream_Write(output_stream, buffer, bytes_read); /* ReadData */
+    }
+
+    svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+    free(buffer);
+
+}
+
+void guac_rdpdr_fs_process_write(guac_rdpdr_device* device,
+        wStream* input_stream, int file_id, int completion_id) {
+
+    UINT32 length;
+    UINT64 offset;
+    int bytes_written;
+
+    wStream* output_stream;
+
+    /* Read packet */
+    Stream_Read_UINT32(input_stream, length);
+    Stream_Read_UINT64(input_stream, offset);
+    Stream_Seek(input_stream, 20); /* Padding */
+
+    guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+            "%s: [file_id=%i] length=%i, offset=%" PRIu64,
+             __func__, file_id, length, (uint64_t) offset);
+
+    /* Attempt write */
+    bytes_written = guac_rdp_fs_write((guac_rdp_fs*) device->data, file_id,
+            offset, Stream_Pointer(input_stream), length);
+
+    /* If error, return invalid parameter */
+    if (bytes_written < 0) {
+        output_stream = guac_rdpdr_new_io_completion(device, completion_id,
+                guac_rdp_fs_get_status(bytes_written), 5);
+        Stream_Write_UINT32(output_stream, 0); /* Length */
+        Stream_Write_UINT8(output_stream, 0);  /* Padding */
+    }
+
+    /* Otherwise, send success */
+    else {
+        output_stream = guac_rdpdr_new_io_completion(device, completion_id,
+                STATUS_SUCCESS, 5);
+        Stream_Write_UINT32(output_stream, bytes_written); /* Length */
+        Stream_Write_UINT8(output_stream, 0);              /* Padding */
+    }
+
+    svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_close(guac_rdpdr_device* device,
+        wStream* input_stream, int file_id, int completion_id) {
+
+    wStream* output_stream;
+    guac_rdp_fs_file* file;
+
+    guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+            "%s: [file_id=%i]",
+            __func__, file_id);
+
+    /* Get file */
+    file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
+    if (file == NULL)
+        return;
+
+    /* If file was written to, and it's in the \Download folder, start stream */
+    if (file->bytes_written > 0 &&
+            strncmp(file->absolute_path, "\\Download\\", 10) == 0) {
+        guac_rdpdr_start_download(device, file->absolute_path);
+        guac_rdp_fs_delete((guac_rdp_fs*) device->data, file_id);
+    }
+
+    /* Close file */
+    guac_rdp_fs_close((guac_rdp_fs*) device->data, file_id);
+
+    output_stream = guac_rdpdr_new_io_completion(device, completion_id,
+            STATUS_SUCCESS, 4);
+    Stream_Write(output_stream, "\0\0\0\0", 4); /* Padding */
+
+    svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_volume_info(guac_rdpdr_device* device, wStream* input_stream,
+        int file_id, int completion_id) {
+
+    int fs_information_class;
+
+    Stream_Read_UINT32(input_stream, fs_information_class);
+
+    /* Dispatch to appropriate class-specific handler */
+    switch (fs_information_class) {
+
+        case FileFsVolumeInformation:
+            guac_rdpdr_fs_process_query_volume_info(device, input_stream,
+                    file_id, completion_id);
+            break;
+
+        case FileFsSizeInformation:
+            guac_rdpdr_fs_process_query_size_info(device, input_stream,
+                    file_id, completion_id);
+            break;
+
+        case FileFsDeviceInformation:
+            guac_rdpdr_fs_process_query_device_info(device, input_stream,
+                    file_id, completion_id);
+            break;
+
+        case FileFsAttributeInformation:
+            guac_rdpdr_fs_process_query_attribute_info(device, input_stream,
+                    file_id, completion_id);
+            break;
+
+        case FileFsFullSizeInformation:
+            guac_rdpdr_fs_process_query_full_size_info(device, input_stream,
+                    file_id, completion_id);
+            break;
+
+        default:
+            guac_client_log(device->rdpdr->client, GUAC_LOG_INFO,
+                    "Unknown volume information class: 0x%x", fs_information_class);
+    }
+
+}
+
+void guac_rdpdr_fs_process_file_info(guac_rdpdr_device* device, wStream* input_stream,
+        int file_id, int completion_id) {
+
+    int fs_information_class;
+
+    Stream_Read_UINT32(input_stream, fs_information_class);
+
+    /* Dispatch to appropriate class-specific handler */
+    switch (fs_information_class) {
+
+        case FileBasicInformation:
+            guac_rdpdr_fs_process_query_basic_info(device, input_stream,
+                    file_id, completion_id);
+            break;
+
+        case FileStandardInformation:
+            guac_rdpdr_fs_process_query_standard_info(device, input_stream,
+                    file_id, completion_id);
+            break;
+
+        case FileAttributeTagInformation:
+            guac_rdpdr_fs_process_query_attribute_tag_info(device, input_stream,
+                    file_id, completion_id);
+            break;
+
+        default:
+            guac_client_log(device->rdpdr->client, GUAC_LOG_INFO,
+                    "Unknown file information class: 0x%x", fs_information_class);
+    }
+
+}
+
+void guac_rdpdr_fs_process_set_volume_info(guac_rdpdr_device* device,
+        wStream* input_stream, int file_id, int completion_id) {
+
+    wStream* output_stream = guac_rdpdr_new_io_completion(device,
+            completion_id, STATUS_NOT_SUPPORTED, 0);
+
+    guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+            "%s: [file_id=%i] Set volume info not supported",
+            __func__, file_id);
+
+    svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_set_file_info(guac_rdpdr_device* device,
+        wStream* input_stream, int file_id, int completion_id) {
+
+    int fs_information_class;
+    int length;
+
+    Stream_Read_UINT32(input_stream, fs_information_class);
+    Stream_Read_UINT32(input_stream, length); /* Length */
+    Stream_Seek(input_stream, 24);            /* Padding */
+
+    /* Dispatch to appropriate class-specific handler */
+    switch (fs_information_class) {
+
+        case FileBasicInformation:
+            guac_rdpdr_fs_process_set_basic_info(device, input_stream,
+                    file_id, completion_id, length);
+            break;
+
+        case FileEndOfFileInformation:
+            guac_rdpdr_fs_process_set_end_of_file_info(device, input_stream,
+                    file_id, completion_id, length);
+            break;
+
+        case FileDispositionInformation:
+            guac_rdpdr_fs_process_set_disposition_info(device, input_stream,
+                    file_id, completion_id, length);
+            break;
+
+        case FileRenameInformation:
+            guac_rdpdr_fs_process_set_rename_info(device, input_stream,
+                    file_id, completion_id, length);
+            break;
+
+        case FileAllocationInformation:
+            guac_rdpdr_fs_process_set_allocation_info(device, input_stream,
+                    file_id, completion_id, length);
+            break;
+
+        default:
+            guac_client_log(device->rdpdr->client, GUAC_LOG_INFO,
+                    "Unknown file information class: 0x%x",
+                    fs_information_class);
+    }
+
+}
+
+void guac_rdpdr_fs_process_device_control(guac_rdpdr_device* device,
+        wStream* input_stream, int file_id, int completion_id) {
+
+    wStream* output_stream = guac_rdpdr_new_io_completion(device,
+            completion_id, STATUS_INVALID_PARAMETER, 4);
+
+    guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+            "%s: [file_id=%i] IGNORED",
+            __func__, file_id);
+
+    /* No content for now */
+    Stream_Write_UINT32(output_stream, 0);
+
+    svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_notify_change_directory(guac_rdpdr_device* device,
+        wStream* input_stream, int file_id, int completion_id) {
+
+    guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+            "%s: [file_id=%i] Not implemented",
+            __func__, file_id);
+
+}
+
+void guac_rdpdr_fs_process_query_directory(guac_rdpdr_device* device, wStream* input_stream,
+        int file_id, int completion_id) {
+
+    wStream* output_stream;
+
+    guac_rdp_fs_file* file;
+    int fs_information_class, initial_query;
+    int path_length;
+
+    const char* entry_name;
+
+    /* Get file */
+    file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
+    if (file == NULL)
+        return;
+
+    /* Read main header */
+    Stream_Read_UINT32(input_stream, fs_information_class);
+    Stream_Read_UINT8(input_stream,  initial_query);
+    Stream_Read_UINT32(input_stream, path_length);
+
+    /* If this is the first query, the path is included after padding */
+    if (initial_query) {
+
+        Stream_Seek(input_stream, 23);       /* Padding */
+
+        /* Convert path to UTF-8 */
+        guac_rdp_utf16_to_utf8(Stream_Pointer(input_stream), path_length/2 - 1,
+                file->dir_pattern, sizeof(file->dir_pattern));
+
+    }
+
+    guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+            "%s: [file_id=%i] initial_query=%i, dir_pattern=\"%s\"",
+             __func__, file_id, initial_query, file->dir_pattern);
+
+    /* Find first matching entry in directory */
+    while ((entry_name = guac_rdp_fs_read_dir((guac_rdp_fs*) device->data,
+                    file_id)) != NULL) {
+
+        /* Convert to absolute path */
+        char entry_path[GUAC_RDP_FS_MAX_PATH];
+        if (guac_rdp_fs_convert_path(file->absolute_path,
+                    entry_name, entry_path) == 0) {
+
+            int entry_file_id;
+
+            /* Pattern defined and match fails, continue with next file */
+            if (guac_rdp_fs_matches(entry_path, file->dir_pattern))
+                continue;
+
+            /* Open directory entry */
+            entry_file_id = guac_rdp_fs_open((guac_rdp_fs*) device->data,
+                    entry_path, ACCESS_FILE_READ_DATA, 0, DISP_FILE_OPEN, 0);
+
+            if (entry_file_id >= 0) {
+
+                /* Dispatch to appropriate class-specific handler */
+                switch (fs_information_class) {
+
+                    case FileDirectoryInformation:
+                        guac_rdpdr_fs_process_query_directory_info(device,
+                                entry_name, entry_file_id, completion_id);
+                        break;
+
+                    case FileFullDirectoryInformation:
+                        guac_rdpdr_fs_process_query_full_directory_info(device,
+                                entry_name, entry_file_id, completion_id);
+                        break;
+
+                    case FileBothDirectoryInformation:
+                        guac_rdpdr_fs_process_query_both_directory_info(device,
+                                entry_name, entry_file_id, completion_id);
+                        break;
+
+                    case FileNamesInformation:
+                        guac_rdpdr_fs_process_query_names_info(device,
+                                entry_name, entry_file_id, completion_id);
+                        break;
+
+                    default:
+                        guac_client_log(device->rdpdr->client, GUAC_LOG_INFO,
+                                "Unknown dir information class: 0x%x",
+                                fs_information_class);
+                }
+
+                guac_rdp_fs_close((guac_rdp_fs*) device->data, entry_file_id);
+                return;
+
+            } /* end if file exists */
+        } /* end if path valid */
+    } /* end if entry exists */
+
+    /*
+     * Handle errors as a lack of files.
+     */
+
+    output_stream = guac_rdpdr_new_io_completion(device, completion_id,
+            STATUS_NO_MORE_FILES, 5);
+
+    Stream_Write_UINT32(output_stream, 0); /* Length */
+    Stream_Write_UINT8(output_stream, 0);  /* Padding */
+
+    svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_lock_control(guac_rdpdr_device* device, wStream* input_stream,
+        int file_id, int completion_id) {
+
+    wStream* output_stream = guac_rdpdr_new_io_completion(device,
+            completion_id, STATUS_NOT_SUPPORTED, 5);
+
+    guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+            "%s: [file_id=%i] Lock not supported",
+            __func__, file_id);
+
+    Stream_Zero(output_stream, 5); /* Padding */
+
+    svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+
+}
+
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages.h b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages.h
new file mode 100644
index 0000000..592e4ee
--- /dev/null
+++ b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef __GUAC_RDPDR_FS_MESSAGES_H
+#define __GUAC_RDPDR_FS_MESSAGES_H
+
+/**
+ * Handlers for core drive I/O requests. Requests handled here may be simple
+ * messages handled directly, or more complex multi-type messages handled
+ * elsewhere.
+ *
+ * @file rdpdr_fs_messages.h
+ */
+
+#include "config.h"
+
+#include "rdpdr_service.h"
+
+#ifdef ENABLE_WINPR
+#include <winpr/stream.h>
+#else
+#include "compat/winpr-stream.h"
+#endif
+
+/**
+ * Handles a Server Create Drive Request. Despite its name, this request opens
+ * a file.
+ */
+void guac_rdpdr_fs_process_create(guac_rdpdr_device* device,
+        wStream* input_stream, int completion_id);
+
+/**
+ * Handles a Server Close Drive Reqiest. This request closes an open file.
+ */
+void guac_rdpdr_fs_process_close(guac_rdpdr_device* device,
+        wStream* input_stream, int file_id, int completion_id);
+
+/**
+ * Handles a Server Drive Read Request. This request reads from a file.
+ */
+void guac_rdpdr_fs_process_read(guac_rdpdr_device* device,
+        wStream* input_stream, int file_id, int completion_id);
+
+/**
+ * Handles a Server Drive Write Request. This request writes to a file.
+ */
+void guac_rdpdr_fs_process_write(guac_rdpdr_device* device,
+        wStream* input_stream, int file_id, int completion_id);
+
+/**
+ * Handles a Server Drive Control Request. This request handles one of any
+ * number of Windows FSCTL_* control functions.
+ */
+void guac_rdpdr_fs_process_device_control(guac_rdpdr_device* device, wStream* input_stream,
+        int file_id, int completion_id);
+
+/**
+ * Handles a Server Drive Query Volume Information Request. This request
+ * queries information about the redirected volume (drive). This request
+ * has several query types which have their own handlers defined in a
+ * separate file.
+ */
+void guac_rdpdr_fs_process_volume_info(guac_rdpdr_device* device, wStream* input_stream,
+        int file_id, int completion_id);
+
+/**
+ * Handles a Server Drive Set Volume Information Request. Currently, this
+ * RDPDR implementation does not support setting of volume information.
+ */
+void guac_rdpdr_fs_process_set_volume_info(guac_rdpdr_device* device, wStream* input_stream,
+        int file_id, int completion_id);
+
+/**
+ * Handles a Server Drive Query Information Request. This request queries
+ * information about a specific file. This request has several query types
+ * which have their own handlers defined in a separate file.
+ */
+void guac_rdpdr_fs_process_file_info(guac_rdpdr_device* device, wStream* input_stream,
+        int file_id, int completion_id);
+
+/**
+ * Handles a Server Drive Set Information Request. This request sets
+ * information about a specific file. Currently, this RDPDR implementation does
+ * not support setting of file information.
+ */
+void guac_rdpdr_fs_process_set_file_info(guac_rdpdr_device* device, wStream* input_stream,
+        int file_id, int completion_id);
+
+/**
+ * Handles a Server Drive Query Directory Request. This request queries
+ * information about a specific directory. This request has several query types
+ * which have their own handlers defined in a separate file.
+ */
+void guac_rdpdr_fs_process_query_directory(guac_rdpdr_device* device, wStream* input_stream,
+        int file_id, int completion_id);
+
+/**
+ * Handles a Server Drive NotifyChange Directory Request. This request requests
+ * directory change notification.
+ */
+void guac_rdpdr_fs_process_notify_change_directory(guac_rdpdr_device* device,
+        wStream* input_stream, int file_id, int completion_id);
+
+/**
+ * Handles a Server Drive Lock Control Request. This request locks or unlocks
+ * portions of a file.
+ */
+void guac_rdpdr_fs_process_lock_control(guac_rdpdr_device* device, wStream* input_stream,
+        int file_id, int completion_id);
+
+#endif
+
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_dir_info.c b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_dir_info.c
new file mode 100644
index 0000000..1392f42
--- /dev/null
+++ b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_dir_info.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "rdpdr_service.h"
+#include "rdp_fs.h"
+#include "rdp_status.h"
+#include "unicode.h"
+
+#include <freerdp/utils/svc_plugin.h>
+#include <guacamole/unicode.h>
+
+#ifdef ENABLE_WINPR
+#include <winpr/stream.h>
+#else
+#include "compat/winpr-stream.h"
+#endif
+
+#include <stddef.h>
+
+void guac_rdpdr_fs_process_query_directory_info(guac_rdpdr_device* device,
+        const char* entry_name, int file_id, int completion_id) {
+
+    guac_rdp_fs_file* file;
+
+    wStream* output_stream;
+    int length = guac_utf8_strlen(entry_name);
+    int utf16_length = length*2;
+
+    unsigned char utf16_entry_name[256];
+    guac_rdp_utf8_to_utf16((const unsigned char*) entry_name, length,
+            (char*) utf16_entry_name, sizeof(utf16_entry_name));
+
+    /* Get file */
+    file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
+    if (file == NULL)
+        return;
+
+    guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+            "%s: [file_id=%i (entry_name=\"%s\")]",
+            __func__, file_id, entry_name);
+
+    output_stream = guac_rdpdr_new_io_completion(device, completion_id,
+            STATUS_SUCCESS, 4 + 64 + utf16_length + 2);
+
+    Stream_Write_UINT32(output_stream,
+            64 + utf16_length + 2); /* Length */
+
+    Stream_Write_UINT32(output_stream, 0); /* NextEntryOffset */
+    Stream_Write_UINT32(output_stream, 0); /* FileIndex */
+    Stream_Write_UINT64(output_stream, file->ctime); /* CreationTime */
+    Stream_Write_UINT64(output_stream, file->atime); /* LastAccessTime */
+    Stream_Write_UINT64(output_stream, file->mtime); /* LastWriteTime */
+    Stream_Write_UINT64(output_stream, file->mtime); /* ChangeTime */
+    Stream_Write_UINT64(output_stream, file->size);  /* EndOfFile */
+    Stream_Write_UINT64(output_stream, file->size);  /* AllocationSize */
+    Stream_Write_UINT32(output_stream, file->attributes);   /* FileAttributes */
+    Stream_Write_UINT32(output_stream, utf16_length+2); /* FileNameLength*/
+
+    Stream_Write(output_stream, utf16_entry_name, utf16_length); /* FileName */
+    Stream_Write(output_stream, "\0\0", 2);
+
+    svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_query_full_directory_info(guac_rdpdr_device* device,
+        const char* entry_name, int file_id, int completion_id) {
+
+    guac_rdp_fs_file* file;
+
+    wStream* output_stream;
+    int length = guac_utf8_strlen(entry_name);
+    int utf16_length = length*2;
+
+    unsigned char utf16_entry_name[256];
+    guac_rdp_utf8_to_utf16((const unsigned char*) entry_name, length,
+            (char*) utf16_entry_name, sizeof(utf16_entry_name));
+
+    /* Get file */
+    file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
+    if (file == NULL)
+        return;
+
+    guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+            "%s: [file_id=%i (entry_name=\"%s\")]",
+            __func__, file_id, entry_name);
+
+    output_stream = guac_rdpdr_new_io_completion(device, completion_id,
+            STATUS_SUCCESS, 4 + 68 + utf16_length + 2);
+
+    Stream_Write_UINT32(output_stream,
+            68 + utf16_length + 2); /* Length */
+
+    Stream_Write_UINT32(output_stream, 0); /* NextEntryOffset */
+    Stream_Write_UINT32(output_stream, 0); /* FileIndex */
+    Stream_Write_UINT64(output_stream, file->ctime); /* CreationTime */
+    Stream_Write_UINT64(output_stream, file->atime); /* LastAccessTime */
+    Stream_Write_UINT64(output_stream, file->mtime); /* LastWriteTime */
+    Stream_Write_UINT64(output_stream, file->mtime); /* ChangeTime */
+    Stream_Write_UINT64(output_stream, file->size);  /* EndOfFile */
+    Stream_Write_UINT64(output_stream, file->size);  /* AllocationSize */
+    Stream_Write_UINT32(output_stream, file->attributes);   /* FileAttributes */
+    Stream_Write_UINT32(output_stream, utf16_length+2); /* FileNameLength*/
+    Stream_Write_UINT32(output_stream, 0); /* EaSize */
+
+    Stream_Write(output_stream, utf16_entry_name, utf16_length); /* FileName */
+    Stream_Write(output_stream, "\0\0", 2);
+
+    svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_query_both_directory_info(guac_rdpdr_device* device,
+        const char* entry_name, int file_id, int completion_id) {
+
+    guac_rdp_fs_file* file;
+
+    wStream* output_stream;
+    int length = guac_utf8_strlen(entry_name);
+    int utf16_length = length*2;
+
+    unsigned char utf16_entry_name[256];
+    guac_rdp_utf8_to_utf16((const unsigned char*) entry_name, length,
+            (char*) utf16_entry_name, sizeof(utf16_entry_name));
+
+    /* Get file */
+    file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
+    if (file == NULL)
+        return;
+
+    guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+            "%s: [file_id=%i (entry_name=\"%s\")]",
+            __func__, file_id, entry_name);
+
+    output_stream = guac_rdpdr_new_io_completion(device, completion_id,
+            STATUS_SUCCESS, 4 + 69 + 24 + utf16_length + 2);
+
+    Stream_Write_UINT32(output_stream,
+            69 + 24 + utf16_length + 2); /* Length */
+
+    Stream_Write_UINT32(output_stream, 0); /* NextEntryOffset */
+    Stream_Write_UINT32(output_stream, 0); /* FileIndex */
+    Stream_Write_UINT64(output_stream, file->ctime); /* CreationTime */
+    Stream_Write_UINT64(output_stream, file->atime); /* LastAccessTime */
+    Stream_Write_UINT64(output_stream, file->mtime); /* LastWriteTime */
+    Stream_Write_UINT64(output_stream, file->mtime); /* ChangeTime */
+    Stream_Write_UINT64(output_stream, file->size);  /* EndOfFile */
+    Stream_Write_UINT64(output_stream, file->size);  /* AllocationSize */
+    Stream_Write_UINT32(output_stream, file->attributes);   /* FileAttributes */
+    Stream_Write_UINT32(output_stream, utf16_length+2); /* FileNameLength*/
+    Stream_Write_UINT32(output_stream, 0); /* EaSize */
+    Stream_Write_UINT8(output_stream,  0); /* ShortNameLength */
+
+    /* Apparently, the reserved byte here must be skipped ... */
+
+    Stream_Zero(output_stream, 24); /* FileName */
+    Stream_Write(output_stream, utf16_entry_name, utf16_length); /* FileName */
+    Stream_Write(output_stream, "\0\0", 2);
+
+    svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_query_names_info(guac_rdpdr_device* device,
+        const char* entry_name, int file_id, int completion_id) {
+
+    guac_rdp_fs_file* file;
+
+    wStream* output_stream;
+    int length = guac_utf8_strlen(entry_name);
+    int utf16_length = length*2;
+
+    unsigned char utf16_entry_name[256];
+    guac_rdp_utf8_to_utf16((const unsigned char*) entry_name, length,
+            (char*) utf16_entry_name, sizeof(utf16_entry_name));
+
+    /* Get file */
+    file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
+    if (file == NULL)
+        return;
+
+    guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+            "%s: [file_id=%i (entry_name=\"%s\")]",
+            __func__, file_id, entry_name);
+
+    output_stream = guac_rdpdr_new_io_completion(device, completion_id,
+            STATUS_SUCCESS, 4 + 12 + utf16_length + 2);
+
+    Stream_Write_UINT32(output_stream,
+            12 + utf16_length + 2); /* Length */
+
+    Stream_Write_UINT32(output_stream, 0); /* NextEntryOffset */
+    Stream_Write_UINT32(output_stream, 0); /* FileIndex */
+    Stream_Write_UINT32(output_stream, utf16_length+2); /* FileNameLength*/
+    Stream_Write(output_stream, utf16_entry_name, utf16_length); /* FileName */
+    Stream_Write(output_stream, "\0\0", 2);
+
+    svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+
+}
+
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_dir_info.h b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_dir_info.h
new file mode 100644
index 0000000..535771f
--- /dev/null
+++ b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_dir_info.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef __GUAC_RDPDR_FS_MESSAGES_DIR_INFO_H
+#define __GUAC_RDPDR_FS_MESSAGES_DIR_INFO_H
+
+/**
+ * Handlers for directory queries received over the RDPDR channel via the
+ * IRP_MJ_DIRECTORY_CONTROL major function and the IRP_MN_QUERY_DIRECTORY minor
+ * function.
+ *
+ * @file rdpdr_fs_messages_dir_info.h
+ */
+
+#include "config.h"
+
+#include "rdpdr_service.h"
+
+#ifdef ENABLE_WINPR
+#include <winpr/stream.h>
+#else
+#include "compat/winpr-stream.h"
+#endif
+
+/**
+ * Processes a query request for FileDirectoryInformation. From the
+ * documentation this is "defined as the file's name, time stamp, and size, or its
+ * attributes."
+ */
+void guac_rdpdr_fs_process_query_directory_info(guac_rdpdr_device* device,
+        const char* entry_name, int file_id, int completion_id);
+
+/**
+ * Processes a query request for FileFullDirectoryInformation. From the
+ * documentation, this is "defined as all the basic information, plus extended
+ * attribute size."
+ */
+void guac_rdpdr_fs_process_query_full_directory_info(guac_rdpdr_device* device,
+        const char* entry_name, int file_id, int completion_id);
+
+/**
+ * Processes a query request for FileBothDirectoryInformation. From the
+ * documentation, this absurdly-named request is "basic information plus
+ * extended attribute size and short name about a file or directory."
+ */
+void guac_rdpdr_fs_process_query_both_directory_info(guac_rdpdr_device* device,
+        const char* entry_name, int file_id, int completion_id);
+
+/**
+ * Processes a query request for FileNamesInformation. From the documentation,
+ * this is "detailed information on the names of files in a directory."
+ */
+void guac_rdpdr_fs_process_query_names_info(guac_rdpdr_device* device,
+        const char* entry_name, int file_id, int completion_id);
+
+#endif
+
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_file_info.c b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_file_info.c
new file mode 100644
index 0000000..a0a7a73
--- /dev/null
+++ b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_file_info.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "rdpdr_service.h"
+#include "rdp_fs.h"
+#include "rdp_status.h"
+#include "unicode.h"
+
+#include <freerdp/utils/svc_plugin.h>
+
+#ifdef ENABLE_WINPR
+#include <winpr/stream.h>
+#include <winpr/wtypes.h>
+#else
+#include "compat/winpr-stream.h"
+#include "compat/winpr-wtypes.h"
+#endif
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <string.h>
+
+void guac_rdpdr_fs_process_query_basic_info(guac_rdpdr_device* device, wStream* input_stream,
+        int file_id, int completion_id) {
+
+    wStream* output_stream;
+    guac_rdp_fs_file* file;
+
+    /* Get file */
+    file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
+    if (file == NULL)
+        return;
+
+    guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+            "%s: [file_id=%i]",
+            __func__, file_id);
+
+    output_stream = guac_rdpdr_new_io_completion(device, completion_id,
+            STATUS_SUCCESS, 40);
+
+    Stream_Write_UINT32(output_stream, 36);
+    Stream_Write_UINT64(output_stream, file->ctime);      /* CreationTime   */
+    Stream_Write_UINT64(output_stream, file->atime);      /* LastAccessTime */
+    Stream_Write_UINT64(output_stream, file->mtime);      /* LastWriteTime  */
+    Stream_Write_UINT64(output_stream, file->mtime);      /* ChangeTime     */
+    Stream_Write_UINT32(output_stream, file->attributes); /* FileAttributes */
+
+    /* Reserved field must not be sent */
+
+    svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_query_standard_info(guac_rdpdr_device* device, wStream* input_stream,
+        int file_id, int completion_id) {
+
+    wStream* output_stream;
+    guac_rdp_fs_file* file;
+    BOOL is_directory = FALSE;
+
+    /* Get file */
+    file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
+    if (file == NULL)
+        return;
+
+    guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+            "%s: [file_id=%i]",
+            __func__, file_id);
+
+    if (file->attributes & FILE_ATTRIBUTE_DIRECTORY)
+        is_directory = TRUE;
+
+    output_stream = guac_rdpdr_new_io_completion(device, completion_id,
+            STATUS_SUCCESS, 26);
+
+    Stream_Write_UINT32(output_stream, 22);
+    Stream_Write_UINT64(output_stream, file->size);   /* AllocationSize */
+    Stream_Write_UINT64(output_stream, file->size);   /* EndOfFile      */
+    Stream_Write_UINT32(output_stream, 1);            /* NumberOfLinks  */
+    Stream_Write_UINT8(output_stream,  0);            /* DeletePending  */
+    Stream_Write_UINT8(output_stream,  is_directory); /* Directory      */
+
+    /* Reserved field must not be sent */
+
+    svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_query_attribute_tag_info(guac_rdpdr_device* device,
+        wStream* input_stream, int file_id, int completion_id) {
+
+    wStream* output_stream;
+    guac_rdp_fs_file* file;
+
+    /* Get file */
+    file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
+    if (file == NULL)
+        return;
+
+    guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+            "%s: [file_id=%i]",
+            __func__, file_id);
+
+    output_stream = guac_rdpdr_new_io_completion(device, completion_id,
+            STATUS_SUCCESS, 12);
+
+    Stream_Write_UINT32(output_stream, 8);
+    Stream_Write_UINT32(output_stream, file->attributes); /* FileAttributes */
+    Stream_Write_UINT32(output_stream, 0);                /* ReparseTag */
+
+    /* Reserved field must not be sent */
+
+    svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_set_rename_info(guac_rdpdr_device* device,
+        wStream* input_stream, int file_id, int completion_id, int length) {
+
+    int result;
+    int filename_length;
+    wStream* output_stream;
+    char destination_path[GUAC_RDP_FS_MAX_PATH];
+
+    /* Read structure */
+    Stream_Seek_UINT8(input_stream); /* ReplaceIfExists */
+    Stream_Seek_UINT8(input_stream); /* RootDirectory */
+    Stream_Read_UINT32(input_stream, filename_length); /* FileNameLength */
+
+    /* Convert name to UTF-8 */
+    guac_rdp_utf16_to_utf8(Stream_Pointer(input_stream), filename_length/2,
+            destination_path, sizeof(destination_path));
+
+    guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+            "%s: [file_id=%i] destination_path=\"%s\"",
+            __func__, file_id, destination_path);
+
+    /* If file moving to \Download folder, start stream, do not move */
+    if (strncmp(destination_path, "\\Download\\", 10) == 0) {
+
+        guac_rdp_fs_file* file;
+
+        /* Get file */
+        file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id);
+        if (file == NULL)
+            return;
+
+        /* Initiate download, pretend move succeeded */
+        guac_rdpdr_start_download(device, file->absolute_path);
+        output_stream = guac_rdpdr_new_io_completion(device,
+                completion_id, STATUS_SUCCESS, 4);
+
+    }
+
+    /* Otherwise, rename as requested */
+    else {
+
+        result = guac_rdp_fs_rename((guac_rdp_fs*) device->data, file_id,
+                destination_path);
+        if (result < 0)
+            output_stream = guac_rdpdr_new_io_completion(device,
+                    completion_id, guac_rdp_fs_get_status(result), 4);
+        else
+            output_stream = guac_rdpdr_new_io_completion(device,
+                    completion_id, STATUS_SUCCESS, 4);
+
+    }
+
+    Stream_Write_UINT32(output_stream, length);
+    svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_set_allocation_info(guac_rdpdr_device* device,
+        wStream* input_stream, int file_id, int completion_id, int length) {
+
+    int result;
+    UINT64 size;
+    wStream* output_stream;
+
+    /* Read new size */
+    Stream_Read_UINT64(input_stream, size); /* AllocationSize */
+
+    guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+            "%s: [file_id=%i] size=%" PRIu64,
+            __func__, file_id, (uint64_t) size);
+
+    /* Truncate file */
+    result = guac_rdp_fs_truncate((guac_rdp_fs*) device->data, file_id, size);
+    if (result < 0)
+        output_stream = guac_rdpdr_new_io_completion(device,
+                completion_id, guac_rdp_fs_get_status(result), 4);
+    else
+        output_stream = guac_rdpdr_new_io_completion(device,
+                completion_id, STATUS_SUCCESS, 4);
+
+    Stream_Write_UINT32(output_stream, length);
+    svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_set_disposition_info(guac_rdpdr_device* device,
+        wStream* input_stream, int file_id, int completion_id, int length) {
+
+    wStream* output_stream;
+
+    /* Delete file */
+    int result = guac_rdp_fs_delete((guac_rdp_fs*) device->data, file_id);
+    if (result < 0)
+        output_stream = guac_rdpdr_new_io_completion(device,
+                completion_id, guac_rdp_fs_get_status(result), 4);
+    else
+        output_stream = guac_rdpdr_new_io_completion(device,
+                completion_id, STATUS_SUCCESS, 4);
+
+    guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+            "%s: [file_id=%i]",
+            __func__, file_id);
+
+    Stream_Write_UINT32(output_stream, length);
+
+    svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_set_end_of_file_info(guac_rdpdr_device* device,
+        wStream* input_stream, int file_id, int completion_id, int length) {
+
+    int result;
+    UINT64 size;
+    wStream* output_stream;
+
+    /* Read new size */
+    Stream_Read_UINT64(input_stream, size); /* AllocationSize */
+
+    guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+            "%s: [file_id=%i] size=%" PRIu64,
+            __func__, file_id, (uint64_t) size);
+
+    /* Truncate file */
+    result = guac_rdp_fs_truncate((guac_rdp_fs*) device->data, file_id, size);
+    if (result < 0)
+        output_stream = guac_rdpdr_new_io_completion(device,
+                completion_id, guac_rdp_fs_get_status(result), 4);
+    else
+        output_stream = guac_rdpdr_new_io_completion(device,
+                completion_id, STATUS_SUCCESS, 4);
+
+    Stream_Write_UINT32(output_stream, length);
+    svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_set_basic_info(guac_rdpdr_device* device,
+        wStream* input_stream, int file_id, int completion_id, int length) {
+
+    wStream* output_stream = guac_rdpdr_new_io_completion(device,
+            completion_id, STATUS_SUCCESS, 4);
+
+    /* Currently do nothing, just respond */
+    Stream_Write_UINT32(output_stream, length);
+
+    guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+            "%s: [file_id=%i] IGNORED",
+            __func__, file_id);
+
+    svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+
+}
+
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_file_info.h b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_file_info.h
new file mode 100644
index 0000000..f37c353
--- /dev/null
+++ b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_file_info.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef __GUAC_RDPDR_FS_MESSAGES_FILE_INFO_H
+#define __GUAC_RDPDR_FS_MESSAGES_FILE_INFO_H
+
+/**
+ * Handlers for file queries received over the RDPDR channel via the
+ * IRP_MJ_QUERY_INFORMATION major function.
+ *
+ * @file rdpdr_fs_messages_file_info.h
+ */
+
+#include "config.h"
+
+#include "rdpdr_service.h"
+
+#ifdef ENABLE_WINPR
+#include <winpr/stream.h>
+#else
+#include "compat/winpr-stream.h"
+#endif
+
+/**
+ * Processes a query for FileBasicInformation. From the documentation, this is
+ * "used to query a file for the times of creation, last access, last write,
+ * and change, in addition to file attribute information."
+ */
+void guac_rdpdr_fs_process_query_basic_info(guac_rdpdr_device* device, wStream* input_stream,
+        int file_id, int completion_id);
+
+/**
+ * Processes a query for FileStandardInformation. From the documentation, this
+ * is "used to query for file information such as allocation size, end-of-file
+ * position, and number of links."
+ */
+void guac_rdpdr_fs_process_query_standard_info(guac_rdpdr_device* device, wStream* input_stream,
+        int file_id, int completion_id);
+
+/**
+ * Processes a query for FileAttributeTagInformation. From the documentation
+ * this is "used to query for file attribute and reparse tag information."
+ */
+void guac_rdpdr_fs_process_query_attribute_tag_info(guac_rdpdr_device* device,
+        wStream* input_stream, int file_id, int completion_id);
+
+/**
+ * Process a set operation for FileRenameInformation. From the documentation,
+ * this operation is used to rename a file.
+ */
+void guac_rdpdr_fs_process_set_rename_info(guac_rdpdr_device* device,
+        wStream* input_stream, int file_id, int completion_id, int length);
+
+/**
+ * Process a set operation for FileAllocationInformation. From the
+ * documentation, this operation is used to set a file's allocation size.
+ */
+void guac_rdpdr_fs_process_set_allocation_info(guac_rdpdr_device* device,
+        wStream* input_stream, int file_id, int completion_id, int length);
+
+/**
+ * Process a set operation for FileDispositionInformation. From the
+ * documentation, this operation is used to mark a file for deletion.
+ */
+void guac_rdpdr_fs_process_set_disposition_info(guac_rdpdr_device* device,
+        wStream* input_stream, int file_id, int completion_id, int length);
+
+/**
+ * Process a set operation for FileEndOfFileInformation. From the
+ * documentation, this operation is used "to set end-of-file information for
+ * a file."
+ */
+void guac_rdpdr_fs_process_set_end_of_file_info(guac_rdpdr_device* device,
+        wStream* input_stream, int file_id, int completion_id, int length);
+
+/**
+ * Process a set operation for FileBasicInformation. From the documentation,
+ * this is "used to set file information such as the times of creation, last
+ * access, last write, and change, in addition to file attributes."
+ */
+void guac_rdpdr_fs_process_set_basic_info(guac_rdpdr_device* device,
+        wStream* input_stream, int file_id, int completion_id, int length);
+
+#endif
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_vol_info.c b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_vol_info.c
new file mode 100644
index 0000000..89a939a
--- /dev/null
+++ b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_vol_info.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "rdpdr_messages.h"
+#include "rdpdr_service.h"
+#include "rdp_fs.h"
+#include "rdp_status.h"
+
+#include <freerdp/utils/svc_plugin.h>
+
+#ifdef ENABLE_WINPR
+#include <winpr/stream.h>
+#include <winpr/wtypes.h>
+#else
+#include "compat/winpr-stream.h"
+#include "compat/winpr-wtypes.h"
+#endif
+
+void guac_rdpdr_fs_process_query_volume_info(guac_rdpdr_device* device,
+        wStream* input_stream, int file_id, int completion_id) {
+
+    wStream* output_stream = guac_rdpdr_new_io_completion(device,
+            completion_id, STATUS_SUCCESS, 21 + GUAC_FILESYSTEM_LABEL_LENGTH);
+
+    guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+            "%s: [file_id=%i]",
+            __func__, file_id);
+
+    Stream_Write_UINT32(output_stream, 17 + GUAC_FILESYSTEM_LABEL_LENGTH);
+    Stream_Write_UINT64(output_stream, 0); /* VolumeCreationTime */
+    Stream_Write_UINT32(output_stream, 0); /* VolumeSerialNumber */
+    Stream_Write_UINT32(output_stream, GUAC_FILESYSTEM_LABEL_LENGTH);
+    Stream_Write_UINT8(output_stream, FALSE); /* SupportsObjects */
+    /* Reserved field must not be sent */
+    Stream_Write(output_stream, GUAC_FILESYSTEM_LABEL, GUAC_FILESYSTEM_LABEL_LENGTH);
+
+    svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_query_size_info(guac_rdpdr_device* device, wStream* input_stream,
+        int file_id, int completion_id) {
+
+    guac_rdp_fs_info info = {0};
+    guac_rdp_fs_get_info((guac_rdp_fs*) device->data, &info);
+
+    wStream* output_stream = guac_rdpdr_new_io_completion(device,
+            completion_id, STATUS_SUCCESS, 28);
+
+    guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+            "%s: [file_id=%i]",
+            __func__, file_id);
+
+    Stream_Write_UINT32(output_stream, 24);
+    Stream_Write_UINT64(output_stream, info.blocks_total);     /* TotalAllocationUnits */
+    Stream_Write_UINT64(output_stream, info.blocks_available); /* AvailableAllocationUnits */
+    Stream_Write_UINT32(output_stream, 1);                     /* SectorsPerAllocationUnit */
+    Stream_Write_UINT32(output_stream, info.block_size);       /* BytesPerSector */
+
+    svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_query_device_info(guac_rdpdr_device* device, wStream* input_stream,
+        int file_id, int completion_id) {
+
+    wStream* output_stream = guac_rdpdr_new_io_completion(device,
+            completion_id, STATUS_SUCCESS, 12);
+
+    guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+            "%s: [file_id=%i]",
+            __func__, file_id);
+
+    Stream_Write_UINT32(output_stream, 8);
+    Stream_Write_UINT32(output_stream, FILE_DEVICE_DISK); /* DeviceType */
+    Stream_Write_UINT32(output_stream, 0); /* Characteristics */
+
+    svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_query_attribute_info(guac_rdpdr_device* device, wStream* input_stream,
+        int file_id, int completion_id) {
+
+    wStream* output_stream = guac_rdpdr_new_io_completion(device,
+            completion_id, STATUS_SUCCESS, 16 + GUAC_FILESYSTEM_NAME_LENGTH);
+
+    guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+            "%s: [file_id=%i]",
+            __func__, file_id);
+
+    Stream_Write_UINT32(output_stream, 12 + GUAC_FILESYSTEM_NAME_LENGTH);
+    Stream_Write_UINT32(output_stream,
+              FILE_UNICODE_ON_DISK
+            | FILE_CASE_SENSITIVE_SEARCH
+            | FILE_CASE_PRESERVED_NAMES); /* FileSystemAttributes */
+    Stream_Write_UINT32(output_stream, GUAC_RDP_FS_MAX_PATH ); /* MaximumComponentNameLength */
+    Stream_Write_UINT32(output_stream, GUAC_FILESYSTEM_NAME_LENGTH);
+    Stream_Write(output_stream, GUAC_FILESYSTEM_NAME,
+            GUAC_FILESYSTEM_NAME_LENGTH);
+
+    svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+
+}
+
+void guac_rdpdr_fs_process_query_full_size_info(guac_rdpdr_device* device, wStream* input_stream,
+        int file_id, int completion_id) {
+
+    guac_rdp_fs_info info = {0};
+    guac_rdp_fs_get_info((guac_rdp_fs*) device->data, &info);
+
+    wStream* output_stream = guac_rdpdr_new_io_completion(device,
+            completion_id, STATUS_SUCCESS, 36);
+
+    guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+            "%s: [file_id=%i]",
+            __func__, file_id);
+
+    Stream_Write_UINT32(output_stream, 32);
+    Stream_Write_UINT64(output_stream, info.blocks_total);     /* TotalAllocationUnits */
+    Stream_Write_UINT64(output_stream, info.blocks_available); /* CallerAvailableAllocationUnits */
+    Stream_Write_UINT64(output_stream, info.blocks_available); /* ActualAvailableAllocationUnits */
+    Stream_Write_UINT32(output_stream, 1);                     /* SectorsPerAllocationUnit */
+    Stream_Write_UINT32(output_stream, info.block_size);       /* BytesPerSector */
+
+    svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
+
+}
+
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_vol_info.h b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_vol_info.h
new file mode 100644
index 0000000..3baedc7
--- /dev/null
+++ b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_messages_vol_info.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef __GUAC_RDPDR_FS_MESSAGES_VOL_INFO_H
+#define __GUAC_RDPDR_FS_MESSAGES_VOL_INFO_H
+
+/**
+ * Handlers for directory queries received over the RDPDR channel via the
+ * IRP_MJ_DIRECTORY_CONTROL major function and the IRP_MN_QUERY_DIRECTORY minor
+ * function.
+ *
+ * @file rdpdr_fs_messages_vol_info.h
+ */
+
+#include "config.h"
+
+#include "rdpdr_service.h"
+
+#ifdef ENABLE_WINPR
+#include <winpr/stream.h>
+#else
+#include "compat/winpr-stream.h"
+#endif
+
+/**
+ * Processes a query request for FileFsVolumeInformation. According to the
+ * documentation, this is "used to query information for a volume on which a
+ * file system is mounted."
+ */
+void guac_rdpdr_fs_process_query_volume_info(guac_rdpdr_device* device, wStream* input_stream,
+        int file_id, int completion_id);
+
+/**
+ * Processes a query request for FileFsSizeInformation.
+ */
+void guac_rdpdr_fs_process_query_size_info(guac_rdpdr_device* device, wStream* input_stream,
+        int file_id, int completion_id);
+
+/**
+ * Processes a query request for FileFsAttributeInformation.
+ */
+void guac_rdpdr_fs_process_query_attribute_info(guac_rdpdr_device* device, wStream* input_stream,
+        int file_id, int completion_id);
+
+/**
+ * Processes a query request for FileFsFullSizeInformation.
+ */
+void guac_rdpdr_fs_process_query_full_size_info(guac_rdpdr_device* device, wStream* input_stream,
+        int file_id, int completion_id);
+
+/**
+ * Processes a query request for FileFsDeviceInformation.
+ */
+void guac_rdpdr_fs_process_query_device_info(guac_rdpdr_device* device, wStream* input_stream,
+        int file_id, int completion_id);
+
+#endif
+
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_service.c b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_service.c
new file mode 100644
index 0000000..5ba08c0
--- /dev/null
+++ b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_service.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#include "config.h"
+
+#include "client.h"
+#include "rdpdr_fs_messages.h"
+#include "rdpdr_messages.h"
+#include "rdpdr_service.h"
+
+#include <freerdp/utils/svc_plugin.h>
+#include <guacamole/client.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+
+#ifdef ENABLE_WINPR
+#include <winpr/stream.h>
+#else
+#include "compat/winpr-stream.h"
+#endif
+
+static void guac_rdpdr_device_fs_announce_handler(guac_rdpdr_device* device,
+        wStream* output_stream, int device_id) {
+
+    /* Filesystem header */
+    guac_client_log(device->rdpdr->client, GUAC_LOG_INFO, "Sending filesystem");
+    Stream_Write_UINT32(output_stream, RDPDR_DTYP_FILESYSTEM);
+    Stream_Write_UINT32(output_stream, device_id);
+    Stream_Write(output_stream, "GUAC\0\0\0\0", 8); /* DOS name */
+
+    /* Filesystem data */
+    Stream_Write_UINT32(output_stream, GUAC_FILESYSTEM_NAME_LENGTH);
+    Stream_Write(output_stream, GUAC_FILESYSTEM_NAME, GUAC_FILESYSTEM_NAME_LENGTH);
+
+}
+
+static void guac_rdpdr_device_fs_iorequest_handler(guac_rdpdr_device* device,
+        wStream* input_stream, int file_id, int completion_id, int major_func, int minor_func) {
+
+    switch (major_func) {
+
+        /* File open */
+        case IRP_MJ_CREATE:
+            guac_rdpdr_fs_process_create(device, input_stream, completion_id);
+            break;
+
+        /* File close */
+        case IRP_MJ_CLOSE:
+            guac_rdpdr_fs_process_close(device, input_stream, file_id, completion_id);
+            break;
+
+        /* File read */
+        case IRP_MJ_READ:
+            guac_rdpdr_fs_process_read(device, input_stream, file_id, completion_id);
+            break;
+
+        /* File write */
+        case IRP_MJ_WRITE:
+            guac_rdpdr_fs_process_write(device, input_stream, file_id, completion_id);
+            break;
+
+        /* Device control request (Windows FSCTL_ control codes) */
+        case IRP_MJ_DEVICE_CONTROL:
+            guac_rdpdr_fs_process_device_control(device, input_stream, file_id, completion_id);
+            break;
+
+        /* Query volume (drive) information */
+        case IRP_MJ_QUERY_VOLUME_INFORMATION:
+            guac_rdpdr_fs_process_volume_info(device, input_stream, file_id, completion_id);
+            break;
+
+        /* Set volume (drive) information */
+        case IRP_MJ_SET_VOLUME_INFORMATION:
+            guac_rdpdr_fs_process_set_volume_info(device, input_stream, file_id, completion_id);
+            break;
+
+        /* Query file information */
+        case IRP_MJ_QUERY_INFORMATION:
+            guac_rdpdr_fs_process_file_info(device, input_stream, file_id, completion_id);
+            break;
+
+        /* Set file information */
+        case IRP_MJ_SET_INFORMATION:
+            guac_rdpdr_fs_process_set_file_info(device, input_stream, file_id, completion_id);
+            break;
+
+        case IRP_MJ_DIRECTORY_CONTROL:
+
+            /* Enumerate directory contents */
+            if (minor_func == IRP_MN_QUERY_DIRECTORY)
+                guac_rdpdr_fs_process_query_directory(device, input_stream, file_id, completion_id);
+
+            /* Request notification of changes to directory */
+            else if (minor_func == IRP_MN_NOTIFY_CHANGE_DIRECTORY)
+                guac_rdpdr_fs_process_notify_change_directory(device, input_stream,
+                        file_id, completion_id);
+
+            break;
+
+        /* Lock/unlock portions of a file */
+        case IRP_MJ_LOCK_CONTROL:
+            guac_rdpdr_fs_process_lock_control(device, input_stream, file_id, completion_id);
+            break;
+
+        default:
+            guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR,
+                    "Unknown filesystem I/O request function: 0x%x/0x%x",
+                    major_func, minor_func);
+    }
+
+}
+
+static void guac_rdpdr_device_fs_free_handler(guac_rdpdr_device* device) {
+}
+
+void guac_rdpdr_register_fs(guac_rdpdrPlugin* rdpdr) {
+
+    rdp_guac_client_data* data = (rdp_guac_client_data*) rdpdr->client->data;
+    int id = rdpdr->devices_registered++;
+
+    /* Get new device */
+    guac_rdpdr_device* device = &(rdpdr->devices[id]);
+
+    /* Init device */
+    device->rdpdr       = rdpdr;
+    device->device_id   = id;
+    device->device_name = "Guacamole Filesystem";
+
+    /* Set handlers */
+    device->announce_handler  = guac_rdpdr_device_fs_announce_handler;
+    device->iorequest_handler = guac_rdpdr_device_fs_iorequest_handler;
+    device->free_handler      = guac_rdpdr_device_fs_free_handler;
+
+    /* Init data */
+    device->data = data->filesystem;
+
+    /* Announce filesystem to client */
+    guac_protocol_send_filesystem(rdpdr->client->socket,
+            data->filesystem->object, "Shared Drive");
+    guac_socket_flush(rdpdr->client->socket);
+
+}
+
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_service.h b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_service.h
new file mode 100644
index 0000000..54b3ada
--- /dev/null
+++ b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_service.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef __GUAC_RDPDR_FS_H
+#define __GUAC_RDPDR_FS_H
+
+/**
+ * Functions and macros specific to filesystem handling and initialization
+ * independent of RDP.  The functions here may deal with the RDPDR device
+ * directly, but their semantics must not deal with RDP protocol messaging.
+ * Functions here represent a virtual Windows-style filesystem on top of UNIX
+ * system calls and structures, using the guac_rdpdr_device structure as a home
+ * for common data.
+ *
+ * @file rdpdr_fs.h 
+ */
+
+#include "config.h"
+
+#include "rdpdr_service.h"
+
+#include <guacamole/pool.h>
+
+/**
+ * Registers a new filesystem device within the RDPDR plugin. This must be done
+ * before RDPDR connection finishes.
+ */
+void guac_rdpdr_register_fs(guac_rdpdrPlugin* rdpdr);
+
+#endif
+
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_messages.c b/src/protocols/rdp/guac_rdpdr/rdpdr_messages.c
index 44d0e88..aa11532 100644
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_messages.c
+++ b/src/protocols/rdp/guac_rdpdr/rdpdr_messages.c
@@ -1,61 +1,41 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Original Code is libguac-client-rdp.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "rdpdr_messages.h"
+#include "rdpdr_service.h"
 
-#include <pthread.h>
-#include <stdlib.h>
-#include <string.h>
-#include <freerdp/constants.h>
 #include <freerdp/utils/svc_plugin.h>
+#include <guacamole/client.h>
 
 #ifdef ENABLE_WINPR
 #include <winpr/stream.h>
-#include <winpr/wtypes.h>
 #else
 #include "compat/winpr-stream.h"
-#include "compat/winpr-wtypes.h"
 #endif
 
-#include <guacamole/client.h>
-
-#include "rdpdr_service.h"
-#include "rdpdr_messages.h"
-#include "rdpdr_printer.h"
-#include "client.h"
-
+#include <stdlib.h>
+#include <string.h>
 
 static void guac_rdpdr_send_client_announce_reply(guac_rdpdrPlugin* rdpdr,
         unsigned int major, unsigned int minor, unsigned int client_id) {
@@ -97,14 +77,14 @@ static void guac_rdpdr_send_client_name_request(guac_rdpdrPlugin* rdpdr, const c
 static void guac_rdpdr_send_client_capability(guac_rdpdrPlugin* rdpdr) {
 
     wStream* output_stream = Stream_New(NULL, 256);
-    guac_client_log_info(rdpdr->client, "Sending capabilities...");
+    guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Sending capabilities...");
 
     /* Write header */
     Stream_Write_UINT16(output_stream, RDPDR_CTYP_CORE);
     Stream_Write_UINT16(output_stream, PAKID_CORE_CLIENT_CAPABILITY);
 
     /* Capability count + padding */
-    Stream_Write_UINT16(output_stream, 2);
+    Stream_Write_UINT16(output_stream, 3);
     Stream_Write_UINT16(output_stream, 0); /* Padding */
 
     /* General capability header */
@@ -132,8 +112,13 @@ static void guac_rdpdr_send_client_capability(guac_rdpdrPlugin* rdpdr) {
     Stream_Write_UINT16(output_stream, 8);
     Stream_Write_UINT32(output_stream, PRINT_CAPABILITY_VERSION_01);
 
+    /* Drive support header */
+    Stream_Write_UINT16(output_stream, CAP_DRIVE_TYPE);
+    Stream_Write_UINT16(output_stream, 8);
+    Stream_Write_UINT32(output_stream, DRIVE_CAPABILITY_VERSION_02);
+
     svc_plugin_send((rdpSvcPlugin*) rdpdr, output_stream);
-    guac_client_log_info(rdpdr->client, "Capabilities sent.");
+    guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Capabilities sent.");
 
 }
 
@@ -151,16 +136,15 @@ static void guac_rdpdr_send_client_device_list_announce_request(guac_rdpdrPlugin
     for (i=0; i<rdpdr->devices_registered; i++) {
         guac_rdpdr_device* device = &(rdpdr->devices[i]);
         device->announce_handler(device, output_stream, i);
-        guac_client_log_info(rdpdr->client, "Registered device %i (%s)",
+        guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Registered device %i (%s)",
                 device->device_id, device->device_name);
     }
 
     svc_plugin_send((rdpSvcPlugin*) rdpdr, output_stream);
-    guac_client_log_info(rdpdr->client, "All supported devices sent.");
+    guac_client_log(rdpdr->client, GUAC_LOG_INFO, "All supported devices sent.");
 
 }
 
-
 void guac_rdpdr_process_server_announce(guac_rdpdrPlugin* rdpdr,
         wStream* input_stream) {
 
@@ -174,7 +158,7 @@ void guac_rdpdr_process_server_announce(guac_rdpdrPlugin* rdpdr,
     if (minor < 12)
         client_id = random() & 0xFFFF;
 
-    guac_client_log_info(rdpdr->client, "Connected to RDPDR %u.%u as client 0x%04x", major, minor, client_id);
+    guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Connected to RDPDR %u.%u as client 0x%04x", major, minor, client_id);
 
     /* Respond to announce */
     guac_rdpdr_send_client_announce_reply(rdpdr, major, minor, client_id);
@@ -185,7 +169,7 @@ void guac_rdpdr_process_server_announce(guac_rdpdrPlugin* rdpdr,
 }
 
 void guac_rdpdr_process_clientid_confirm(guac_rdpdrPlugin* rdpdr, wStream* input_stream) {
-    guac_client_log_info(rdpdr->client, "Client ID confirmed");
+    guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Client ID confirmed");
 }
 
 void guac_rdpdr_process_device_reply(guac_rdpdrPlugin* rdpdr, wStream* input_stream) {
@@ -203,14 +187,14 @@ void guac_rdpdr_process_device_reply(guac_rdpdrPlugin* rdpdr, wStream* input_str
     code     =  ntstatus & 0x0000FFFF;
 
     /* Log error / information */
-    if (device_id >= 0 && device_id < rdpdr->devices_registered) {
+    if (device_id < rdpdr->devices_registered) {
 
         if (severity == 0x0)
-            guac_client_log_info(rdpdr->client, "Device %i (%s) connected successfully",
+            guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Device %i (%s) connected successfully",
                     device_id, rdpdr->devices[device_id].device_name);
 
         else
-            guac_client_log_error(rdpdr->client, "Problem connecting device %i (%s): "
+            guac_client_log(rdpdr->client, GUAC_LOG_ERROR, "Problem connecting device %i (%s): "
                     "severity=0x%x, c=0x%x, n=0x%x, facility=0x%x, code=0x%x",
                      device_id, rdpdr->devices[device_id].device_name,
                      severity,      c,      n,      facility,      code);
@@ -218,7 +202,7 @@ void guac_rdpdr_process_device_reply(guac_rdpdrPlugin* rdpdr, wStream* input_str
     }
 
     else
-        guac_client_log_error(rdpdr->client, "Unknown device ID: 0x%08x", device_id);
+        guac_client_log(rdpdr->client, GUAC_LOG_ERROR, "Unknown device ID: 0x%08x", device_id);
 
 }
 
@@ -244,7 +228,7 @@ void guac_rdpdr_process_device_iorequest(guac_rdpdrPlugin* rdpdr, wStream* input
     }
 
     else
-        guac_client_log_error(rdpdr->client, "Unknown device ID: 0x%08x", device_id);
+        guac_client_log(rdpdr->client, GUAC_LOG_ERROR, "Unknown device ID: 0x%08x", device_id);
 
 }
 
@@ -267,7 +251,7 @@ void guac_rdpdr_process_server_capability(guac_rdpdrPlugin* rdpdr, wStream* inpu
         Stream_Read_UINT16(input_stream, length);
 
         /* Ignore all for now */
-        guac_client_log_info(rdpdr->client, "Ignoring server capability set type=0x%04x, length=%i", type, length);
+        guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Ignoring server capability set type=0x%04x, length=%i", type, length);
         Stream_Seek(input_stream, length - 4);
 
     }
@@ -279,16 +263,16 @@ void guac_rdpdr_process_server_capability(guac_rdpdrPlugin* rdpdr, wStream* inpu
 
 void guac_rdpdr_process_user_loggedon(guac_rdpdrPlugin* rdpdr, wStream* input_stream) {
 
-    guac_client_log_info(rdpdr->client, "User logged on");
+    guac_client_log(rdpdr->client, GUAC_LOG_INFO, "User logged on");
     guac_rdpdr_send_client_device_list_announce_request(rdpdr);
 
 }
 
 void guac_rdpdr_process_prn_cache_data(guac_rdpdrPlugin* rdpdr, wStream* input_stream) {
-    guac_client_log_info(rdpdr->client, "Ignoring printer cached configuration data");
+    guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Ignoring printer cached configuration data");
 }
 
 void guac_rdpdr_process_prn_using_xps(guac_rdpdrPlugin* rdpdr, wStream* input_stream) {
-    guac_client_log_info(rdpdr->client, "Printer unexpectedly switched to XPS mode");
+    guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Printer unexpectedly switched to XPS mode");
 }
 
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_messages.h b/src/protocols/rdp/guac_rdpdr/rdpdr_messages.h
index 8d2890f..d4b0b99 100644
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_messages.h
+++ b/src/protocols/rdp/guac_rdpdr/rdpdr_messages.h
@@ -1,50 +1,40 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef __GUAC_RDPDR_MESSAGES_H
 #define __GUAC_RDPDR_MESSAGES_H
 
+#include "config.h"
+
+#include "rdpdr_service.h"
+
 #ifdef ENABLE_WINPR
 #include <winpr/stream.h>
 #else
 #include "compat/winpr-stream.h"
 #endif
 
-#include "rdpdr_service.h"
+#include <stdint.h>
 
 /**
  * Identifies the "core" component of RDPDR as the destination of the received
@@ -95,6 +85,18 @@
 #define GUAC_PRINTER_NAME          "G\0u\0a\0c\0a\0m\0o\0l\0e\0\0\0"
 #define GUAC_PRINTER_NAME_LENGTH   20
 
+/**
+ * Name of the filesystem.
+ */
+#define GUAC_FILESYSTEM_NAME          "G\0u\0a\0c\0a\0m\0o\0l\0e\0\0\0"
+#define GUAC_FILESYSTEM_NAME_LENGTH   20
+
+/**
+ * Label of the filesystem.
+ */
+#define GUAC_FILESYSTEM_LABEL          "G\0U\0A\0C\0F\0I\0L\0E\0"
+#define GUAC_FILESYSTEM_LABEL_LENGTH   16
+
 /*
  * Capability types
  */
@@ -119,6 +121,12 @@
 #define PRINT_CAPABILITY_VERSION_01   1
 
 /*
+ * Drive capability header versions.
+ */
+#define DRIVE_CAPABILITY_VERSION_01   1
+#define DRIVE_CAPABILITY_VERSION_02   2
+
+/*
  * Legal client major version numbers.
  */
 
@@ -181,6 +189,37 @@
 #define IRP_MN_NOTIFY_CHANGE_DIRECTORY 0x00000002
 
 /*
+ * Volume information constants.
+ */
+
+#define FileFsVolumeInformation    0x00000001
+#define FileFsSizeInformation      0x00000003
+#define FileFsDeviceInformation    0x00000004 
+#define FileFsAttributeInformation 0x00000005 
+#define FileFsFullSizeInformation  0x00000007 
+
+/*
+ * File information constants.
+ */
+
+#define FileBasicInformation        0x00000004
+#define FileStandardInformation     0x00000005
+#define FileRenameInformation       0x0000000A
+#define FileDispositionInformation  0x0000000D
+#define FileAllocationInformation   0x00000013
+#define FileEndOfFileInformation    0x00000014
+#define FileAttributeTagInformation 0x00000023 
+
+/*
+ * Directory information constants.
+ */
+
+#define FileDirectoryInformation     0x00000001
+#define FileFullDirectoryInformation 0x00000002
+#define FileBothDirectoryInformation 0x00000003
+#define FileNamesInformation         0x0000000C
+
+/*
  * Message handlers.
  */
 
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c b/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c
index 8d6e9f4..7aca701 100644
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c
+++ b/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c
@@ -1,5 +1,35 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
 
-#include <errno.h>
+#include "rdpdr_messages.h"
+#include "rdpdr_printer.h"
+#include "rdpdr_service.h"
+#include "rdp_status.h"
+
+#include <freerdp/utils/svc_plugin.h>
+#include <guacamole/client.h>
+#include <guacamole/protocol.h>
 
 #ifdef ENABLE_WINPR
 #include <winpr/stream.h>
@@ -7,14 +37,10 @@
 #include "compat/winpr-stream.h"
 #endif
 
-#include <freerdp/utils/svc_plugin.h>
-
-#include <guacamole/protocol.h>
-
-#include "rdpdr_messages.h"
-#include "rdpdr_printer.h"
-#include "rdpdr_service.h"
-#include "client.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
 
 /* Command to run GhostScript safely as a filter writing PDF */
 char* const guac_rdpdr_pdf_filter_command[] = {
@@ -28,6 +54,7 @@ char* const guac_rdpdr_pdf_filter_command[] = {
     "-sOutputFile=-",
     "-c",
     ".setpdfwrite",
+    "-sstdout=/dev/null",
     "-f",
     "-",
     NULL
@@ -44,11 +71,11 @@ static void* guac_rdpdr_print_filter_output_thread(void* data) {
     /* Write all output as blobs */
     while ((length = read(printer_data->printer_output, buffer, sizeof(buffer))) > 0)
         guac_protocol_send_blob(device->rdpdr->client->socket,
-                GUAC_RDPDR_PRINTER_BLOB, buffer, length);
+                printer_data->stream, buffer, length);
 
     /* Log any error */
     if (length < 0)
-        guac_client_log_error(device->rdpdr->client, "Error reading from filter: %s", strerror(errno));
+        guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR, "Error reading from filter: %s", strerror(errno));
 
     return NULL;
 
@@ -64,14 +91,14 @@ static int guac_rdpdr_create_print_process(guac_rdpdr_device* device) {
 
     /* Create STDIN pipe */
     if (pipe(stdin_pipe)) {
-        guac_client_log_error(device->rdpdr->client,
+        guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR,
                 "Unable to create STDIN pipe for PDF filter process: %s", strerror(errno));
         return 1;
     }
 
     /* Create STDOUT pipe */
     if (pipe(stdout_pipe)) {
-        guac_client_log_error(device->rdpdr->client,
+        guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR,
                 "Unable to create STDIN pipe for PDF filter process: %s", strerror(errno));
         close(stdin_pipe[0]);
         close(stdin_pipe[1]);
@@ -84,7 +111,7 @@ static int guac_rdpdr_create_print_process(guac_rdpdr_device* device) {
 
     /* Start output thread */
     if (pthread_create(&(printer_data->printer_output_thread), NULL, guac_rdpdr_print_filter_output_thread, device)) {
-        guac_client_log_error(device->rdpdr->client, "Unable to fork PDF filter process");
+        guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR, "Unable to fork PDF filter process");
         close(stdin_pipe[0]);
         close(stdin_pipe[1]);
         close(stdout_pipe[0]);
@@ -97,7 +124,7 @@ static int guac_rdpdr_create_print_process(guac_rdpdr_device* device) {
 
     /* Log fork errors */
     if (child_pid == -1) {
-        guac_client_log_error(device->rdpdr->client,
+        guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR,
                 "Unable to fork PDF filter process: %s", strerror(errno));
         close(stdin_pipe[0]);
         close(stdin_pipe[1]);
@@ -118,11 +145,11 @@ static int guac_rdpdr_create_print_process(guac_rdpdr_device* device) {
         dup2(stdout_pipe[1], STDOUT_FILENO);
 
         /* Run PDF filter */
-        guac_client_log_info(device->rdpdr->client, "Running %s", guac_rdpdr_pdf_filter_command[0]);
+        guac_client_log(device->rdpdr->client, GUAC_LOG_INFO, "Running %s", guac_rdpdr_pdf_filter_command[0]);
         if (execvp(guac_rdpdr_pdf_filter_command[0], guac_rdpdr_pdf_filter_command) < 0)
-            guac_client_log_error(device->rdpdr->client, "Unable to execute PDF filter command: %s", strerror(errno));
+            guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR, "Unable to execute PDF filter command: %s", strerror(errno));
         else
-            guac_client_log_error(device->rdpdr->client, "Unable to execute PDF filter command, but no error given");
+            guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR, "Unable to execute PDF filter command, but no error given");
 
         /* Terminate child process */
         exit(1);
@@ -130,7 +157,7 @@ static int guac_rdpdr_create_print_process(guac_rdpdr_device* device) {
     }
 
     /* Log fork success */
-    guac_client_log_info(device->rdpdr->client, "Created PDF filter process PID=%i", child_pid);
+    guac_client_log(device->rdpdr->client, GUAC_LOG_INFO, "Created PDF filter process PID=%i", child_pid);
 
     /* Close unneeded ends of pipe */
     close(stdin_pipe[0]);
@@ -142,20 +169,14 @@ static int guac_rdpdr_create_print_process(guac_rdpdr_device* device) {
 void guac_rdpdr_process_print_job_create(guac_rdpdr_device* device,
         wStream* input_stream, int completion_id) {
 
-    guac_rdpdr_printer_data* printer_data = (guac_rdpdr_printer_data*) device->data;
-    wStream* output_stream = Stream_New(NULL, 24);
+    guac_rdpdr_printer_data* printer_data =
+        (guac_rdpdr_printer_data*) device->data;
+
+    wStream* output_stream = guac_rdpdr_new_io_completion(device,
+            completion_id, STATUS_SUCCESS, 4);
 
     /* No bytes received yet */
     printer_data->bytes_received = 0;
-
-    /* Write header */
-    Stream_Write_UINT16(output_stream, RDPDR_CTYP_CORE);
-    Stream_Write_UINT16(output_stream, PAKID_CORE_DEVICE_IOCOMPLETION);
-
-    /* Write content */
-    Stream_Write_UINT32(output_stream, device->device_id);
-    Stream_Write_UINT32(output_stream, completion_id);
-    Stream_Write_UINT32(output_stream, 0); /* Success */
     Stream_Write_UINT32(output_stream, 0); /* fileId */
 
     svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
@@ -169,7 +190,7 @@ void guac_rdpdr_process_print_job_write(guac_rdpdr_device* device,
     int status=0, length;
     unsigned char* buffer;
 
-    wStream* output_stream = Stream_New(NULL, 24);
+    wStream* output_stream;
 
     Stream_Read_UINT32(input_stream, length);
     Stream_Seek(input_stream, 8);  /* Offset */
@@ -217,13 +238,13 @@ void guac_rdpdr_process_print_job_write(guac_rdpdr_device* device,
         }
 
         /* Begin file */
-        guac_client_log_info(device->rdpdr->client, "Print job created");
+        guac_client_log(device->rdpdr->client, GUAC_LOG_INFO, "Print job created");
         guac_protocol_send_file(device->rdpdr->client->socket,
-                GUAC_RDPDR_PRINTER_BLOB, "application/pdf", filename);
+                printer_data->stream, "application/pdf", filename);
 
         /* Start print process */
         if (guac_rdpdr_create_print_process(device) != 0) {
-            status = 0x80000010;
+            status = STATUS_DEVICE_OFF_LINE;
             length = 0;
         }
 
@@ -237,23 +258,18 @@ void guac_rdpdr_process_print_job_write(guac_rdpdr_device* device,
         /* Write data to printer, translate output for RDP */
         length = write(printer_data->printer_input, buffer, length);
         if (length == -1) {
-            guac_client_log_error(device->rdpdr->client, "Error writing to printer: %s", strerror(errno));
-            status = 0x80000010;
+            guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR, "Error writing to printer: %s", strerror(errno));
+            status = STATUS_DEVICE_OFF_LINE;
             length = 0;
         }
 
     }
 
-    /* Write header */
-    Stream_Write_UINT16(output_stream, RDPDR_CTYP_CORE);
-    Stream_Write_UINT16(output_stream, PAKID_CORE_DEVICE_IOCOMPLETION);
+    output_stream = guac_rdpdr_new_io_completion(device, completion_id,
+            status, 5);
 
-    /* Write content */
-    Stream_Write_UINT32(output_stream, device->device_id);
-    Stream_Write_UINT32(output_stream, completion_id);
-    Stream_Write_UINT32(output_stream, status);
     Stream_Write_UINT32(output_stream, length);
-    Stream_Write_UINT8(output_stream, 0); /* padding (stated as optional in spec, but requests fail without) */
+    Stream_Write_UINT8(output_stream, 0); /* Padding */
 
     svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
 
@@ -262,8 +278,13 @@ void guac_rdpdr_process_print_job_write(guac_rdpdr_device* device,
 void guac_rdpdr_process_print_job_close(guac_rdpdr_device* device,
         wStream* input_stream, int completion_id) {
 
-    guac_rdpdr_printer_data* printer_data = (guac_rdpdr_printer_data*) device->data;
-    wStream* output_stream = Stream_New(NULL, 24);
+    guac_rdpdr_printer_data* printer_data =
+        (guac_rdpdr_printer_data*) device->data;
+
+    wStream* output_stream = guac_rdpdr_new_io_completion(device,
+            completion_id, STATUS_SUCCESS, 1);
+
+    Stream_Write_UINT32(output_stream, 0); /* padding*/
 
     /* Close input and wait for output thread to finish */
     close(printer_data->printer_input);
@@ -273,18 +294,8 @@ void guac_rdpdr_process_print_job_close(guac_rdpdr_device* device,
     close(printer_data->printer_output);
 
     /* Close file */
-    guac_client_log_info(device->rdpdr->client, "Print job closed");
-    guac_protocol_send_end(device->rdpdr->client->socket, GUAC_RDPDR_PRINTER_BLOB);
-
-    /* Write header */
-    Stream_Write_UINT16(output_stream, RDPDR_CTYP_CORE);
-    Stream_Write_UINT16(output_stream, PAKID_CORE_DEVICE_IOCOMPLETION);
-
-    /* Write content */
-    Stream_Write_UINT32(output_stream, device->device_id);
-    Stream_Write_UINT32(output_stream, completion_id);
-    Stream_Write_UINT32(output_stream, 0); /* NTSTATUS - success */
-    Stream_Write_UINT32(output_stream, 0); /* padding*/
+    guac_client_log(device->rdpdr->client, GUAC_LOG_INFO, "Print job closed");
+    guac_protocol_send_end(device->rdpdr->client->socket, printer_data->stream);
 
     svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
 
@@ -294,7 +305,7 @@ static void guac_rdpdr_device_printer_announce_handler(guac_rdpdr_device* device
         wStream* output_stream, int device_id) {
 
     /* Printer header */
-    guac_client_log_info(device->rdpdr->client, "Sending printer");
+    guac_client_log(device->rdpdr->client, GUAC_LOG_INFO, "Sending printer");
     Stream_Write_UINT32(output_stream, RDPDR_DTYP_PRINT);
     Stream_Write_UINT32(output_stream, device_id);
     Stream_Write(output_stream, "PRN1\0\0\0\0", 8); /* DOS name */
@@ -337,7 +348,7 @@ static void guac_rdpdr_device_printer_iorequest_handler(guac_rdpdr_device* devic
 
         /* Log unknown */
         default:
-            guac_client_log_error(device->rdpdr->client,
+            guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR,
                     "Unknown printer I/O request function: 0x%x/0x%x",
                     major_func, minor_func);
 
@@ -346,6 +357,8 @@ static void guac_rdpdr_device_printer_iorequest_handler(guac_rdpdr_device* devic
 }
 
 static void guac_rdpdr_device_printer_free_handler(guac_rdpdr_device* device) {
+    guac_rdpdr_printer_data* printer_data = (guac_rdpdr_printer_data*) device->data;
+    guac_client_free_stream(device->rdpdr->client, printer_data->stream);
     free(device->data);
 }
 
@@ -355,6 +368,7 @@ void guac_rdpdr_register_printer(guac_rdpdrPlugin* rdpdr) {
 
     /* Get new device */
     guac_rdpdr_device* device = &(rdpdr->devices[id]);
+    guac_rdpdr_printer_data* printer_data;
 
     /* Init device */
     device->rdpdr       = rdpdr;
@@ -367,7 +381,9 @@ void guac_rdpdr_register_printer(guac_rdpdrPlugin* rdpdr) {
     device->free_handler      = guac_rdpdr_device_printer_free_handler;
 
     /* Init data */
-    device->data = malloc(sizeof(guac_rdpdr_printer_data));
+    printer_data = malloc(sizeof(guac_rdpdr_printer_data));
+    printer_data->stream = guac_client_alloc_stream(rdpdr->client);
+    device->data = printer_data;
 
 }
 
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_printer.h b/src/protocols/rdp/guac_rdpdr/rdpdr_printer.h
index 15fa323..529033d 100644
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_printer.h
+++ b/src/protocols/rdp/guac_rdpdr/rdpdr_printer.h
@@ -1,55 +1,42 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef __GUAC_RDPDR_PRINTER_H
 #define __GUAC_RDPDR_PRINTER_H
 
+#include "config.h"
+
+#include "rdpdr_service.h"
+
+#include <guacamole/stream.h>
+
 #ifdef ENABLE_WINPR
 #include <winpr/stream.h>
 #else
 #include "compat/winpr-stream.h"
 #endif
 
-#include <freerdp/utils/svc_plugin.h>
-
-/**
- * The index of the blob to use when sending printed files.
- */
-#define GUAC_RDPDR_PRINTER_BLOB 0
+#include <pthread.h>
 
 /**
  * Data specific to an instance of the printer device.
@@ -57,6 +44,11 @@
 typedef struct guac_rdpdr_printer_data {
 
     /**
+     * Stream for receiving printed files.
+     */
+    guac_stream* stream;
+
+    /**
      * File descriptor that should be written to when sending documents to the
      * printer.
      */
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_service.c b/src/protocols/rdp/guac_rdpdr/rdpdr_service.c
index 3ebfb70..9be53f7 100644
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_service.c
+++ b/src/protocols/rdp/guac_rdpdr/rdpdr_service.c
@@ -1,63 +1,52 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Original Code is libguac-client-rdp.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "client.h"
+#include "rdp_fs.h"
+#include "rdp_settings.h"
+#include "rdp_stream.h"
+#include "rdpdr_fs_service.h"
+#include "rdpdr_messages.h"
+#include "rdpdr_printer.h"
+#include "rdpdr_service.h"
 
 #include <stdlib.h>
 #include <string.h>
 
 #include <freerdp/constants.h>
 #include <freerdp/utils/svc_plugin.h>
+#include <guacamole/client.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+#include <guacamole/stream.h>
 
 #ifdef ENABLE_WINPR
 #include <winpr/stream.h>
-#include <winpr/wtypes.h>
 #else
 #include "compat/winpr-stream.h"
-#include "compat/winpr-wtypes.h"
 #endif
 
-#include <guacamole/client.h>
-
-#include "rdpdr_service.h"
-#include "rdpdr_messages.h"
-#include "rdpdr_printer.h"
-
-#include "client.h"
-
-
 /**
  * Entry point for RDPDR virtual channel.
  */
@@ -112,8 +101,12 @@ void guac_rdpdr_process_connect(rdpSvcPlugin* plugin) {
     if (client_data->settings.printing_enabled)
         guac_rdpdr_register_printer(rdpdr);
 
+    /* Register drive if enabled */
+    if (client_data->settings.drive_enabled)
+        guac_rdpdr_register_fs(rdpdr);
+
     /* Log that printing, etc. has been loaded */
-    guac_client_log_info(client, "guacdr connected.");
+    guac_client_log(client, GUAC_LOG_INFO, "guacdr connected.");
 
 }
 
@@ -124,7 +117,7 @@ void guac_rdpdr_process_terminate(rdpSvcPlugin* plugin) {
 
     for (i=0; i<rdpdr->devices_registered; i++) {
         guac_rdpdr_device* device = &(rdpdr->devices[i]);
-        guac_client_log_info(rdpdr->client, "Unloading device %i (%s)",
+        guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Unloading device %i (%s)",
                 device->device_id, device->device_name);
         device->free_handler(device);
     }
@@ -179,7 +172,7 @@ void guac_rdpdr_process_receive(rdpSvcPlugin* plugin,
                 break;
 
             default:
-                guac_client_log_info(rdpdr->client, "Ignoring RDPDR core packet with unexpected ID: 0x%04x", packet_id);
+                guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Ignoring RDPDR core packet with unexpected ID: 0x%04x", packet_id);
 
         }
 
@@ -200,14 +193,84 @@ void guac_rdpdr_process_receive(rdpSvcPlugin* plugin,
                 break;
 
             default:
-                guac_client_log_info(rdpdr->client, "Ignoring RDPDR printer packet with unexpected ID: 0x%04x", packet_id);
+                guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Ignoring RDPDR printer packet with unexpected ID: 0x%04x", packet_id);
 
         }
 
     } /* end if printer */
 
     else
-        guac_client_log_info(rdpdr->client, "Ignoring packet for unknown RDPDR component: 0x%04x", component);
+        guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Ignoring packet for unknown RDPDR component: 0x%04x", component);
+
+}
+
+wStream* guac_rdpdr_new_io_completion(guac_rdpdr_device* device,
+        int completion_id, int status, int size) {
+
+    wStream* output_stream = Stream_New(NULL, 16+size);
+
+    /* Write header */
+    Stream_Write_UINT16(output_stream, RDPDR_CTYP_CORE);
+    Stream_Write_UINT16(output_stream, PAKID_CORE_DEVICE_IOCOMPLETION);
+
+    /* Write content */
+    Stream_Write_UINT32(output_stream, device->device_id);
+    Stream_Write_UINT32(output_stream, completion_id);
+    Stream_Write_UINT32(output_stream, status);
+
+    return output_stream;
+
+}
+
+void guac_rdpdr_start_download(guac_rdpdr_device* device, const char* path) {
+
+    /* Get client and stream */
+    guac_client* client = device->rdpdr->client;
+
+    int file_id = guac_rdp_fs_open((guac_rdp_fs*) device->data, path,
+            ACCESS_FILE_READ_DATA, 0, DISP_FILE_OPEN, 0);
+
+    /* If file opened successfully, start stream */
+    if (file_id >= 0) {
+
+        guac_rdp_stream* rdp_stream;
+        const char* basename;
+
+        int i;
+        char c;
+
+        /* Associate stream with transfer status */
+        guac_stream* stream = guac_client_alloc_stream(client);
+        stream->data = rdp_stream = malloc(sizeof(guac_rdp_stream));
+        stream->ack_handler = guac_rdp_download_ack_handler;
+        rdp_stream->type = GUAC_RDP_DOWNLOAD_STREAM;
+        rdp_stream->download_status.file_id = file_id;
+        rdp_stream->download_status.offset = 0;
+
+        /* Get basename from absolute path */
+        i=0;
+        basename = path;
+        do {
+
+            c = path[i];
+            if (c == '/' || c == '\\')
+                basename = &(path[i+1]);
+
+            i++;
+
+        } while (c != '\0');
+
+        guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
+                "%s: Initiating download of \"%s\"", __func__, path);
+
+        /* Begin stream */
+        guac_protocol_send_file(client->socket, stream,
+                "application/octet-stream", basename);
+        guac_socket_flush(client->socket);
+
+    }
+    else
+        guac_client_log(client, GUAC_LOG_ERROR, "Unable to download \"%s\"", path);
 
 }
 
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_service.h b/src/protocols/rdp/guac_rdpdr/rdpdr_service.h
index 09a9a60..58cad9a 100644
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_service.h
+++ b/src/protocols/rdp/guac_rdpdr/rdpdr_service.h
@@ -1,44 +1,33 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef __GUAC_RDPDR_SERVICE_H
 #define __GUAC_RDPDR_SERVICE_H
 
-#include <pthread.h>
+#include "config.h"
+
+#include <freerdp/utils/svc_plugin.h>
+#include <guacamole/client.h>
 
 #ifdef ENABLE_WINPR
 #include <winpr/stream.h>
@@ -46,7 +35,10 @@
 #include "compat/winpr-stream.h"
 #endif
 
-#include <guacamole/client.h>
+/**
+ * The maximum number of bytes to allow for a device read.
+ */
+#define GUAC_RDP_MAX_READ_BUFFER 4194304
 
 typedef struct guac_rdpdrPlugin guac_rdpdrPlugin;
 typedef struct guac_rdpdr_device guac_rdpdr_device;
@@ -112,7 +104,6 @@ struct guac_rdpdr_device {
 
 };
 
-
 /**
  * Structure representing the current state of the Guacamole RDPDR plugin for
  * FreeRDP.
@@ -143,7 +134,6 @@ struct guac_rdpdrPlugin {
 
 };
 
-
 /**
  * Handler called when this plugin is loaded by FreeRDP.
  */
@@ -166,5 +156,17 @@ void guac_rdpdr_process_terminate(rdpSvcPlugin* plugin);
  */
 void guac_rdpdr_process_event(rdpSvcPlugin* plugin, wMessage* event);
 
+/**
+ * Creates a new stream which contains the ommon DR_DEVICE_IOCOMPLETION header
+ * used for virtually all responses.
+ */
+wStream* guac_rdpdr_new_io_completion(guac_rdpdr_device* device,
+        int completion_id, int status, int size);
+
+/**
+ * Begins streaming the given file to the user via a Guacamole file stream.
+ */
+void guac_rdpdr_start_download(guac_rdpdr_device* device, const char* path);
+
 #endif
 
diff --git a/src/protocols/rdp/guac_rdpsnd/rdpsnd_messages.c b/src/protocols/rdp/guac_rdpsnd/rdpsnd_messages.c
index 72b9fdb..a0827d3 100644
--- a/src/protocols/rdp/guac_rdpsnd/rdpsnd_messages.c
+++ b/src/protocols/rdp/guac_rdpsnd/rdpsnd_messages.c
@@ -1,45 +1,37 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2015 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "client.h"
+#include "rdpsnd_messages.h"
+#include "rdpsnd_service.h"
 
 #include <pthread.h>
 #include <stdlib.h>
-#include <string.h>
-#include <freerdp/constants.h>
+
 #include <freerdp/utils/svc_plugin.h>
+#include <guacamole/audio.h>
+#include <guacamole/client.h>
 
 #ifdef ENABLE_WINPR
 #include <winpr/stream.h>
@@ -49,18 +41,10 @@
 #include "compat/winpr-wtypes.h"
 #endif
 
-#include <guacamole/client.h>
-
-#include "audio.h"
-#include "rdpsnd_service.h"
-#include "rdpsnd_messages.h"
-#include "client.h"
-
 /* MESSAGE HANDLERS */
 
 void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd,
-        audio_stream* audio, wStream* input_stream, 
-        guac_rdpsnd_pdu_header* header) {
+        wStream* input_stream, guac_rdpsnd_pdu_header* header) {
 
     int server_format_count;
     int server_version;
@@ -70,8 +54,12 @@ void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd,
     int output_body_size;
     unsigned char* output_stream_end;
 
-    rdp_guac_client_data* guac_client_data =
-        (rdp_guac_client_data*) audio->client->data;
+    /* Get associated client data */
+    guac_client* client = rdpsnd->client;
+    rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
+
+    /* Get audio stream from client data */
+    guac_audio_stream* audio = client_data->audio;
 
     /* Format header */
     Stream_Seek(input_stream, 14);
@@ -104,72 +92,86 @@ void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd,
     Stream_Write_UINT16(output_stream, 6);
     Stream_Write_UINT8(output_stream,  0);
 
-    /* Check each server format, respond if supported */
-    for (i=0; i < server_format_count; i++) {
-
-        unsigned char* format_start;
-
-        int format_tag;
-        int channels;
-        int rate;
-        int bps;
-        int body_size;
-
-        /* Remember position in stream */
-        Stream_GetPointer(input_stream, format_start);
-
-        /* Read format */
-        Stream_Read_UINT16(input_stream, format_tag);
-        Stream_Read_UINT16(input_stream, channels);
-        Stream_Read_UINT32(input_stream, rate);
-        Stream_Seek_UINT32(input_stream);
-        Stream_Seek_UINT16(input_stream);
-        Stream_Read_UINT16(input_stream, bps);
-
-        /* Skip past extra data */
-        Stream_Read_UINT16(input_stream, body_size);
-        Stream_Seek(input_stream, body_size);
-
-        /* If PCM, accept */
-        if (format_tag == WAVE_FORMAT_PCM) {
-
-            /* If can fit another format, accept it */
-            if (rdpsnd->format_count < GUAC_RDP_MAX_FORMATS) {
-
-                /* Add channel */
-                int current = rdpsnd->format_count++;
-                rdpsnd->formats[current].rate     = rate;
-                rdpsnd->formats[current].channels = channels;
-                rdpsnd->formats[current].bps      = bps;
-
-                /* Log format */
-                guac_client_log_info(audio->client,
-                        "Accepted format: %i-bit PCM with %i channels at "
-                        "%i Hz",
-                        bps, channels, rate);
-
-                /* Queue format for sending as accepted */
-                Stream_EnsureRemainingCapacity(output_stream, 18 + body_size);
-                Stream_Write(output_stream, format_start, 18 + body_size);
-
-                /* 
-                 * BEWARE that using Stream_EnsureRemainingCapacity means that any
-                 * pointers returned via Stream_GetPointer on output_stream are invalid.
-                 */
+    /* Check each server format, respond if supported and audio is enabled */
+    if (audio != NULL) {
+        for (i=0; i < server_format_count; i++) {
+
+            unsigned char* format_start;
+
+            int format_tag;
+            int channels;
+            int rate;
+            int bps;
+            int body_size;
+
+            /* Remember position in stream */
+            Stream_GetPointer(input_stream, format_start);
+
+            /* Read format */
+            Stream_Read_UINT16(input_stream, format_tag);
+            Stream_Read_UINT16(input_stream, channels);
+            Stream_Read_UINT32(input_stream, rate);
+            Stream_Seek_UINT32(input_stream);
+            Stream_Seek_UINT16(input_stream);
+            Stream_Read_UINT16(input_stream, bps);
+
+            /* Skip past extra data */
+            Stream_Read_UINT16(input_stream, body_size);
+            Stream_Seek(input_stream, body_size);
+
+            /* If PCM, accept */
+            if (format_tag == WAVE_FORMAT_PCM) {
+
+                /* If can fit another format, accept it */
+                if (rdpsnd->format_count < GUAC_RDP_MAX_FORMATS) {
+
+                    /* Add channel */
+                    int current = rdpsnd->format_count++;
+                    rdpsnd->formats[current].rate     = rate;
+                    rdpsnd->formats[current].channels = channels;
+                    rdpsnd->formats[current].bps      = bps;
+
+                    /* Log format */
+                    guac_client_log(client, GUAC_LOG_INFO,
+                            "Accepted format: %i-bit PCM with %i channels at "
+                            "%i Hz",
+                            bps, channels, rate);
+
+                    /* Ensure audio stream is configured to use accepted
+                     * format */
+                    guac_audio_stream_reset(audio, NULL, rate, channels, bps);
+
+                    /* Queue format for sending as accepted */
+                    Stream_EnsureRemainingCapacity(output_stream,
+                            18 + body_size);
+                    Stream_Write(output_stream, format_start, 18 + body_size);
+
+                    /*
+                     * BEWARE that using Stream_EnsureRemainingCapacity means
+                     * that any pointers returned via Stream_GetPointer on
+                     * output_stream are invalid.
+                     */
+
+                }
+
+                /* Otherwise, log that we dropped one */
+                else
+                    guac_client_log(client, GUAC_LOG_INFO,
+                            "Dropped valid format: %i-bit PCM with %i "
+                            "channels at %i Hz",
+                            bps, channels, rate);
 
             }
 
-            /* Otherwise, log that we dropped one */
-            else
-                guac_client_log_info(audio->client,
-                        "Dropped valid format: %i-bit PCM with %i channels at "
-                        "%i Hz",
-                        bps, channels, rate);
-
         }
-
     }
 
+    /* Otherwise, ignore all supported formats as we do not intend to actually
+     * receive audio */
+    else
+        guac_client_log(client, GUAC_LOG_DEBUG,
+                "Audio explicitly disabled. Ignoring supported formats.");
+
     /* Calculate size of PDU */
     output_body_size = Stream_GetPosition(output_stream) - 4;
     Stream_GetPointer(output_stream, output_stream_end);
@@ -186,7 +188,7 @@ void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd,
     Stream_SetPointer(output_stream, output_stream_end);
 
     /* Send accepted formats */
-    pthread_mutex_lock(&(guac_client_data->rdp_lock));
+    pthread_mutex_lock(&(client_data->rdp_lock));
     svc_plugin_send((rdpSvcPlugin*)rdpsnd, output_stream);
 
     /* If version greater than 6, must send Quality Mode PDU */
@@ -203,20 +205,20 @@ void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd,
         svc_plugin_send((rdpSvcPlugin*)rdpsnd, output_stream);
     }
 
-    pthread_mutex_unlock(&(guac_client_data->rdp_lock));
+    pthread_mutex_unlock(&(client_data->rdp_lock));
 
 }
 
 /* server is getting a feel of the round trip time */
 void guac_rdpsnd_training_handler(guac_rdpsndPlugin* rdpsnd,
-        audio_stream* audio, wStream* input_stream,
-        guac_rdpsnd_pdu_header* header) {
+        wStream* input_stream, guac_rdpsnd_pdu_header* header) {
 
     int data_size;
     wStream* output_stream;
 
-    rdp_guac_client_data* guac_client_data =
-        (rdp_guac_client_data*) audio->client->data;
+    /* Get associated client data */
+    guac_client* client = rdpsnd->client;
+    rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
 
     /* Read timestamp and data size */
     Stream_Read_UINT16(input_stream, rdpsnd->server_timestamp);
@@ -230,25 +232,30 @@ void guac_rdpsnd_training_handler(guac_rdpsndPlugin* rdpsnd,
     Stream_Write_UINT16(output_stream, rdpsnd->server_timestamp);
     Stream_Write_UINT16(output_stream, data_size);
 
-    pthread_mutex_lock(&(guac_client_data->rdp_lock));
+    pthread_mutex_lock(&(client_data->rdp_lock));
     svc_plugin_send((rdpSvcPlugin*) rdpsnd, output_stream);
-    pthread_mutex_unlock(&(guac_client_data->rdp_lock));
+    pthread_mutex_unlock(&(client_data->rdp_lock));
 
 }
 
 void guac_rdpsnd_wave_info_handler(guac_rdpsndPlugin* rdpsnd,
-        audio_stream* audio, wStream* input_stream,
-        guac_rdpsnd_pdu_header* header) {
+        wStream* input_stream, guac_rdpsnd_pdu_header* header) {
 
-    unsigned char buffer[4];
     int format;
 
+    /* Get associated client data */
+    guac_client* client = rdpsnd->client;
+    rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
+
+    /* Get audio stream from client data */
+    guac_audio_stream* audio = client_data->audio;
+
     /* Read wave information */
     Stream_Read_UINT16(input_stream, rdpsnd->server_timestamp);
     Stream_Read_UINT16(input_stream, format);
     Stream_Read_UINT8(input_stream, rdpsnd->waveinfo_block_number);
     Stream_Seek(input_stream, 3);
-    Stream_Read(input_stream, buffer, 4);
+    Stream_Read(input_stream, rdpsnd->initial_wave_data, 4);
 
     /*
      * Size of incoming wave data is equal to the body size field of this
@@ -260,35 +267,42 @@ void guac_rdpsnd_wave_info_handler(guac_rdpsndPlugin* rdpsnd,
     /* Read wave in next iteration */
     rdpsnd->next_pdu_is_wave = TRUE;
 
-    /* Init stream with requested format */
-    audio_stream_begin(audio,
-            rdpsnd->formats[format].rate,
-            rdpsnd->formats[format].channels,
-            rdpsnd->formats[format].bps);
-
-    /* Write initial 4 bytes of data */
-    audio_stream_write_pcm(audio, buffer, 4);
+    /* Reset audio stream if format has changed */
+    if (audio != NULL)
+        guac_audio_stream_reset(audio, NULL,
+                rdpsnd->formats[format].rate,
+                rdpsnd->formats[format].channels,
+                rdpsnd->formats[format].bps);
 
 }
 
 void guac_rdpsnd_wave_handler(guac_rdpsndPlugin* rdpsnd,
-        audio_stream* audio, wStream* input_stream,
-        guac_rdpsnd_pdu_header* header) {
+        wStream* input_stream, guac_rdpsnd_pdu_header* header) {
 
     rdpSvcPlugin* plugin = (rdpSvcPlugin*)rdpsnd;
 
-    rdp_guac_client_data* guac_client_data =
-        (rdp_guac_client_data*) audio->client->data;
+    /* Get associated client data */
+    guac_client* client = rdpsnd->client;
+    rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
+
+    /* Get audio stream from client data */
+    guac_audio_stream* audio = client_data->audio;
 
     /* Wave Confirmation PDU */
     wStream* output_stream = Stream_New(NULL, 8);
 
     /* Get wave data */
-    unsigned char* buffer = Stream_Buffer(input_stream) + 4;
+    unsigned char* buffer = Stream_Buffer(input_stream);
+
+    /* Copy over first four bytes */
+    memcpy(buffer, rdpsnd->initial_wave_data, 4);
 
     /* Write rest of audio packet */
-    audio_stream_write_pcm(audio, buffer, rdpsnd->incoming_wave_size);
-    audio_stream_end(audio);
+    if (audio != NULL) {
+        guac_audio_stream_write_pcm(audio, buffer,
+                rdpsnd->incoming_wave_size + 4);
+        guac_audio_stream_flush(audio);
+    }
 
     /* Write Wave Confirmation PDU */
     Stream_Write_UINT8(output_stream, SNDC_WAVECONFIRM);
@@ -299,9 +313,9 @@ void guac_rdpsnd_wave_handler(guac_rdpsndPlugin* rdpsnd,
     Stream_Write_UINT8(output_stream, 0);
 
     /* Send Wave Confirmation PDU */
-    pthread_mutex_lock(&(guac_client_data->rdp_lock));
+    pthread_mutex_lock(&(client_data->rdp_lock));
     svc_plugin_send(plugin, output_stream);
-    pthread_mutex_unlock(&(guac_client_data->rdp_lock));
+    pthread_mutex_unlock(&(client_data->rdp_lock));
 
     /* We no longer expect to receive wave data */
     rdpsnd->next_pdu_is_wave = FALSE;
@@ -309,10 +323,9 @@ void guac_rdpsnd_wave_handler(guac_rdpsndPlugin* rdpsnd,
 }
 
 void guac_rdpsnd_close_handler(guac_rdpsndPlugin* rdpsnd,
-        audio_stream* audio, wStream* input_stream,
-        guac_rdpsnd_pdu_header* header) {
+        wStream* input_stream, guac_rdpsnd_pdu_header* header) {
 
-    /* STUB: Do nothing for now */
+    /* Do nothing */
 
 }
 
diff --git a/src/protocols/rdp/guac_rdpsnd/rdpsnd_messages.h b/src/protocols/rdp/guac_rdpsnd/rdpsnd_messages.h
index 55832a3..f75235a 100644
--- a/src/protocols/rdp/guac_rdpsnd/rdpsnd_messages.h
+++ b/src/protocols/rdp/guac_rdpsnd/rdpsnd_messages.h
@@ -1,43 +1,39 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2015 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef __GUAC_RDPSND_MESSAGES_H
 #define __GUAC_RDPSND_MESSAGES_H
 
+#include "config.h"
+
+#include "rdpsnd_service.h"
+
+#ifdef ENABLE_WINPR
+#include <winpr/stream.h>
+#else
+#include "compat/winpr-stream.h"
+#endif
+
 /*
  * PDU Message Types
  */
@@ -124,39 +120,106 @@ typedef struct guac_rdpsnd_pdu_header {
 } guac_rdpsnd_pdu_header;
 
 /**
- * Handler for the SNDC_FORMATS (Server Audio Formats and Version) PDU.
+ * Handler for the SNDC_FORMATS (Server Audio Formats and Version) PDU. The
+ * SNDC_FORMATS PDU describes all audio formats supported by the RDP server, as
+ * well as the version of RDPSND implemented.
+ *
+ * @param rdpsnd
+ *     The Guacamole RDPSND plugin receiving the SNDC_FORMATS PDU.
+ *
+ * @param input_stream
+ *     The FreeRDP input stream containing the remaining raw bytes (after the
+ *     common header) of the SNDC_FORMATS PDU.
+ *
+ * @param header
+ *     The header content of the SNDC_FORMATS PDU. All RDPSND messages contain
+ *     the same header information.
  */
 void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd,
-        audio_stream* audio, wStream* input_stream,
-        guac_rdpsnd_pdu_header* header);
+        wStream* input_stream, guac_rdpsnd_pdu_header* header);
 
 /**
- * Handler for the SNDC_TRAINING (Training) PDU.
+ * Handler for the SNDC_TRAINING (Training) PDU. The SNDC_TRAINING PDU is used
+ * to by RDP servers to test audio streaming latency, etc. without actually
+ * sending audio data. See:
+ *
+ * https://msdn.microsoft.com/en-us/library/cc240961.aspx
+ *
+ * @param rdpsnd
+ *     The Guacamole RDPSND plugin receiving the SNDC_TRAINING PDU.
+ *
+ * @param input_stream
+ *     The FreeRDP input stream containing the remaining raw bytes (after the
+ *     common header) of the SNDC_TRAINING PDU.
+ *
+ * @param header
+ *     The header content of the SNDC_TRAINING PDU. All RDPSND messages contain
+ *     the same header information.
  */
 void guac_rdpsnd_training_handler(guac_rdpsndPlugin* rdpsnd,
-        audio_stream* audio, wStream* input_stream,
-        guac_rdpsnd_pdu_header* header);
+        wStream* input_stream, guac_rdpsnd_pdu_header* header);
 
 /**
- * Handler for the SNDC_WAVE (WaveInfo) PDU.
+ * Handler for the SNDC_WAVE (WaveInfo) PDU. The SNDC_WAVE immediately precedes
+ * a SNDWAV PDU and describes the data about to be received. It also (very
+ * strangely) contains exactly 4 bytes of audio data. The following SNDWAV PDU
+ * then contains 4 bytes of padding prior to the audio data where it would make
+ * perfect sense for this data to go. See:
+ *
+ * https://msdn.microsoft.com/en-us/library/cc240963.aspx
+ *
+ * @param rdpsnd
+ *     The Guacamole RDPSND plugin receiving the SNDC_WAVE PDU.
+ *
+ * @param input_stream
+ *     The FreeRDP input stream containing the remaining raw bytes (after the
+ *     common header) of the SNDC_WAVE PDU.
+ *
+ * @param header
+ *     The header content of the SNDC_WAVE PDU. All RDPSND messages contain
+ *     the same header information.
  */
 void guac_rdpsnd_wave_info_handler(guac_rdpsndPlugin* rdpsnd,
-        audio_stream* audio, wStream* input_stream,
-        guac_rdpsnd_pdu_header* header);
+        wStream* input_stream, guac_rdpsnd_pdu_header* header);
 
 /**
- * Handler for the SNDWAV (Wave) PDU which follows any WaveInfo PDU.
+ * Handler for the SNDWAV (Wave) PDU which follows any WaveInfo PDU. The SNDWAV
+ * PDU contains the actual audio data, less the four bytes of audio data
+ * included in the SNDC_WAVE PDU.
+ *
+ * @param rdpsnd
+ *     The Guacamole RDPSND plugin receiving the SNDWAV PDU.
+ *
+ * @param input_stream
+ *     The FreeRDP input stream containing the remaining raw bytes (after the
+ *     common header) of the SNDWAV PDU.
+ *
+ * @param header
+ *     The header content of the SNDWAV PDU. All RDPSND messages contain
+ *     the same header information.
  */
 void guac_rdpsnd_wave_handler(guac_rdpsndPlugin* rdpsnd,
-        audio_stream* audio, wStream* input_stream,
-        guac_rdpsnd_pdu_header* header);
+        wStream* input_stream, guac_rdpsnd_pdu_header* header);
 
 /**
- * Handler for the SNDC_CLOSE (Close) PDU.
+ * Handler for the SNDC_CLOSE (Close) PDU. This PDU is sent when audio
+ * streaming has stopped. This PDU is currently ignored by Guacamole. See:
+ *
+ * https://msdn.microsoft.com/en-us/library/cc240970.aspx
+ *
+ * @param rdpsnd
+ *     The Guacamole RDPSND plugin receiving the SNDC_CLOSE PDU.
+ *
+ * @param input_stream
+ *     The FreeRDP input stream containing the remaining raw bytes (after the
+ *     common header) of the SNDC_CLOSE PDU.
+ *
+ * @param header
+ *     The header content of the SNDC_CLOSE PDU. All RDPSND messages contain
+ *     the same header information.
  */
 void guac_rdpsnd_close_handler(guac_rdpsndPlugin* rdpsnd,
-        audio_stream* audio, wStream* input_stream,
-        guac_rdpsnd_pdu_header* header);
+        wStream* input_stream, guac_rdpsnd_pdu_header* header);
 
 #endif
 
diff --git a/src/protocols/rdp/guac_rdpsnd/rdpsnd_service.c b/src/protocols/rdp/guac_rdpsnd/rdpsnd_service.c
index 3d4b808..30810ef 100644
--- a/src/protocols/rdp/guac_rdpsnd/rdpsnd_service.c
+++ b/src/protocols/rdp/guac_rdpsnd/rdpsnd_service.c
@@ -1,61 +1,43 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2015 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "rdpsnd_service.h"
+#include "rdpsnd_messages.h"
 
 #include <stdlib.h>
 #include <string.h>
 
 #include <freerdp/constants.h>
-#include <freerdp/types.h>
 #include <freerdp/utils/svc_plugin.h>
+#include <guacamole/client.h>
 
 #ifdef ENABLE_WINPR
 #include <winpr/stream.h>
-#include <winpr/wtypes.h>
 #else
 #include "compat/winpr-stream.h"
-#include "compat/winpr-wtypes.h"
 #endif
 
-#include <guacamole/client.h>
-
-#include "audio.h"
-#include "rdpsnd_service.h"
-#include "rdpsnd_messages.h"
-
 /**
  * Entry point for RDPSND virtual channel.
  */
@@ -90,11 +72,11 @@ void guac_rdpsnd_process_connect(rdpSvcPlugin* plugin) {
 
     guac_rdpsndPlugin* rdpsnd = (guac_rdpsndPlugin*) plugin;
 
-    /* Get audio stream from plugin parameters */
-    audio_stream* audio = rdpsnd->audio =
-        (audio_stream*) plugin->channel_entry_points.pExtendedData;
+    /* Get client from plugin parameters */
+    guac_client* client = rdpsnd->client =
+        (guac_client*) plugin->channel_entry_points.pExtendedData;
 
-    /* NULL out pExtendedData so we don't lose our audio_stream due to an
+    /* NULL out pExtendedData so we don't lose our guac_client due to an
      * automatic free() within libfreerdp */
     plugin->channel_entry_points.pExtendedData = NULL;
 
@@ -104,7 +86,7 @@ void guac_rdpsnd_process_connect(rdpSvcPlugin* plugin) {
 #endif
 
     /* Log that sound has been loaded */
-    guac_client_log_info(audio->client, "guacsnd connected.");
+    guac_client_log(client, GUAC_LOG_INFO, "guacsnd connected.");
 
 }
 
@@ -122,9 +104,6 @@ void guac_rdpsnd_process_receive(rdpSvcPlugin* plugin,
     guac_rdpsndPlugin* rdpsnd = (guac_rdpsndPlugin*) plugin;
     guac_rdpsnd_pdu_header header;
 
-    /* Get audio stream from plugin */
-    audio_stream* audio = rdpsnd->audio;
-
     /* Read RDPSND PDU header */
     Stream_Read_UINT8(input_stream, header.message_type);
     Stream_Seek_UINT8(input_stream);
@@ -135,7 +114,7 @@ void guac_rdpsnd_process_receive(rdpSvcPlugin* plugin,
      * ignore the header and parse as a Wave PDU.
      */
     if (rdpsnd->next_pdu_is_wave) {
-        guac_rdpsnd_wave_handler(rdpsnd, audio, input_stream, &header);
+        guac_rdpsnd_wave_handler(rdpsnd, input_stream, &header);
         return;
     }
 
@@ -144,26 +123,22 @@ void guac_rdpsnd_process_receive(rdpSvcPlugin* plugin,
 
         /* Server Audio Formats and Version PDU */
         case SNDC_FORMATS:
-            guac_rdpsnd_formats_handler(rdpsnd, audio,
-                    input_stream, &header);
+            guac_rdpsnd_formats_handler(rdpsnd, input_stream, &header);
             break;
 
         /* Training PDU */
         case SNDC_TRAINING:
-            guac_rdpsnd_training_handler(rdpsnd, audio,
-                    input_stream, &header);
+            guac_rdpsnd_training_handler(rdpsnd, input_stream, &header);
             break;
 
         /* WaveInfo PDU */
         case SNDC_WAVE:
-            guac_rdpsnd_wave_info_handler(rdpsnd, audio,
-                    input_stream, &header);
+            guac_rdpsnd_wave_info_handler(rdpsnd, input_stream, &header);
             break;
 
         /* Close PDU */
         case SNDC_CLOSE:
-            guac_rdpsnd_close_handler(rdpsnd, audio,
-                    input_stream, &header);
+            guac_rdpsnd_close_handler(rdpsnd, input_stream, &header);
             break;
 
     }
diff --git a/src/protocols/rdp/guac_rdpsnd/rdpsnd_service.h b/src/protocols/rdp/guac_rdpsnd/rdpsnd_service.h
index 263e8ea..dadf7ff 100644
--- a/src/protocols/rdp/guac_rdpsnd/rdpsnd_service.h
+++ b/src/protocols/rdp/guac_rdpsnd/rdpsnd_service.h
@@ -1,50 +1,46 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2015 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef __GUAC_RDPSND_SERVICE_H
 #define __GUAC_RDPSND_SERVICE_H
 
+#include "config.h"
+
+#include <freerdp/utils/svc_plugin.h>
+#include <guacamole/client.h>
+
+#ifdef ENABLE_WINPR
+#include <winpr/stream.h>
+#else
+#include "compat/winpr-stream.h"
+#endif
+
 /**
  * The maximum number of PCM formats to accept during the initial RDPSND
  * handshake with the RDP server.
  */
 #define GUAC_RDP_MAX_FORMATS 16
 
-
 /**
  * Abstract representation of a PCM format, including the sample rate, number
  * of channels, and bits per sample.
@@ -70,7 +66,6 @@ typedef struct guac_pcm_format {
 
 } guac_pcm_format;
 
-
 /**
  * Structure representing the current state of the Guacamole RDPSND plugin for
  * FreeRDP.
@@ -85,9 +80,10 @@ typedef struct guac_rdpsndPlugin {
     rdpSvcPlugin plugin;
 
     /**
-     * The current audio stream.
+     * The Guacamole client associated with the guac_audio_stream that this
+     * plugin should use to stream received audio packets.
      */
-    audio_stream* audio;
+    guac_client* client;
 
     /**
      * The block number of the last SNDC_WAVE (WaveInfo) PDU received.
@@ -101,7 +97,15 @@ typedef struct guac_rdpsndPlugin {
     int next_pdu_is_wave;
 
     /**
+     * The wave data received within the last SNDC_WAVE (WaveInfo) PDU.
+     */
+    unsigned char initial_wave_data[4];
+
+    /**
      * The size, in bytes, of the wave data in the coming Wave PDU, if any.
+     * This does not include the initial wave data received within the last
+     * SNDC_WAVE (WaveInfo) PDU, which is always the first four bytes of the
+     * actual wave data block.
      */
     int incoming_wave_size;
 
@@ -124,7 +128,6 @@ typedef struct guac_rdpsndPlugin {
 
 } guac_rdpsndPlugin;
 
-
 /**
  * Handler called when this plugin is loaded by FreeRDP.
  */
diff --git a/src/protocols/rdp/guac_svc/svc_service.c b/src/protocols/rdp/guac_svc/svc_service.c
new file mode 100644
index 0000000..8a35e66
--- /dev/null
+++ b/src/protocols/rdp/guac_svc/svc_service.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "svc_service.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <freerdp/constants.h>
+#include <freerdp/utils/svc_plugin.h>
+#include <guacamole/client.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+
+#ifdef ENABLE_WINPR
+#include <winpr/stream.h>
+#else
+#include "compat/winpr-stream.h"
+#endif
+
+/**
+ * Entry point for arbitrary SVC.
+ */
+int VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints) {
+
+    /* Gain access to plugin data */
+    CHANNEL_ENTRY_POINTS_FREERDP* entry_points_ex =
+        (CHANNEL_ENTRY_POINTS_FREERDP*) pEntryPoints;
+
+    /* Allocate plugin */
+    guac_svcPlugin* svc_plugin =
+        (guac_svcPlugin*) calloc(1, sizeof(guac_svcPlugin));
+
+    /* Get SVC descriptor from plugin parameters */
+    guac_rdp_svc* svc = (guac_rdp_svc*) entry_points_ex->pExtendedData;
+
+    /* Init channel def */
+    strncpy(svc_plugin->plugin.channel_def.name, svc->name,
+            GUAC_RDP_SVC_MAX_LENGTH);
+    svc_plugin->plugin.channel_def.options = 
+          CHANNEL_OPTION_INITIALIZED
+        | CHANNEL_OPTION_ENCRYPT_RDP
+        | CHANNEL_OPTION_COMPRESS_RDP;
+
+    /* Init plugin */
+    svc_plugin->svc = svc;
+
+    /* Set callbacks */
+    svc_plugin->plugin.connect_callback   = guac_svc_process_connect;
+    svc_plugin->plugin.receive_callback   = guac_svc_process_receive;
+    svc_plugin->plugin.event_callback     = guac_svc_process_event;
+    svc_plugin->plugin.terminate_callback = guac_svc_process_terminate;
+
+    /* Store plugin reference in SVC */
+    svc->plugin = (rdpSvcPlugin*) svc_plugin;
+
+    /* Finish init */
+    svc_plugin_init((rdpSvcPlugin*) svc_plugin, pEntryPoints);
+    return 1;
+
+}
+
+/* 
+ * Service Handlers
+ */
+
+void guac_svc_process_connect(rdpSvcPlugin* plugin) {
+
+    /* Get corresponding guac_rdp_svc */
+    guac_svcPlugin* svc_plugin = (guac_svcPlugin*) plugin;
+    guac_rdp_svc* svc = svc_plugin->svc;
+
+    /* NULL out pExtendedData so we don't lose our guac_rdp_svc due to an
+     * automatic free() within libfreerdp */
+    plugin->channel_entry_points.pExtendedData = NULL;
+
+    /* Create pipe */
+    svc->output_pipe = guac_client_alloc_stream(svc->client);
+    guac_protocol_send_pipe(svc->client->socket, svc->output_pipe,
+            "application/octet-stream", svc->name);
+
+    /* Log connection to static channel */
+    guac_client_log(svc->client, GUAC_LOG_INFO,
+            "Static channel \"%s\" connected.", svc->name);
+
+}
+
+void guac_svc_process_terminate(rdpSvcPlugin* plugin) {
+
+    /* Get corresponding guac_rdp_svc */
+    guac_svcPlugin* svc_plugin = (guac_svcPlugin*) plugin;
+    guac_rdp_svc* svc = svc_plugin->svc;
+
+    /* Remove and free SVC */
+    guac_client_log(svc->client, GUAC_LOG_INFO, "Closing channel \"%s\"...", svc->name);
+    guac_rdp_remove_svc(svc->client, svc->name);
+    free(svc);
+
+    free(plugin);
+
+}
+
+void guac_svc_process_event(rdpSvcPlugin* plugin, wMessage* event) {
+    freerdp_event_free(event);
+}
+
+void guac_svc_process_receive(rdpSvcPlugin* plugin,
+        wStream* input_stream) {
+
+    /* Get corresponding guac_rdp_svc */
+    guac_svcPlugin* svc_plugin = (guac_svcPlugin*) plugin;
+    guac_rdp_svc* svc = svc_plugin->svc;
+
+    /* Fail if output not created */
+    if (svc->output_pipe == NULL) {
+        guac_client_log(svc->client, GUAC_LOG_ERROR,
+                "Output for channel \"%s\" dropped.",
+                svc->name);
+        return;
+    }
+
+    /* Send blob */
+    guac_protocol_send_blob(svc->client->socket, svc->output_pipe,
+            Stream_Buffer(input_stream),
+            Stream_Length(input_stream));
+
+    guac_socket_flush(svc->client->socket);
+
+}
+
diff --git a/src/protocols/rdp/guac_svc/svc_service.h b/src/protocols/rdp/guac_svc/svc_service.h
new file mode 100644
index 0000000..bb509ac
--- /dev/null
+++ b/src/protocols/rdp/guac_svc/svc_service.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef __GUAC_SVC_SERVICE_H
+#define __GUAC_SVC_SERVICE_H
+
+#include "config.h"
+#include "rdp_svc.h"
+
+#include <freerdp/utils/svc_plugin.h>
+
+#ifdef ENABLE_WINPR
+#include <winpr/stream.h>
+#else
+#include "compat/winpr-stream.h"
+#endif
+
+/**
+ * Structure representing the current state of an arbitrary static virtual
+ * channel.
+ */
+typedef struct guac_svcPlugin {
+
+    /**
+     * The FreeRDP parts of this plugin. This absolutely MUST be first.
+     * FreeRDP depends on accessing this structure as if it were an instance
+     * of rdpSvcPlugin.
+     */
+    rdpSvcPlugin plugin;
+
+    /**
+     * The Guacamole-specific SVC structure describing the channel this
+     * instance represents.
+     */
+    guac_rdp_svc* svc;
+
+} guac_svcPlugin;
+
+/**
+ * Handler called when this plugin is loaded by FreeRDP.
+ */
+void guac_svc_process_connect(rdpSvcPlugin* plugin);
+
+/**
+ * Handler called when this plugin receives data along its designated channel.
+ */
+void guac_svc_process_receive(rdpSvcPlugin* plugin,
+        wStream* input_stream);
+
+/**
+ * Handler called when this plugin is being unloaded.
+ */
+void guac_svc_process_terminate(rdpSvcPlugin* plugin);
+
+/**
+ * Handler called when this plugin receives an event.
+ */
+void guac_svc_process_event(rdpSvcPlugin* plugin, wMessage* event);
+
+#endif
+
diff --git a/src/protocols/rdp/keymaps/base.keymap b/src/protocols/rdp/keymaps/base.keymap
new file mode 100644
index 0000000..188165c
--- /dev/null
+++ b/src/protocols/rdp/keymaps/base.keymap
@@ -0,0 +1,92 @@
+#
+# Copyright (C) 2013 Glyptodon LLC
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+name "base"
+
+# Typeable characters
+map 0x39 ~ " "    # Space
+map 0x0F ~ 0xff09 # Tab
+
+# Control characters
+map      0x0E ~ 0xff08 # BackSpace
+map      0x1C ~ 0xff0d # Return
+map      0x01 ~ 0xff1b # Escape
+map +ext 0x52 ~ 0xff63 # Insert
+map +ext 0x53 ~ 0xffff # Delete
+map +ext 0x47 ~ 0xff50 # Home
+map +ext 0x4F ~ 0xff57 # End
+map +ext 0x4B ~ 0xff51 # Left
+map +ext 0x48 ~ 0xff52 # Up
+map +ext 0x4D ~ 0xff53 # Right
+map +ext 0x50 ~ 0xff54 # Down
+map +ext 0x49 ~ 0xff55 # Page_Up
+map +ext 0x51 ~ 0xff56 # Page_Down
+
+# Locks
+map      0x45 ~ 0xff7f # Num_Lock
+map      0x46 ~ 0xff14 # Scroll_Lock
+map +ext 0x3A ~ 0xffe5 # Caps_Lock
+
+# Keypad numerals
+map 0x52 ~ 0xffb0 # KP_0
+map 0x4F ~ 0xffb1 # KP_1
+map 0x50 ~ 0xffb2 # KP_2
+map 0x51 ~ 0xffb3 # KP_3
+map 0x4B ~ 0xffb4 # KP_4
+map 0x4C ~ 0xffb5 # KP_5
+map 0x4D ~ 0xffb6 # KP_6
+map 0x47 ~ 0xffb7 # KP_7
+map 0x48 ~ 0xffb8 # KP_8
+map 0x49 ~ 0xffb9 # KP_9
+
+# Keypad operators
+map      0x37 ~ 0xffaa # KP_multiply
+map      0x4e ~ 0xffab # KP_add
+map      0x4a ~ 0xffad # KP_subtract
+map      0x53 ~ 0xffae # KP_decimal
+map +ext 0x35 ~ 0xffaf # KP_divide
+
+# F keys
+map 0x3B ~ 0xffbe # F1
+map 0x3C ~ 0xffbf # F2
+map 0x3D ~ 0xffc0 # F3
+map 0x3E ~ 0xffc1 # F4
+map 0x3F ~ 0xffc2 # F5
+map 0x40 ~ 0xffc3 # F6
+map 0x41 ~ 0xffc4 # F7
+map 0x42 ~ 0xffc5 # F8
+map 0x43 ~ 0xffc6 # F9
+map 0x44 ~ 0xffc7 # F10
+map 0x57 ~ 0xffc8 # F11
+map 0x58 ~ 0xffc9 # F12
+
+# Modifiers
+map      0x2A ~ 0xffe1 # Shift_L
+map      0x36 ~ 0xffe2 # Shift_R
+map      0x1D ~ 0xffe3 # Control_L
+map      0x9D ~ 0xffe4 # Control_R
+map      0x38 ~ 0xffe9 # Alt_L
+map +ext 0x38 ~ 0xffea # Alt_R
+map +ext 0x5B ~ 0xffeb # Super_L
+map +ext 0x5C ~ 0xffec # Super_R
+map +ext 0x5D ~ 0xff67 # Menu
+
diff --git a/src/protocols/rdp/keymaps/de_de_qwertz.keymap b/src/protocols/rdp/keymaps/de_de_qwertz.keymap
new file mode 100644
index 0000000..8dd193f
--- /dev/null
+++ b/src/protocols/rdp/keymaps/de_de_qwertz.keymap
@@ -0,0 +1,67 @@
+#
+# Copyright (C) 2013 Glyptodon LLC
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+parent  "base"
+name    "de-de-qwertz"
+freerdp "KBD_GERMAN"
+
+#
+# Basic keys
+#
+
+map -altgr -shift      0x02..0x0C      ~ "1234567890ß"
+map -altgr -shift      0x10..0x1B      ~ "qwertzuiopü+"
+map -altgr -shift      0x1E..0x28 0x2B ~ "asdfghjklöä#"
+map -altgr -shift 0x56 0x2C..0x35      ~ "<yxcvbnm,.-"
+
+map -altgr +shift 0x29 0x02..0x0C      ~ "°!"§$%&/()=?"
+map -altgr +shift      0x10..0x1B      ~ "QWERTZUIOPÜ*"
+map -altgr +shift      0x1E..0x28 0x2B ~ "ASDFGHJKLÖÄ'"
+map -altgr +shift 0x56 0x2C..0x35      ~ ">YXCVBNM;:_"
+
+#
+# Keys requiring AltGr
+#
+
+map +altgr -shift 0x03 ~ "²"
+map +altgr -shift 0x04 ~ "³"
+map +altgr -shift 0x08 ~ "{"
+map +altgr -shift 0x09 ~ "["
+map +altgr -shift 0x0A ~ "]"
+map +altgr -shift 0x0B ~ "}"
+map +altgr -shift 0x0C ~ "\"
+
+map +altgr -shift 0x10 ~ "@"
+map +altgr -shift 0x12 ~ "€"
+
+map +altgr -shift 0x56 ~ "|"
+map +altgr -shift 0x32 ~ "µ"
+
+#
+# Dead keys
+#
+
+map -altgr +shift 0x0D ~ 0xFE50 # Dead grave
+map -altgr -shift 0x0D ~ 0xFE51 # Dead acute
+map -altgr -shift 0x29 ~ 0xFE52 # Dead circumflex
+map +altgr -shift 0x0C ~ 0xFE53 # Dead tilde
+
diff --git a/src/protocols/rdp/keymaps/en_us_qwerty.keymap b/src/protocols/rdp/keymaps/en_us_qwerty.keymap
new file mode 100644
index 0000000..305f21e
--- /dev/null
+++ b/src/protocols/rdp/keymaps/en_us_qwerty.keymap
@@ -0,0 +1,36 @@
+#
+# Copyright (C) 2013 Glyptodon LLC
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+parent  "base"
+name    "en-us-qwerty"
+freerdp "KBD_US"
+
+map -shift 0x29 0x02..0x0D      ~ "`1234567890-="
+map -shift      0x10..0x1B 0x2B ~ "qwertyuiop[]\"
+map -shift      0x1E..0x28      ~ "asdfghjkl;'"
+map -shift      0x2C..0x35      ~ "zxcvbnm,./"
+
+map +shift 0x29 0x02..0x0D      ~ "~!@#$%^&*()_+"
+map +shift      0x10..0x1B 0x2B ~ "QWERTYUIOP{}|"
+map +shift      0x1E..0x28      ~ "ASDFGHJKL:""
+map +shift      0x2C..0x35      ~ "ZXCVBNM<>?"
+
diff --git a/src/protocols/rdp/keymaps/failsafe.keymap b/src/protocols/rdp/keymaps/failsafe.keymap
new file mode 100644
index 0000000..db9fa0e
--- /dev/null
+++ b/src/protocols/rdp/keymaps/failsafe.keymap
@@ -0,0 +1,26 @@
+#
+# Copyright (C) 2013 Glyptodon LLC
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+parent  "base"
+name    "failsafe"
+freerdp "KBD_US"
+
diff --git a/src/protocols/rdp/keymaps/fr_fr_azerty.keymap b/src/protocols/rdp/keymaps/fr_fr_azerty.keymap
new file mode 100644
index 0000000..1984729
--- /dev/null
+++ b/src/protocols/rdp/keymaps/fr_fr_azerty.keymap
@@ -0,0 +1,56 @@
+#
+# Copyright (C) 2013 Glyptodon LLC
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+parent  "base"
+name    "fr-fr-azerty"
+freerdp "KBD_FRENCH"
+
+#
+# Basic keys
+#
+
+map -altgr -shift 0x29 0x02..0x0D      ~ "²&é"'(-è_çà)="
+map -altgr -shift      0x10..0x19 0x1B ~ "azertyuiop$"
+map -altgr -shift      0x1E..0x28 0x2B ~ "qsdfghjklmù*"
+map -altgr -shift 0x56 0x2C..0x35      ~ "<wxcvbn,;:!"
+
+map -altgr +shift      0x02..0x0D      ~ "1234567890°+"
+map -altgr +shift      0x10..0x19 0x1B ~ "AZERTYUIOP£"
+map -altgr +shift      0x1E..0x28 0x2B ~ "QSDFGHJKLM%µ"
+map -altgr +shift 0x56 0x2C..0x35      ~ ">WXCVBN?./§"
+
+#
+# Keys requiring AltGr
+#
+
+map +altgr -shift 0x03..0x0D ~ "~#{[|`\^@]}"
+
+map +altgr -shift 0x12 ~ "€"
+map +altgr -shift 0x1B ~ "¤"
+
+#
+# Dead keys
+#
+
+map -altgr -shift 0x1A ~ 0xFE52 # Dead circumflex
+map -altgr +shift 0x1A ~ 0xFE57 # Dead umlaut
+
diff --git a/src/protocols/rdp/keymaps/generate.pl b/src/protocols/rdp/keymaps/generate.pl
new file mode 100755
index 0000000..50fe461
--- /dev/null
+++ b/src/protocols/rdp/keymaps/generate.pl
@@ -0,0 +1,272 @@
+#!/usr/bin/perl
+#
+# Copyright (C) 2013 Glyptodon LLC
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+#
+# generate.pl
+#
+# Parse .keymap files, producing corresponding .c files that can be included
+# into the RDP plugin source.
+#
+
+# We need at least Perl 5.8 for Unicode's sake
+use 5.008;
+
+sub keymap_symbol {
+    my $name = shift;
+    $name =~ s/-/_/g;
+    return 'guac_rdp_keymap_' . $name;
+}
+
+#
+# _generated_keymaps.c
+#
+
+my @keymaps = ();
+
+open OUTPUT, ">", "_generated_keymaps.c";
+print OUTPUT 
+       '#include "config.h"'                                . "\n"
+     . '#include "rdp_keymap.h"'                            . "\n"
+     . '#include <freerdp/input.h>'                         . "\n"
+     .                                                        "\n"
+     . '#ifdef HAVE_FREERDP_LOCALE_KEYBOARD_H'              . "\n"
+     . '#include <freerdp/locale/keyboard.h>'               . "\n"
+     . '#else'                                              . "\n"
+     . '#include <freerdp/kbd/layouts.h>'                   . "\n"
+     . '#endif'                                             . "\n"
+     .                                                        "\n"
+     . '#include <stddef.h>'                                . "\n"
+     .                                                        "\n";
+
+for my $filename (@ARGV) {
+
+    my $content = "";
+    my $parent = "";
+    my $layout_name = "";
+    my $freerdp = "";
+
+    # Parse file
+    open INPUT, '<', "$filename";
+    binmode INPUT, ":encoding(utf8)";
+    while (<INPUT>) {
+
+        chomp;
+
+        # Comments
+        if (m/^\s*#/) {}
+
+        # Name
+        elsif ((my $name) = m/^\s*name\s+"(.*)"\s*(?:#.*)?$/) {
+            $layout_name = $name;
+        }
+
+        # Parent map
+        elsif ((my $name) = m/^\s*parent\s+"(.*)"\s*(?:#.*)?$/) {
+            $parent = keymap_symbol($name);
+        }
+
+        # FreeRDP equiv 
+        elsif ((my $name) = m/^\s*freerdp\s+"(.*)"\s*(?:#.*)?$/) {
+            $freerdp = $name;
+        }
+
+        # Map
+        elsif ((my $range, my $onto) =
+               m/^\s*map\s+([^~]*)\s+~\s+(".*"|0x[0-9A-Fa-f]+)\s*(?:#.*)?$/) {
+
+            my @keysyms   = ();
+            my @scancodes = ();
+
+            my $ext_flags = 0;
+            my $set_shift = 0;
+            my $set_altgr = 0;
+
+            my $clear_shift = 0;
+            my $clear_altgr = 0;
+
+            # Parse ranges and options
+            foreach $_ (split(/\s+/, $range)) {
+
+                # Set option/modifier
+                if ((my $opt) = m/^\+([a-z]+)$/) {
+                    if    ($opt eq "shift") { $set_shift = 1; }
+                    elsif ($opt eq "altgr") { $set_altgr = 1; }
+                    elsif ($opt eq "ext")   { $ext_flags = 1; }
+                    else {
+                        die "$filename: $.: ERROR: "
+                          . "Invalid set option\n";
+                    }
+                }
+
+                # Clear option/modifier
+                elsif ((my $opt) = m/^-([a-z]+)$/) {
+                    if    ($opt eq "shift") { $clear_shift = 1; }
+                    elsif ($opt eq "altgr") { $clear_altgr = 1; }
+                    else {
+                        die "$filename: $.: ERROR: "
+                          . "Invalid clear option\n";
+                    }
+                }
+
+                # Single scancode
+                elsif ((my $scancode) = m/^(0x[0-9A-Fa-f]+)$/) {
+                    $scancodes[++$#scancodes] = hex($scancode);
+                }
+
+                # Range of scancodes
+                elsif ((my $start, my $end) =
+                       m/^(0x[0-9A-Fa-f]+)\.\.(0x[0-9A-Fa-f]+)$/) {
+                    for (my $i=hex($start); $i<=hex($end); $i++) {
+                        $scancodes[++$#scancodes] = $i;
+                    }
+                }
+
+                # Invalid token
+                else {
+                    die "$filename: $.: ERROR: "
+                      . "Invalid token\n";
+                }
+
+            }
+
+            # Parse onto
+            if ($onto =~ m/^0x/) {
+                $keysyms[0] = hex($onto);
+            }
+            else {
+                foreach my $char (split('',
+                                  substr($onto, 1, length($onto)-2))) {
+                    my $codepoint = ord($char);
+                    if ($codepoint >= 0x0100) {
+                        $keysyms[++$#keysyms] = 0x01000000 | $codepoint;
+                    }
+                    else {
+                        $keysyms[++$#keysyms] = $codepoint;
+                    }
+                }
+            }
+
+            # Check mapping
+            if ($#keysyms != $#scancodes) {
+                die "$filename: $.: ERROR: "
+                  . "Keysym and scancode range lengths differ\n";
+            }
+
+            # Write keysym/scancode pairs
+            for (my $i=0; $i<=$#keysyms; $i++) {
+
+                $content .= "    {"
+                         .  " .keysym = "   . $keysyms[$i] . ","
+                         .  " .scancode = " . $scancodes[$i];
+
+                # Set requirements
+                if ($set_shift && !$set_altgr) {
+                    $content .= ", .set_keysyms = GUAC_KEYSYMS_SHIFT";
+                }
+                elsif (!$set_shift && $set_altgr) {
+                    $content .= ", .set_keysyms = GUAC_KEYSYMS_ALTGR";
+                }
+                elsif ($set_shift && $set_altgr) {
+                    $content .= ", .set_keysyms = GUAC_KEYSYMS_SHIFT_ALTGR";
+                }
+
+                # Clear requirements
+                if ($clear_shift && !$clear_altgr) {
+                    $content .= ", .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT";
+                }
+                elsif (!$clear_shift && $clear_altgr) {
+                    $content .= ", .clear_keysyms = GUAC_KEYSYMS_ALTGR";
+                }
+                elsif ($clear_shift && $clear_altgr) {
+                    $content .= ", .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR";
+                }
+
+                # Flags
+                if ($ext_flags) {
+                    $content .= ", .flags = KBD_FLAGS_EXTENDED";
+                }
+
+                $content .= " },\n";
+
+            }
+
+        }
+
+        # Invalid token
+        elsif (m/\S/) {
+            die "$filename: $.: ERROR: "
+              . "Invalid token\n";
+        }
+
+    }
+    close INPUT;
+
+    # Header
+    my $sym = keymap_symbol($layout_name);
+    print OUTPUT
+                                                                  "\n"
+         . '/* Autogenerated from ' . $filename . ' */'         . "\n"
+         . 'static guac_rdp_keysym_desc __' . $sym . '[] = {'   . "\n"
+         .      $content
+         . '    {0}'                                            . "\n"
+         . '};'                                                 . "\n";
+
+    # Desc header
+    print OUTPUT                                                   "\n"
+          . 'static const guac_rdp_keymap ' . $sym . ' = { '     . "\n";
+
+    # Layout name
+    print OUTPUT "    .name = \"$layout_name\",\n";
+
+    # Parent layout (if any)
+    if ($parent) {
+        print OUTPUT "    .parent = &$parent,\n";
+    }
+
+    # FreeRDP layout (if any)
+    if ($freerdp) {
+        print OUTPUT "    .freerdp_keyboard_layout = $freerdp,\n";
+    }
+
+    # Desc footer
+    print OUTPUT
+            '    .mapping = __' . $sym                           . "\n"
+          . '};'                                                 . "\n";
+
+    $keymaps[++$#keymaps] = $sym;
+    print STDERR "Added: $layout_name\n";
+
+}
+
+print OUTPUT                                                   "\n"
+      . 'const guac_rdp_keymap* GUAC_KEYMAPS[] = {'          . "\n";
+
+foreach my $keymap (@keymaps) {
+    print OUTPUT "    &$keymap,\n";
+}
+print OUTPUT
+        '    NULL'                                           . "\n"
+      . '};'                                                 . "\n";
+
+close OUTPUT;
+
diff --git a/src/protocols/rdp/keymaps/it_it_qwerty.keymap b/src/protocols/rdp/keymaps/it_it_qwerty.keymap
new file mode 100644
index 0000000..ab4481c
--- /dev/null
+++ b/src/protocols/rdp/keymaps/it_it_qwerty.keymap
@@ -0,0 +1,53 @@
+#
+# Copyright (C) 2013 Glyptodon LLC
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+parent  "base"
+name    "it-it-qwerty"
+freerdp "KBD_ITALIAN"
+
+#
+# Basic keys
+#
+
+map -altgr -shift 0x29 0x02..0x0D      ~ "\1234567890'ì"
+map -altgr -shift      0x10..0x1B      ~ "qwertyuiopè+"
+map -altgr -shift      0x1E..0x28 0x2B ~ "asdfghjklòàù"
+map -altgr -shift 0x56 0x2C..0x35      ~ "<zxcvbnm,.-"
+
+map -altgr +shift 0x29 0x02..0x0D      ~ "|!"£$%&/()=?^"
+map -altgr +shift      0x10..0x1B      ~ "QWERTYUIOPé*"
+map -altgr +shift      0x1E..0x28 0x2B ~ "ASDFGHJKLç°§"
+map -altgr +shift 0x56 0x2C..0x35      ~ ">ZXCVBNM;:_"
+
+#
+# Keys requiring AltGr
+#
+
+map +altgr -shift 0x12 ~ "€"
+map +altgr -shift 0x1A ~ "["
+map +altgr -shift 0x1B ~ "]"
+map +altgr -shift 0x27 ~ "@"
+map +altgr -shift 0x28 ~ "#"
+
+map +altgr +shift 0x1A ~ "{"
+map +altgr +shift 0x1B ~ "}"
+
diff --git a/src/protocols/rdp/keymaps/sv_se_qwerty.keymap b/src/protocols/rdp/keymaps/sv_se_qwerty.keymap
new file mode 100644
index 0000000..a4ac875
--- /dev/null
+++ b/src/protocols/rdp/keymaps/sv_se_qwerty.keymap
@@ -0,0 +1,68 @@
+#
+# Copyright (C) 2014 Glyptodon LLC
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+parent  "base"
+name    "sv-se-qwerty"
+freerdp "KBD_SWEDISH"
+
+#
+# Basic keys
+#
+
+map -altgr -shift 0x29 0x02..0x0C      ~ "§1234567890+"
+map -altgr -shift      0x10..0x1A      ~ "qwertyuiopå"
+map -altgr -shift      0x1E..0x28 0x2B ~ "asdfghjklöä'"
+map -altgr -shift 0x56 0x2C..0x35      ~ "<zxcvbnm,.-"
+
+map -altgr +shift 0x29 0x02..0x0C      ~ "½!"#¤%&/()=?"
+map -altgr +shift      0x10..0x1A      ~ "QWERTYUIOPÅ"
+map -altgr +shift      0x1E..0x28 0x2B ~ "ASDFGHJKLÖÄ*"
+map -altgr +shift 0x56 0x2C..0x35      ~ ">ZXCVBNM;:_"
+
+#
+# Keys requiring AltGr
+#
+
+map +altgr -shift 0x03 ~ "@"
+map +altgr -shift 0x04 ~ "£"
+map +altgr -shift 0x05 ~ "$"
+map +altgr -shift 0x08 ~ "{"
+map +altgr -shift 0x09 ~ "["
+map +altgr -shift 0x0A ~ "]"
+map +altgr -shift 0x0B ~ "}"
+map +altgr -shift 0x0C ~ "\"
+
+map +altgr -shift 0x12 ~ "€"
+
+map +altgr -shift 0x56 ~ "|"
+map +altgr -shift 0x32 ~ "µ"
+
+#
+# Dead keys
+#
+
+map -altgr -shift 0x0D ~ 0xFE51 # Dead acute
+map -altgr +shift 0x0D ~ 0xFE50 # Dead grave
+map -altgr -shift 0x1B ~ 0xFE57 # Dead umlaut
+map -altgr +shift 0x1B ~ 0xFE52 # Dead circumflex
+map +altgr -shift 0x1B ~ 0xFE53 # Dead tilde
+
diff --git a/src/protocols/rdp/ogg_encoder.c b/src/protocols/rdp/ogg_encoder.c
deleted file mode 100644
index 8c6f97d..0000000
--- a/src/protocols/rdp/ogg_encoder.c
+++ /dev/null
@@ -1,211 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include <stdlib.h>
-
-#include <guacamole/client.h>
-#include <guacamole/protocol.h>
-
-#include <vorbis/vorbisenc.h>
-
-#include "audio.h"
-#include "ogg_encoder.h"
-
-void ogg_encoder_begin_handler(audio_stream* audio) {
-
-    /* Allocate stream state */
-    ogg_encoder_state* state = (ogg_encoder_state*)
-        malloc(sizeof(ogg_encoder_state));
-
-    /* Init state */
-    vorbis_info_init(&(state->info));
-    vorbis_encode_init_vbr(&(state->info), audio->channels, audio->rate, 0.4);
-
-    vorbis_analysis_init(&(state->vorbis_state), &(state->info));
-    vorbis_block_init(&(state->vorbis_state), &(state->vorbis_block));
-
-    vorbis_comment_init(&(state->comment));
-    vorbis_comment_add_tag(&(state->comment), "ENCODER", "libguac-client-rdp");
-
-    ogg_stream_init(&(state->ogg_state), rand());
-
-    /* Write headers */
-    {
-        ogg_packet header;
-        ogg_packet header_comm;
-        ogg_packet header_code;
-
-        vorbis_analysis_headerout(
-                &(state->vorbis_state),
-                &(state->comment),
-                &header, &header_comm, &header_code);
-
-        ogg_stream_packetin(&(state->ogg_state), &header);
-        ogg_stream_packetin(&(state->ogg_state), &header_comm);
-        ogg_stream_packetin(&(state->ogg_state), &header_code);
-
-        /* For each packet */
-        while (ogg_stream_flush(&(state->ogg_state), &(state->ogg_page)) != 0) {
-
-            /* Write packet header */
-            audio_stream_write_encoded(audio,
-                    state->ogg_page.header,
-                    state->ogg_page.header_len);
-
-            /* Write packet body */
-            audio_stream_write_encoded(audio,
-                    state->ogg_page.body,
-                    state->ogg_page.body_len);
-        }
-
-    }
-
-    audio->data = state;
-
-}
-
-void ogg_encoder_write_blocks(audio_stream* audio) {
-
-    /* Get state */
-    ogg_encoder_state* state = (ogg_encoder_state*) audio->data;
-
-    while (vorbis_analysis_blockout(&(state->vorbis_state),
-                &(state->vorbis_block)) == 1) {
-
-        /* Analyze */
-        vorbis_analysis(&(state->vorbis_block), NULL);
-        vorbis_bitrate_addblock(&(state->vorbis_block));
-
-        /* Flush Ogg pages */
-        while (vorbis_bitrate_flushpacket(&(state->vorbis_state),
-                    &(state->ogg_packet))) {
-
-            /* Weld packet into bitstream */
-            ogg_stream_packetin(&(state->ogg_state), &(state->ogg_packet));
-
-            /* Write out pages */
-            while (ogg_stream_pageout(&(state->ogg_state),
-                        &(state->ogg_page)) != 0) {
-
-                /* Write packet header */
-                audio_stream_write_encoded(audio,
-                        state->ogg_page.header,
-                        state->ogg_page.header_len);
-
-                /* Write packet body */
-                audio_stream_write_encoded(audio,
-                        state->ogg_page.body,
-                        state->ogg_page.body_len);
-
-                if (ogg_page_eos(&(state->ogg_page)))
-                    break;
-
-            }
-
-        }
-
-    }
-
-}
-
-void ogg_encoder_end_handler(audio_stream* audio) {
-
-    /* Get state */
-    ogg_encoder_state* state = (ogg_encoder_state*) audio->data;
-
-    /* Write end-of-stream */
-    vorbis_analysis_wrote(&(state->vorbis_state), 0);
-    ogg_encoder_write_blocks(audio);
-
-    /* Clean up encoder */
-    ogg_stream_clear(&(state->ogg_state));
-    vorbis_block_clear(&(state->vorbis_block));
-    vorbis_dsp_clear(&(state->vorbis_state));
-    vorbis_comment_clear(&(state->comment));
-    vorbis_info_clear(&(state->info));
-
-    /* Free stream state */
-    free(audio->data);
-
-}
-
-void ogg_encoder_write_handler(audio_stream* audio, 
-        unsigned char* pcm_data, int length) {
-
-    /* Get state */
-    ogg_encoder_state* state = (ogg_encoder_state*) audio->data;
-
-    /* Calculate samples */
-    int samples = length / audio->channels * 8 / audio->bps;
-    int i;
-
-    /* Get buffer */
-    float** buffer = vorbis_analysis_buffer(&(state->vorbis_state), samples);
-
-    signed char* readbuffer = (signed char*) pcm_data;
-
-    for (i=0; i<samples; i++) {
-
-        /* FIXME: For now, assume 2 channels, 16-bit */
-        int left  = ((readbuffer[i*4+1]<<8)|(0x00ff&(int)readbuffer[i*4]));
-        int right = ((readbuffer[i*4+3]<<8)|(0x00ff&(int)readbuffer[i*4+2]));
-
-        /* Store sample in buffer */
-        buffer[0][i] = left  / 32768.f;
-        buffer[1][i] = right / 32768.f;
-
-    }
-
-    /* Submit data */
-    vorbis_analysis_wrote(&(state->vorbis_state), samples);
-
-    /* Write data */
-    ogg_encoder_write_blocks(audio);
-
-}
-
-/* Encoder handlers */
-audio_encoder _ogg_encoder = {
-    .mimetype      = "audio/ogg",
-    .begin_handler = ogg_encoder_begin_handler,
-    .write_handler = ogg_encoder_write_handler,
-    .end_handler   = ogg_encoder_end_handler
-};
-
-/* Actual encoder */
-audio_encoder* ogg_encoder = &_ogg_encoder;
-
diff --git a/src/protocols/rdp/ogg_encoder.h b/src/protocols/rdp/ogg_encoder.h
deleted file mode 100644
index 78bfa6e..0000000
--- a/src/protocols/rdp/ogg_encoder.h
+++ /dev/null
@@ -1,67 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef __GUAC_OGG_ENCODER_H
-#define __GUAC_OGG_ENCODER_H
-
-#include "audio.h"
-
-#include <vorbis/vorbisenc.h>
-
-typedef struct ogg_encoder_state {
-
-    /**
-     * Ogg state
-     */
-    ogg_stream_state ogg_state;
-    ogg_page ogg_page;
-    ogg_packet ogg_packet;
-
-    /**
-     * Vorbis state
-     */
-    vorbis_info info;
-    vorbis_comment comment;
-    vorbis_dsp_state vorbis_state;
-    vorbis_block vorbis_block;
-
-} ogg_encoder_state;
-
-extern audio_encoder* ogg_encoder;
-
-#endif
-
diff --git a/src/protocols/rdp/rdp_bitmap.c b/src/protocols/rdp/rdp_bitmap.c
index be0c6b6..8de2fcc 100644
--- a/src/protocols/rdp/rdp_bitmap.c
+++ b/src/protocols/rdp/rdp_bitmap.c
@@ -1,54 +1,38 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
- * Matt Hortman
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <pthread.h>
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <cairo/cairo.h>
+#include "config.h"
 
-#include <guacamole/socket.h>
-#include <guacamole/client.h>
-#include <guacamole/protocol.h>
+#include "client.h"
+#include "guac_surface.h"
+#include "rdp_bitmap.h"
+#include "rdp_settings.h"
 
-#include <freerdp/freerdp.h>
-#include <freerdp/codec/color.h>
+#include <cairo/cairo.h>
 #include <freerdp/codec/bitmap.h>
+#include <freerdp/codec/color.h>
+#include <freerdp/freerdp.h>
+#include <guacamole/client.h>
+#include <guacamole/socket.h>
 
 #ifdef ENABLE_WINPR
 #include <winpr/wtypes.h>
@@ -56,66 +40,69 @@
 #include "compat/winpr-wtypes.h"
 #endif
 
-#include "client.h"
-#include "rdp_bitmap.h"
+#include <stdio.h>
+#include <stdlib.h>
 
 void guac_rdp_cache_bitmap(rdpContext* context, rdpBitmap* bitmap) {
 
     guac_client* client = ((rdp_freerdp_context*) context)->client;
     guac_socket* socket = client->socket; 
 
-    /* Allocate buffer */
+    /* Allocate surface */
     guac_layer* buffer = guac_client_alloc_buffer(client);
+    guac_common_surface* surface = guac_common_surface_alloc(client, socket,
+            buffer, bitmap->width, bitmap->height);
 
     /* Cache image data if present */
     if (bitmap->data != NULL) {
 
         /* Create surface from image data */
-        cairo_surface_t* surface = cairo_image_surface_create_for_data(
+        cairo_surface_t* image = cairo_image_surface_create_for_data(
             bitmap->data, CAIRO_FORMAT_RGB24,
             bitmap->width, bitmap->height, 4*bitmap->width);
 
         /* Send surface to buffer */
-        guac_protocol_send_png(socket,
-                GUAC_COMP_SRC, buffer, 0, 0, surface);
+        guac_common_surface_draw(surface, 0, 0, image);
 
         /* Free surface */
-        cairo_surface_destroy(surface);
+        cairo_surface_destroy(image);
 
     }
 
     /* Store buffer reference in bitmap */
-    ((guac_rdp_bitmap*) bitmap)->layer = buffer;
+    ((guac_rdp_bitmap*) bitmap)->buffer = buffer;
+    ((guac_rdp_bitmap*) bitmap)->surface = surface;
 
 }
 
-
 void guac_rdp_bitmap_new(rdpContext* context, rdpBitmap* bitmap) {
 
     /* Convert image data if present */
-    if (bitmap->data != NULL) {
-
-        /* Get client data */
-        guac_client* client = ((rdp_freerdp_context*) context)->client;
-        rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
+    if (bitmap->data != NULL && bitmap->bpp != 32) {
 
         /* Convert image data to 32-bit RGB */
         unsigned char* image_buffer = freerdp_image_convert(bitmap->data, NULL,
                 bitmap->width, bitmap->height,
-                client_data->settings.color_depth,
+                guac_rdp_get_depth(context->instance),
                 32, ((rdp_freerdp_context*) context)->clrconv);
 
         /* Free existing image, if any */
-        if (image_buffer != bitmap->data)
+        if (image_buffer != bitmap->data) {
+#ifdef FREERDP_BITMAP_REQUIRES_ALIGNED_MALLOC
+            _aligned_free(bitmap->data);
+#else
             free(bitmap->data);
+#endif
+        }
 
         /* Store converted image in bitmap */
         bitmap->data = image_buffer;
 
     }
 
-    /* No corresponding layer yet - caching is deferred. */
-    ((guac_rdp_bitmap*) bitmap)->layer = NULL;
+    /* No corresponding surface yet - caching is deferred. */
+    ((guac_rdp_bitmap*) bitmap)->buffer = NULL;
+    ((guac_rdp_bitmap*) bitmap)->surface = NULL;
 
     /* Start at zero usage */
     ((guac_rdp_bitmap*) bitmap)->used = 0;
@@ -125,39 +112,35 @@ void guac_rdp_bitmap_new(rdpContext* context, rdpBitmap* bitmap) {
 void guac_rdp_bitmap_paint(rdpContext* context, rdpBitmap* bitmap) {
 
     guac_client* client = ((rdp_freerdp_context*) context)->client;
-    guac_socket* socket = client->socket;
+    rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
+
+    guac_common_surface* surface = ((guac_rdp_bitmap*) bitmap)->surface;
 
     int width = bitmap->right - bitmap->left + 1;
     int height = bitmap->bottom - bitmap->top + 1;
 
     /* If not cached, cache if necessary */
-    if (((guac_rdp_bitmap*) bitmap)->layer == NULL
-            && ((guac_rdp_bitmap*) bitmap)->used >= 1)
+    if (surface == NULL && ((guac_rdp_bitmap*) bitmap)->used >= 1)
         guac_rdp_cache_bitmap(context, bitmap);
 
     /* If cached, retrieve from cache */
-    if (((guac_rdp_bitmap*) bitmap)->layer != NULL)
-        guac_protocol_send_copy(socket,
-                ((guac_rdp_bitmap*) bitmap)->layer,
-                0, 0, width, height,
-                GUAC_COMP_OVER,
-                GUAC_DEFAULT_LAYER, bitmap->left, bitmap->top);
+    if (surface != NULL)
+        guac_common_surface_copy(surface, 0, 0, width, height,
+                                 client_data->default_surface, bitmap->left, bitmap->top);
 
     /* Otherwise, draw with stored image data */
     else if (bitmap->data != NULL) {
 
         /* Create surface from image data */
-        cairo_surface_t* surface = cairo_image_surface_create_for_data(
+        cairo_surface_t* image = cairo_image_surface_create_for_data(
             bitmap->data, CAIRO_FORMAT_RGB24,
             width, height, 4*bitmap->width);
 
-        /* Send surface to buffer */
-        guac_protocol_send_png(socket,
-                GUAC_COMP_OVER, GUAC_DEFAULT_LAYER,
-                bitmap->left, bitmap->top, surface);
+        /* Draw image on default surface */
+        guac_common_surface_draw(client_data->default_surface, bitmap->left, bitmap->top, image);
 
         /* Free surface */
-        cairo_surface_destroy(surface);
+        cairo_surface_destroy(image);
 
     }
 
@@ -167,35 +150,42 @@ void guac_rdp_bitmap_paint(rdpContext* context, rdpBitmap* bitmap) {
 }
 
 void guac_rdp_bitmap_free(rdpContext* context, rdpBitmap* bitmap) {
+
     guac_client* client = ((rdp_freerdp_context*) context)->client;
+    guac_layer* buffer = ((guac_rdp_bitmap*) bitmap)->buffer;
+    guac_common_surface* surface = ((guac_rdp_bitmap*) bitmap)->surface;
+
+    /* If cached, free surface */
+    if (surface != NULL)
+        guac_common_surface_free(surface);
 
     /* If cached, free buffer */
-    if (((guac_rdp_bitmap*) bitmap)->layer != NULL)
-        guac_client_free_buffer(client, ((guac_rdp_bitmap*) bitmap)->layer);
+    if (buffer != NULL)
+        guac_client_free_buffer(client, buffer);
 
 }
 
 void guac_rdp_bitmap_setsurface(rdpContext* context, rdpBitmap* bitmap, BOOL primary) {
+
     guac_client* client = ((rdp_freerdp_context*) context)->client;
+    rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
 
     if (primary)
-        ((rdp_guac_client_data*) client->data)->current_surface
-            = GUAC_DEFAULT_LAYER;
+        client_data->current_surface = client_data->default_surface;
 
     else {
 
         /* Make sure that the recieved bitmap is not NULL before processing */
         if (bitmap == NULL) {
-            guac_client_log_info(client, "NULL bitmap found in bitmap_setsurface instruction.");
+            guac_client_log(client, GUAC_LOG_INFO, "NULL bitmap found in bitmap_setsurface instruction.");
             return;
         }
 
         /* If not available as a surface, make available. */
-        if (((guac_rdp_bitmap*) bitmap)->layer == NULL)
+        if (((guac_rdp_bitmap*) bitmap)->surface == NULL)
             guac_rdp_cache_bitmap(context, bitmap);
 
-        ((rdp_guac_client_data*) client->data)->current_surface 
-            = ((guac_rdp_bitmap*) bitmap)->layer;
+        client_data->current_surface = ((guac_rdp_bitmap*) bitmap)->surface;
 
     }
 
@@ -209,21 +199,70 @@ void guac_rdp_bitmap_decompress(rdpContext* context, rdpBitmap* bitmap, UINT8* d
         int width, int height, int bpp, int length, BOOL compressed, int codec_id) {
 #endif
 
-    int size = width * height * (bpp + 7) / 8;
+    int size = width * height * 4;
+
+#ifdef FREERDP_BITMAP_REQUIRES_ALIGNED_MALLOC
+    /* Free pre-existing data, if any (might be reused) */
+    if (bitmap->data != NULL)
+        _aligned_free(bitmap->data);
+
+    /* Allocate new data */
+    bitmap->data = (UINT8*) _aligned_malloc(size, 16);
+#else
+    /* Free pre-existing data, if any (might be reused) */
+    free(bitmap->data);
+
+    /* Allocate new data */
+    bitmap->data = (UINT8*) malloc(size);
+#endif
+
+    if (compressed) {
 
-    if (bitmap->data == NULL)
-        bitmap->data = (UINT8*) malloc(size);
-    else
-        bitmap->data = (UINT8*) realloc(bitmap->data, size);
+#ifdef HAVE_RDPCONTEXT_CODECS 
+        rdpCodecs* codecs = context->codecs;
 
-    if (compressed)
+        /* Decode as interleaved if less than 32 bits per pixel */
+        if (bpp < 32) {
+            freerdp_client_codecs_prepare(codecs, FREERDP_CODEC_INTERLEAVED);
+#ifdef INTERLEAVED_DECOMPRESS_TAKES_PALETTE
+            interleaved_decompress(codecs->interleaved, data, length, bpp,
+                &(bitmap->data), PIXEL_FORMAT_XRGB32, -1, 0, 0, width, height,
+                (BYTE*) ((rdp_freerdp_context*) context)->palette);
+            bitmap->bpp = 32;
+#else
+            interleaved_decompress(codecs->interleaved, data, length, bpp,
+                &(bitmap->data), PIXEL_FORMAT_XRGB32, -1, 0, 0, width, height);
+            bitmap->bpp = bpp;
+#endif
+        }
+
+        /* Otherwise, decode as planar */
+        else {
+            freerdp_client_codecs_prepare(codecs, FREERDP_CODEC_PLANAR);
+#ifdef PLANAR_DECOMPRESS_CAN_FLIP
+            planar_decompress(codecs->planar, data, length,
+                &(bitmap->data), PIXEL_FORMAT_XRGB32, -1, 0, 0, width, height,
+                TRUE);
+            bitmap->bpp = 32;
+#else
+            planar_decompress(codecs->planar, data, length,
+                &(bitmap->data), PIXEL_FORMAT_XRGB32, -1, 0, 0, width, height);
+            bitmap->bpp = bpp;
+#endif
+        }
+#else
         bitmap_decompress(data, bitmap->data, width, height, length, bpp, bpp);
-    else
+        bitmap->bpp = bpp;
+#endif
+
+    }
+    else {
         freerdp_image_flip(data, bitmap->data, width, height, bpp);
+        bitmap->bpp = bpp;
+    }
 
     bitmap->compressed = FALSE;
     bitmap->length = size;
-    bitmap->bpp = bpp;
 
 }
 
diff --git a/src/protocols/rdp/rdp_bitmap.h b/src/protocols/rdp/rdp_bitmap.h
index 6c31711..cab99a3 100644
--- a/src/protocols/rdp/rdp_bitmap.h
+++ b/src/protocols/rdp/rdp_bitmap.h
@@ -1,44 +1,34 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Original Code is libguac-client-rdp.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef _GUAC_RDP_RDP_BITMAP_H
 #define _GUAC_RDP_RDP_BITMAP_H
 
+#include "config.h"
+#include "guac_surface.h"
+
 #include <freerdp/freerdp.h>
+#include <guacamole/layer.h>
 
 #ifdef ENABLE_WINPR
 #include <winpr/wtypes.h>
@@ -46,8 +36,6 @@
 #include "compat/winpr-wtypes.h"
 #endif
 
-#include <guacamole/protocol.h>
-
 typedef struct guac_rdp_bitmap {
 
     /**
@@ -56,9 +44,14 @@ typedef struct guac_rdp_bitmap {
     rdpBitmap bitmap;
 
     /**
-     * Guacamole layer containing cached image data.
+     * The allocated buffer which backs this bitmap.
+     */
+    guac_layer* buffer;
+
+    /**
+     * Surface containing cached image data.
      */
-    guac_layer* layer;
+    guac_common_surface* surface;
 
     /**
      * The number of times a bitmap has been used.
diff --git a/src/protocols/rdp/rdp_cliprdr.c b/src/protocols/rdp/rdp_cliprdr.c
index 328af2f..cf09a29 100644
--- a/src/protocols/rdp/rdp_cliprdr.c
+++ b/src/protocols/rdp/rdp_cliprdr.c
@@ -1,50 +1,36 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
+#include "config.h"
+
+#include "client.h"
+#include "rdp_cliprdr.h"
+#include "guac_clipboard.h"
+#include "guac_iconv.h"
 
-#include <freerdp/freerdp.h>
 #include <freerdp/channels/channels.h>
+#include <freerdp/freerdp.h>
 #include <freerdp/utils/event.h>
-
-#ifdef HAVE_FREERDP_CLIENT_CLIPRDR_H
-#include <freerdp/client/cliprdr.h>
-#else
-#include "compat/client-cliprdr.h"
-#endif
+#include <guacamole/client.h>
 
 #ifdef ENABLE_WINPR
 #include <winpr/wtypes.h>
@@ -52,11 +38,14 @@
 #include "compat/winpr-wtypes.h"
 #endif
 
-#include <guacamole/client.h>
-#include <guacamole/protocol.h>
+#ifdef HAVE_FREERDP_CLIENT_CLIPRDR_H
+#include <freerdp/client/cliprdr.h>
+#else
+#include "compat/client-cliprdr.h"
+#endif
 
-#include "client.h"
-#include "rdp_cliprdr.h"
+#include <stdlib.h>
+#include <string.h>
 
 void guac_rdp_process_cliprdr_event(guac_client* client, wMessage* event) {
 
@@ -87,11 +76,11 @@ void guac_rdp_process_cliprdr_event(guac_client* client, wMessage* event) {
 
             default:
 #ifdef LEGACY_EVENT
-                guac_client_log_info(client,
+                guac_client_log(client, GUAC_LOG_INFO,
                         "Unknown cliprdr event type: 0x%x",
                         event->event_type);
 #else
-                guac_client_log_info(client,
+                guac_client_log(client, GUAC_LOG_INFO,
                         "Unknown cliprdr event type: 0x%x",
                         GetMessageType(event->id));
 #endif
@@ -114,19 +103,43 @@ void guac_rdp_process_cb_monitor_ready(guac_client* client, wMessage* event) {
     /* Received notification of clipboard support. */
 
     /* Respond with supported format list */
-    format_list->formats = (UINT32*) malloc(sizeof(UINT32));
+    format_list->formats = (UINT32*) malloc(sizeof(UINT32)*2);
     format_list->formats[0] = CB_FORMAT_TEXT;
-    format_list->num_formats = 1;
+    format_list->formats[1] = CB_FORMAT_UNICODETEXT;
+    format_list->num_formats = 2;
 
     freerdp_channels_send_event(channels, (wMessage*) format_list);
 
 }
 
+/**
+ * Sends a clipboard data request for the given format.
+ */
+static void __guac_rdp_cb_request_format(guac_client* client, int format) {
+
+    rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
+    rdpChannels* channels = client_data->rdp_inst->context->channels;
+
+    /* Create new data request */
+    RDP_CB_DATA_REQUEST_EVENT* data_request =
+        (RDP_CB_DATA_REQUEST_EVENT*) freerdp_event_new(
+                CliprdrChannel_Class,
+                CliprdrChannel_DataRequest,
+                NULL, NULL);
+
+    /* Set to requested format */
+    client_data->requested_clipboard_format = format;
+    data_request->format = format;
+
+    /* Send request */
+    freerdp_channels_send_event(channels, (wMessage*) data_request);
+
+}
+
 void guac_rdp_process_cb_format_list(guac_client* client,
         RDP_CB_FORMAT_LIST_EVENT* event) {
 
-    rdpChannels* channels = 
-        ((rdp_guac_client_data*) client->data)->rdp_inst->context->channels;
+    int formats = 0;
 
     /* Received notification of available data */
 
@@ -134,93 +147,118 @@ void guac_rdp_process_cb_format_list(guac_client* client,
     for (i=0; i<event->num_formats; i++) {
 
         /* If plain text available, request it */
-        if (event->formats[i] == CB_FORMAT_TEXT) {
-
-            /* Create new data request */
-            RDP_CB_DATA_REQUEST_EVENT* data_request =
-                (RDP_CB_DATA_REQUEST_EVENT*) freerdp_event_new(
-                        CliprdrChannel_Class,
-                        CliprdrChannel_DataRequest,
-                        NULL, NULL);
+        if (event->formats[i] == CB_FORMAT_TEXT)
+            formats |= GUAC_RDP_CLIPBOARD_FORMAT_CP1252;
+        else if (event->formats[i] == CB_FORMAT_UNICODETEXT)
+            formats |= GUAC_RDP_CLIPBOARD_FORMAT_UTF16;
 
-            /* We want plain text */
-            data_request->format = CB_FORMAT_TEXT;
-
-            /* Send request */
-            freerdp_channels_send_event(channels, (wMessage*) data_request);
-            return;
+    }
 
-        }
+    /* Prefer Unicode to plain text */
+    if (formats & GUAC_RDP_CLIPBOARD_FORMAT_UTF16) {
+        __guac_rdp_cb_request_format(client, CB_FORMAT_UNICODETEXT);
+        return;
+    }
 
+    /* Use plain text if Unicode unavailable */
+    if (formats & GUAC_RDP_CLIPBOARD_FORMAT_CP1252) {
+        __guac_rdp_cb_request_format(client, CB_FORMAT_TEXT);
+        return;
     }
 
-    /* Otherwise, no supported data available */
-    guac_client_log_info(client, "Ignoring unsupported clipboard data");
+    /* Ignore if no supported format available */
+    guac_client_log(client, GUAC_LOG_INFO, "Ignoring unsupported clipboard data");
 
 }
 
 void guac_rdp_process_cb_data_request(guac_client* client,
         RDP_CB_DATA_REQUEST_EVENT* event) {
 
-    rdpChannels* channels = 
-        ((rdp_guac_client_data*) client->data)->rdp_inst->context->channels;
+    rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
+    rdpChannels* channels = client_data->rdp_inst->context->channels;
 
-    /* If text requested, send clipboard text contents */
-    if (event->format == CB_FORMAT_TEXT) {
+    guac_iconv_write* writer;
+    const char* input = client_data->clipboard->buffer;
+    char* output = malloc(GUAC_RDP_CLIPBOARD_MAX_LENGTH);
 
-        /* Get clipboard data */
-        const char* clipboard =
-            ((rdp_guac_client_data*) client->data)->clipboard;
+    RDP_CB_DATA_RESPONSE_EVENT* data_response;
 
-        /* Create new data response */
-        RDP_CB_DATA_RESPONSE_EVENT* data_response =
-            (RDP_CB_DATA_RESPONSE_EVENT*) freerdp_event_new(
-                    CliprdrChannel_Class,
-                    CliprdrChannel_DataResponse,
-                    NULL, NULL);
+    /* Determine output encoding */
+    switch (event->format) {
 
-        /* Set data and length */
-        if (clipboard != NULL) {
-            data_response->data = (UINT8*) strdup(clipboard);
-            data_response->size = strlen(clipboard) + 1;
-        }
-        else {
-            data_response->data = (UINT8*) strdup("");
-            data_response->size = 1;
-        }
+        case CB_FORMAT_TEXT:
+            writer = GUAC_WRITE_CP1252;
+            break;
 
-        /* Send response */
-        freerdp_channels_send_event(channels, (wMessage*) data_response);
+        case CB_FORMAT_UNICODETEXT:
+            writer = GUAC_WRITE_UTF16;
+            break;
+
+        default:
+            guac_client_log(client, GUAC_LOG_ERROR, 
+                    "Server requested unsupported clipboard data type");
+            return;
 
     }
 
-    /* Otherwise ... failure */
-    else
-        guac_client_log_error(client, 
-                "Server requested unsupported clipboard data type");
+    /* Create new data response */
+    data_response = (RDP_CB_DATA_RESPONSE_EVENT*) freerdp_event_new(
+                CliprdrChannel_Class,
+                CliprdrChannel_DataResponse,
+                NULL, NULL);
+
+    /* Set data and size */
+    data_response->data = (BYTE*) output;
+    guac_iconv(GUAC_READ_UTF8, &input, client_data->clipboard->length,
+               writer, &output, GUAC_RDP_CLIPBOARD_MAX_LENGTH);
+    data_response->size = ((BYTE*) output) - data_response->data;
+
+    /* Send response */
+    freerdp_channels_send_event(channels, (wMessage*) data_response);
 
 }
 
 void guac_rdp_process_cb_data_response(guac_client* client,
         RDP_CB_DATA_RESPONSE_EVENT* event) {
 
-    /* Received clipboard data */
-    if (event->data[event->size - 1] == '\0') {
+    rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
+    char received_data[GUAC_RDP_CLIPBOARD_MAX_LENGTH];
+
+    guac_iconv_read* reader;
+    const char* input = (char*) event->data;
+    char* output = received_data;
 
-        /* Free existing data */
-        free(((rdp_guac_client_data*) client->data)->clipboard);
+    /* Find correct source encoding */
+    switch (client_data->requested_clipboard_format) {
+
+        /* Non-Unicode */
+        case CB_FORMAT_TEXT:
+            reader = GUAC_READ_CP1252;
+            break;
+
+        /* Unicode (UTF-16) */
+        case CB_FORMAT_UNICODETEXT:
+            reader = GUAC_READ_UTF16;
+            break;
+
+        default:
+            guac_client_log(client, GUAC_LOG_ERROR, "Requested clipboard data in "
+                    "unsupported format %i",
+                    client_data->requested_clipboard_format);
+            return;
+
+    }
 
-        /* Store clipboard data */
-        ((rdp_guac_client_data*) client->data)->clipboard =
-            strdup((char*) event->data);
+    /* Convert send clipboard data */
+    if (guac_iconv(reader, &input, event->size,
+            GUAC_WRITE_UTF8, &output, sizeof(received_data))) {
 
-        /* Send clipboard data */
-        guac_protocol_send_clipboard(client->socket, (char*) event->data);
+        int length = strnlen(received_data, sizeof(received_data));
+        guac_common_clipboard_reset(client_data->clipboard, "text/plain");
+        guac_common_clipboard_append(client_data->clipboard, received_data, length);
+        guac_common_clipboard_send(client_data->clipboard, client);
 
     }
-    else
-        guac_client_log_error(client,
-                "Clipboard data missing null terminator");
 
 }
 
diff --git a/src/protocols/rdp/rdp_cliprdr.h b/src/protocols/rdp/rdp_cliprdr.h
index 8e83297..e8ed53f 100644
--- a/src/protocols/rdp/rdp_cliprdr.h
+++ b/src/protocols/rdp/rdp_cliprdr.h
@@ -1,50 +1,54 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Original Code is libguac-client-rdp.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef __GUAC_RDP_RDP_CLIPRDR_H
 #define __GUAC_RDP_RDP_CLIPRDR_H
 
+#include "config.h"
+
+#include <guacamole/client.h>
+
 #ifdef ENABLE_WINPR
 #include <winpr/stream.h>
 #else
 #include "compat/winpr-stream.h"
 #endif
 
-#include <freerdp/freerdp.h>
+#ifdef HAVE_FREERDP_CLIENT_CLIPRDR_H
+#include <freerdp/client/cliprdr.h>
+#else
+#include "compat/client-cliprdr.h"
+#endif
+
+/**
+ * Clipboard format for text encoded in Windows CP1252.
+ */
+#define GUAC_RDP_CLIPBOARD_FORMAT_CP1252 1
+
+/**
+ * Clipboard format for text encoded in UTF-16.
+ */
+#define GUAC_RDP_CLIPBOARD_FORMAT_UTF16 2
 
 void guac_rdp_process_cliprdr_event(guac_client* client, wMessage* event);
 void guac_rdp_process_cb_monitor_ready(guac_client* client, wMessage* event);
diff --git a/src/protocols/rdp/rdp_color.c b/src/protocols/rdp/rdp_color.c
new file mode 100644
index 0000000..9828d3b
--- /dev/null
+++ b/src/protocols/rdp/rdp_color.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "client.h"
+#include "rdp_settings.h"
+
+#include <freerdp/codec/color.h>
+#include <freerdp/freerdp.h>
+
+#ifdef ENABLE_WINPR
+#include <winpr/wtypes.h>
+#else
+#include "compat/winpr-wtypes.h"
+#endif
+
+UINT32 guac_rdp_convert_color(rdpContext* context, UINT32 color) {
+
+#ifdef HAVE_FREERDP_CONVERT_GDI_ORDER_COLOR
+    UINT32* palette = ((rdp_freerdp_context*) context)->palette;
+
+    /* Convert given color to ARGB32 */
+    return freerdp_convert_gdi_order_color(color,
+            guac_rdp_get_depth(context->instance), PIXEL_FORMAT_ARGB32,
+            (BYTE*) palette);
+
+#elif defined(HAVE_FREERDP_COLOR_CONVERT_DRAWING_ORDER_COLOR_TO_GDI_COLOR)
+    CLRCONV* clrconv = ((rdp_freerdp_context*) context)->clrconv;
+
+    /* Convert given color to ARGB32 */
+    return freerdp_color_convert_drawing_order_color_to_gdi_color(color,
+            guac_rdp_get_depth(context->instance), clrconv);
+
+#else
+    CLRCONV* clrconv = ((rdp_freerdp_context*) context)->clrconv;
+
+    /* Convert given color to ARGB32 */
+    return freerdp_color_convert_var(color,
+            guac_rdp_get_depth(context->instance), 32,
+            clrconv);
+#endif
+
+}
+
diff --git a/src/protocols/rdp/rdp_color.h b/src/protocols/rdp/rdp_color.h
new file mode 100644
index 0000000..6c47450
--- /dev/null
+++ b/src/protocols/rdp/rdp_color.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GUAC_RDP_COLOR_H
+#define GUAC_RDP_COLOR_H
+
+#include <freerdp/freerdp.h>
+
+#ifdef ENABLE_WINPR
+#include <winpr/wtypes.h>
+#else
+#include "compat/winpr-wtypes.h"
+#endif
+
+/**
+ * Converts the given color to ARGB32. The color given may be an index
+ * referring to the palette, a 16-bit or 32-bit color, etc. all depending on
+ * the current color depth of the RDP session.
+ */
+UINT32 guac_rdp_convert_color(rdpContext* context, UINT32 color);
+
+#endif
+
diff --git a/src/protocols/rdp/rdp_disp.c b/src/protocols/rdp/rdp_disp.c
new file mode 100644
index 0000000..f13e092
--- /dev/null
+++ b/src/protocols/rdp/rdp_disp.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+#include "client.h"
+
+#include <freerdp/freerdp.h>
+#include <freerdp/client/disp.h>
+#include <guacamole/client.h>
+#include <guacamole/timestamp.h>
+
+guac_rdp_disp* guac_rdp_disp_alloc() {
+
+    guac_rdp_disp* disp = malloc(sizeof(guac_rdp_disp));
+
+    /* Not yet connected */
+    disp->disp = NULL;
+
+    /* No requests have been made */
+    disp->last_request = 0;
+    disp->requested_width  = 0;
+    disp->requested_height = 0;
+
+    return disp;
+
+}
+
+void guac_rdp_disp_free(guac_rdp_disp* disp) {
+    free(disp);
+}
+
+void guac_rdp_disp_load_plugin(rdpContext* context) {
+
+#ifdef HAVE_RDPSETTINGS_SUPPORTDISPLAYCONTROL
+    context->settings->SupportDisplayControl = TRUE;
+#endif
+
+    /* Add "disp" channel */
+    ADDIN_ARGV* args = malloc(sizeof(ADDIN_ARGV));
+    args->argc = 1;
+    args->argv = malloc(sizeof(char**) * 1);
+    args->argv[0] = strdup("disp");
+    freerdp_dynamic_channel_collection_add(context->settings, args);
+
+}
+
+void guac_rdp_disp_connect(guac_rdp_disp* guac_disp, DispClientContext* disp) {
+    guac_disp->disp = disp;
+}
+
+/**
+ * Fits a given dimension within the allowed bounds for Display Update
+ * messages, adjusting the other dimension such that aspect ratio is
+ * maintained.
+ *
+ * @param a The dimension to fit within allowed bounds.
+ *
+ * @param b
+ *     The other dimension to adjust if and only if necessary to preserve
+ *     aspect ratio.
+ */
+static void guac_rdp_disp_fit(int* a, int* b) {
+
+    int a_value = *a;
+    int b_value = *b;
+
+    /* Ensure first dimension is within allowed range */
+    if (a_value < GUAC_RDP_DISP_MIN_SIZE) {
+
+        /* Adjust other dimension to maintain aspect ratio */
+        int adjusted_b = b_value * GUAC_RDP_DISP_MIN_SIZE / a_value;
+        if (adjusted_b > GUAC_RDP_DISP_MAX_SIZE)
+            adjusted_b = GUAC_RDP_DISP_MAX_SIZE;
+
+        *a = GUAC_RDP_DISP_MIN_SIZE;
+        *b = adjusted_b;
+
+    }
+    else if (a_value > GUAC_RDP_DISP_MAX_SIZE) {
+
+        /* Adjust other dimension to maintain aspect ratio */
+        int adjusted_b = b_value * GUAC_RDP_DISP_MAX_SIZE / a_value;
+        if (adjusted_b < GUAC_RDP_DISP_MIN_SIZE)
+            adjusted_b = GUAC_RDP_DISP_MIN_SIZE;
+
+        *a = GUAC_RDP_DISP_MAX_SIZE;
+        *b = adjusted_b;
+
+    }
+
+}
+
+void guac_rdp_disp_set_size(guac_rdp_disp* disp, rdpContext* context,
+        int width, int height) {
+
+    /* Fit width within bounds, adjusting height to maintain aspect ratio */
+    guac_rdp_disp_fit(&width, &height);
+
+    /* Fit height within bounds, adjusting width to maintain aspect ratio */
+    guac_rdp_disp_fit(&height, &width);
+
+    /* Width must be even */
+    if (width % 2 == 1)
+        width -= 1;
+
+    /* Store deferred size */
+    disp->requested_width = width;
+    disp->requested_height = height;
+
+    /* Send display update notification if possible */
+    guac_rdp_disp_update_size(disp, context);
+
+}
+
+void guac_rdp_disp_update_size(guac_rdp_disp* disp, rdpContext* context) {
+
+    guac_client* client = ((rdp_freerdp_context*) context)->client;
+
+    /* Send display update notification if display channel is connected */
+    if (disp->disp == NULL)
+        return;
+
+    int width = disp->requested_width;
+    int height = disp->requested_height;
+
+    DISPLAY_CONTROL_MONITOR_LAYOUT monitors[1] = {{
+        .Flags  = 0x1, /* DISPLAYCONTROL_MONITOR_PRIMARY */
+        .Left = 0,
+        .Top = 0,
+        .Width  = width,
+        .Height = height,
+        .PhysicalWidth = 0,
+        .PhysicalHeight = 0,
+        .Orientation = 0,
+        .DesktopScaleFactor = 0,
+        .DeviceScaleFactor = 0
+    }};
+
+    guac_timestamp now = guac_timestamp_current();
+
+    /* Limit display update frequency */
+    if (disp->last_request != 0
+            && now - disp->last_request <= GUAC_RDP_DISP_UPDATE_INTERVAL)
+        return;
+
+    /* Do NOT send requests unless the size will change */
+    if (width == guac_rdp_get_width(context->instance)
+            && height == guac_rdp_get_height(context->instance))
+        return;
+
+    guac_client_log(client, GUAC_LOG_DEBUG,
+            "Resizing remote display to %ix%i",
+            width, height);
+
+    disp->last_request = now;
+    disp->disp->SendMonitorLayout(disp->disp, 1, monitors);
+
+}
+
diff --git a/src/protocols/rdp/rdp_disp.h b/src/protocols/rdp/rdp_disp.h
new file mode 100644
index 0000000..375f476
--- /dev/null
+++ b/src/protocols/rdp/rdp_disp.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GUAC_RDP_DISP_H
+#define GUAC_RDP_DISP_H
+
+#include <freerdp/client/disp.h>
+#include <freerdp/freerdp.h>
+
+/**
+ * The minimum value for width or height, in pixels.
+ */
+#define GUAC_RDP_DISP_MIN_SIZE 200
+
+/**
+ * The maximum value for width or height, in pixels.
+ */
+#define GUAC_RDP_DISP_MAX_SIZE 8192
+
+/**
+ * The minimum amount of time that must elapse between display size updates,
+ * in milliseconds.
+ */
+#define GUAC_RDP_DISP_UPDATE_INTERVAL 500
+
+/**
+ * Display size update module.
+ */
+typedef struct guac_rdp_disp {
+
+    /**
+     * Display control interface.
+     */
+    DispClientContext* disp;
+
+    /**
+     * The timestamp of the last display update request, or 0 if no request
+     * has been sent yet.
+     */
+    guac_timestamp last_request;
+
+    /**
+     * The last requested screen width, in pixels.
+     */
+    int requested_width;
+
+    /**
+     * The last requested screen height, in pixels.
+     */
+    int requested_height;
+
+} guac_rdp_disp;
+
+/**
+ * Allocates a new display update module, which will ultimately control the
+ * display update channel once conected.
+ *
+ * @return A new display update module.
+ */
+guac_rdp_disp* guac_rdp_disp_alloc();
+
+/**
+ * Frees the given display update module.
+ *
+ * @param disp The display update module to free.
+ */
+void guac_rdp_disp_free(guac_rdp_disp* disp);
+
+/**
+ * Loads the "disp" plugin for FreeRDP. It is still up to external code to
+ * detect when the "disp" channel is connected, and update the guac_rdp_disp
+ * with a call to guac_rdp_disp_connect().
+ *
+ * @param context The rdpContext associated with the active RDP session.
+ */
+void guac_rdp_disp_load_plugin(rdpContext* context);
+
+/**
+ * Stores the given DispClientContext within the given guac_rdp_disp, such that
+ * display updates can be properly sent. Until this is called, changes to the
+ * display size will be deferred.
+ *
+ * @param guac_disp The display update module to associate with the connected
+ *                  display update channel.
+ * @param disp The DispClientContext associated by FreeRDP with the connected
+ *             display update channel.
+ */
+void guac_rdp_disp_connect(guac_rdp_disp* guac_disp, DispClientContext* disp);
+
+/**
+ * Requests a display size update, which may then be sent immediately to the
+ * RDP server. If an update was recently sent, this update may be delayed until
+ * the RDP server has had time to settle. The width/height values provided may
+ * be automatically altered to comply with the restrictions imposed by the
+ * display update channel.
+ *
+ * @param disp The display update module which should maintain the requested
+ *             size, sending the corresponding display update request when
+ *             appropriate.
+ * @param context The rdpContext associated with the active RDP session.
+ * @param width The desired display width, in pixels. Due to the restrictions
+ *              of the RDP display update channel, this will be contrained to
+ *              the range of 200 through 8192 inclusive, and rounded down to
+ *              the nearest even number.
+ * @param height The desired display height, in pixels. Due to the restrictions
+ *               of the RDP display update channel, this will be contrained to
+ *               the range of 200 through 8192 inclusive.
+ */
+void guac_rdp_disp_set_size(guac_rdp_disp* disp, rdpContext* context,
+        int width, int height);
+
+/**
+ * Sends an actual display update request to the RDP server based on previous
+ * calls to guac_rdp_disp_set_size(). If an update was recently sent, the
+ * update may be delayed until a future call to this function.
+ *
+ * @param disp The display update module which should track the update request.
+ * @param context The rdpContext associated with the active RDP session.
+ */
+void guac_rdp_disp_update_size(guac_rdp_disp* disp, rdpContext* context);
+
+#endif
+
diff --git a/src/protocols/rdp/rdp_fs.c b/src/protocols/rdp/rdp_fs.c
new file mode 100644
index 0000000..e6ccbdf
--- /dev/null
+++ b/src/protocols/rdp/rdp_fs.c
@@ -0,0 +1,775 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "rdp_fs.h"
+#include "rdp_status.h"
+#include "rdp_stream.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <guacamole/object.h>
+#include <guacamole/pool.h>
+
+guac_rdp_fs* guac_rdp_fs_alloc(guac_client* client, const char* drive_path,
+        int create_drive_path) {
+
+    /* Create drive path if it does not exist */
+    if (create_drive_path) {
+        guac_client_log(client, GUAC_LOG_DEBUG,
+               "%s: Creating directory \"%s\" if necessary.",
+               __func__, drive_path);
+
+        /* Log error if directory creation fails */
+        if (mkdir(drive_path, S_IRWXU) && errno != EEXIST) {
+            guac_client_log(client, GUAC_LOG_ERROR,
+                    "Unable to create directory \"%s\": %s",
+                    drive_path, strerror(errno));
+        }
+    }
+
+    guac_rdp_fs* fs = malloc(sizeof(guac_rdp_fs));
+
+    fs->client = client;
+    fs->object = guac_client_alloc_object(client);
+    fs->object->get_handler = guac_rdp_download_get_handler;
+    fs->object->put_handler = guac_rdp_upload_put_handler;
+
+    fs->drive_path = strdup(drive_path);
+    fs->file_id_pool = guac_pool_alloc(0);
+    fs->open_files = 0;
+
+    return fs;
+
+}
+
+void guac_rdp_fs_free(guac_rdp_fs* fs) {
+    guac_client_free_object(fs->client, fs->object);
+    guac_pool_free(fs->file_id_pool);
+    free(fs->drive_path);
+    free(fs);
+}
+
+/**
+ * Translates an absolute Windows virtual_path to an absolute virtual_path
+ * which is within the "drive virtual_path" specified in the connection
+ * settings.
+ */
+static void __guac_rdp_fs_translate_path(guac_rdp_fs* fs,
+        const char* virtual_path, char* real_path) {
+
+    /* Get drive path */
+    char* drive_path = fs->drive_path;
+
+    int i;
+
+    /* Start with path from settings */
+    for (i=0; i<GUAC_RDP_FS_MAX_PATH-1; i++) {
+
+        /* Break on end-of-string */
+        char c = *(drive_path++);
+        if (c == 0)
+            break;
+
+        /* Copy character */
+        *(real_path++) = c;
+
+    }
+
+    /* Translate path */
+    for (; i<GUAC_RDP_FS_MAX_PATH-1; i++) {
+
+        /* Stop at end of string */
+        char c = *(virtual_path++);
+        if (c == 0)
+            break;
+
+        /* Translate backslashes to forward slashes */
+        if (c == '\\')
+            c = '/';
+
+        /* Store in real path buffer */
+        *(real_path++)= c;
+
+    }
+
+    /* Null terminator */
+    *real_path = 0;
+
+}
+
+int guac_rdp_fs_get_errorcode(int err) {
+
+    /* Translate errno codes to GUAC_RDP_FS codes */
+    if (err == ENFILE)  return GUAC_RDP_FS_ENFILE;
+    if (err == ENOENT)  return GUAC_RDP_FS_ENOENT;
+    if (err == ENOTDIR) return GUAC_RDP_FS_ENOTDIR;
+    if (err == ENOSPC)  return GUAC_RDP_FS_ENOSPC;
+    if (err == EISDIR)  return GUAC_RDP_FS_EISDIR;
+    if (err == EACCES)  return GUAC_RDP_FS_EACCES;
+    if (err == EEXIST)  return GUAC_RDP_FS_EEXIST;
+    if (err == EINVAL)  return GUAC_RDP_FS_EINVAL;
+    if (err == ENOSYS)  return GUAC_RDP_FS_ENOSYS;
+    if (err == ENOTSUP) return GUAC_RDP_FS_ENOTSUP;
+
+    /* Default to invalid parameter */
+    return GUAC_RDP_FS_EINVAL;
+
+}
+
+int guac_rdp_fs_get_status(int err) {
+
+    /* Translate GUAC_RDP_FS error code to RDPDR status code */
+    if (err == GUAC_RDP_FS_ENFILE)  return STATUS_NO_MORE_FILES;
+    if (err == GUAC_RDP_FS_ENOENT)  return STATUS_NO_SUCH_FILE;
+    if (err == GUAC_RDP_FS_ENOTDIR) return STATUS_NOT_A_DIRECTORY;
+    if (err == GUAC_RDP_FS_ENOSPC)  return STATUS_DISK_FULL;
+    if (err == GUAC_RDP_FS_EISDIR)  return STATUS_FILE_IS_A_DIRECTORY;
+    if (err == GUAC_RDP_FS_EACCES)  return STATUS_ACCESS_DENIED;
+    if (err == GUAC_RDP_FS_EEXIST)  return STATUS_OBJECT_NAME_COLLISION;
+    if (err == GUAC_RDP_FS_EINVAL)  return STATUS_INVALID_PARAMETER;
+    if (err == GUAC_RDP_FS_ENOSYS)  return STATUS_NOT_IMPLEMENTED;
+    if (err == GUAC_RDP_FS_ENOTSUP) return STATUS_NOT_SUPPORTED;
+
+    /* Default to invalid parameter */
+    return STATUS_INVALID_PARAMETER;
+
+}
+
+int guac_rdp_fs_open(guac_rdp_fs* fs, const char* path,
+        int access, int file_attributes, int create_disposition,
+        int create_options) {
+
+    char real_path[GUAC_RDP_FS_MAX_PATH];
+    char normalized_path[GUAC_RDP_FS_MAX_PATH];
+
+    struct stat file_stat;
+    int fd;
+    int file_id;
+    guac_rdp_fs_file* file;
+
+    int flags = 0;
+
+    guac_client_log(fs->client, GUAC_LOG_DEBUG,
+            "%s: path=\"%s\", access=0x%x, file_attributes=0x%x, "
+            "create_disposition=0x%x, create_options=0x%x",
+            __func__, path, access, file_attributes,
+            create_disposition, create_options);
+
+    /* If no files available, return too many open */
+    if (fs->open_files >= GUAC_RDP_FS_MAX_FILES) {
+        guac_client_log(fs->client, GUAC_LOG_DEBUG,
+                "%s: Too many open files.",
+                __func__, path);
+        return GUAC_RDP_FS_ENFILE;
+    }
+
+    /* If path empty, transform to root path */
+    if (path[0] == '\0')
+        path = "\\";
+
+    /* If path is relative, the file does not exist */
+    else if (path[0] != '\\' && path[0] != '/') {
+        guac_client_log(fs->client, GUAC_LOG_DEBUG,
+                "%s: Access denied - supplied path \"%s\" is relative.",
+                __func__, path);
+        return GUAC_RDP_FS_ENOENT;
+    }
+
+    /* Translate access into flags */
+    if (access & ACCESS_GENERIC_ALL)
+        flags = O_RDWR;
+    else if ((access & ( ACCESS_GENERIC_WRITE
+                       | ACCESS_FILE_WRITE_DATA
+                       | ACCESS_FILE_APPEND_DATA))
+          && (access & (ACCESS_GENERIC_READ  | ACCESS_FILE_READ_DATA)))
+        flags = O_RDWR;
+    else if (access & ( ACCESS_GENERIC_WRITE
+                      | ACCESS_FILE_WRITE_DATA
+                      | ACCESS_FILE_APPEND_DATA))
+        flags = O_WRONLY;
+    else
+        flags = O_RDONLY;
+
+    /* Normalize path, return no-such-file if invalid  */
+    if (guac_rdp_fs_normalize_path(path, normalized_path)) {
+        guac_client_log(fs->client, GUAC_LOG_DEBUG,
+                "%s: Normalization of path \"%s\" failed.", __func__, path);
+        return GUAC_RDP_FS_ENOENT;
+    }
+
+    guac_client_log(fs->client, GUAC_LOG_DEBUG,
+            "%s: Normalized path \"%s\" to \"%s\".",
+            __func__, path, normalized_path);
+
+    /* Translate normalized path to real path */
+    __guac_rdp_fs_translate_path(fs, normalized_path, real_path);
+
+    guac_client_log(fs->client, GUAC_LOG_DEBUG,
+            "%s: Translated path \"%s\" to \"%s\".",
+            __func__, normalized_path, real_path);
+
+    switch (create_disposition) {
+
+        /* Create if not exist, fail otherwise */
+        case DISP_FILE_CREATE:
+            flags |= O_CREAT | O_EXCL;
+            break;
+
+        /* Open file if exists and do not overwrite, fail otherwise */
+        case DISP_FILE_OPEN:
+            /* No flag necessary - default functionality of open */
+            break;
+
+        /* Open if exists, create otherwise */
+        case DISP_FILE_OPEN_IF:
+            flags |= O_CREAT;
+            break;
+
+        /* Overwrite if exists, fail otherwise */
+        case DISP_FILE_OVERWRITE:
+            flags |= O_TRUNC;
+            break;
+
+        /* Overwrite if exists, create otherwise */
+        case DISP_FILE_OVERWRITE_IF:
+            flags |= O_CREAT | O_TRUNC;
+            break;
+
+        /* Supersede (replace) if exists, otherwise create */
+        case DISP_FILE_SUPERSEDE:
+            unlink(real_path);
+            flags |= O_CREAT | O_TRUNC;
+            break;
+
+        /* Unrecognised disposition */
+        default:
+            return GUAC_RDP_FS_ENOSYS;
+
+    }
+
+    /* Create directory first, if necessary */
+    if ((create_options & FILE_DIRECTORY_FILE) && (flags & O_CREAT)) {
+
+        /* Create directory */
+        if (mkdir(real_path, S_IRWXU)) {
+            if (errno != EEXIST || (flags & O_EXCL)) {
+                guac_client_log(fs->client, GUAC_LOG_DEBUG,
+                        "%s: mkdir() failed: %s",
+                        __func__, strerror(errno));
+                return guac_rdp_fs_get_errorcode(errno);
+            }
+        }
+
+        /* Unset O_CREAT and O_EXCL as directory must exist before open() */
+        flags &= ~(O_CREAT | O_EXCL);
+
+    }
+
+    guac_client_log(fs->client, GUAC_LOG_DEBUG,
+            "%s: native open: real_path=\"%s\", flags=0x%x",
+            __func__, real_path, flags);
+
+    /* Open file */
+    fd = open(real_path, flags, S_IRUSR | S_IWUSR);
+
+    /* If file open failed as we're trying to write a dir, retry as read-only */
+    if (fd == -1 && errno == EISDIR) {
+        flags &= ~(O_WRONLY | O_RDWR);
+        flags |= O_RDONLY;
+        fd = open(real_path, flags, S_IRUSR | S_IWUSR);
+    }
+
+    if (fd == -1) {
+        guac_client_log(fs->client, GUAC_LOG_DEBUG,
+                "%s: open() failed: %s", __func__, strerror(errno));
+        return guac_rdp_fs_get_errorcode(errno);
+    }
+
+    /* Get file ID, init file */
+    file_id = guac_pool_next_int(fs->file_id_pool);
+    file = &(fs->files[file_id]);
+    file->id = file_id;
+    file->fd  = fd;
+    file->dir = NULL;
+    file->dir_pattern[0] = '\0';
+    file->absolute_path = strdup(normalized_path);
+    file->real_path = strdup(real_path);
+    file->bytes_written = 0;
+
+    guac_client_log(fs->client, GUAC_LOG_DEBUG,
+            "%s: Opened \"%s\" as file_id=%i",
+            __func__, normalized_path, file_id);
+
+    /* Attempt to pull file information */
+    if (fstat(fd, &file_stat) == 0) {
+
+        /* Load size and times */
+        file->size  = file_stat.st_size;
+        file->ctime = WINDOWS_TIME(file_stat.st_ctime);
+        file->mtime = WINDOWS_TIME(file_stat.st_mtime);
+        file->atime = WINDOWS_TIME(file_stat.st_atime);
+
+        /* Set type */
+        if (S_ISDIR(file_stat.st_mode))
+            file->attributes = FILE_ATTRIBUTE_DIRECTORY;
+        else
+            file->attributes = FILE_ATTRIBUTE_NORMAL;
+
+    }
+
+    /* If information cannot be retrieved, fake it */
+    else {
+
+        /* Init information to 0, lacking any alternative */
+        file->size  = 0;
+        file->ctime = 0;
+        file->mtime = 0;
+        file->atime = 0;
+        file->attributes = FILE_ATTRIBUTE_NORMAL;
+
+    }
+
+    fs->open_files++;
+
+    return file_id;
+
+}
+
+int guac_rdp_fs_read(guac_rdp_fs* fs, int file_id, int offset,
+        void* buffer, int length) {
+
+    int bytes_read;
+
+    guac_rdp_fs_file* file = guac_rdp_fs_get_file(fs, file_id);
+    if (file == NULL) {
+        guac_client_log(fs->client, GUAC_LOG_DEBUG,
+                "%s: Read from bad file_id: %i", __func__, file_id);
+        return GUAC_RDP_FS_EINVAL;
+    }
+
+    /* Attempt read */
+    lseek(file->fd, offset, SEEK_SET);
+    bytes_read = read(file->fd, buffer, length);
+
+    /* Translate errno on error */
+    if (bytes_read < 0)
+        return guac_rdp_fs_get_errorcode(errno);
+
+    return bytes_read;
+
+}
+
+int guac_rdp_fs_write(guac_rdp_fs* fs, int file_id, int offset,
+        void* buffer, int length) {
+
+    int bytes_written;
+
+    guac_rdp_fs_file* file = guac_rdp_fs_get_file(fs, file_id);
+    if (file == NULL) {
+        guac_client_log(fs->client, GUAC_LOG_DEBUG,
+                "%s: Write to bad file_id: %i", __func__, file_id);
+        return GUAC_RDP_FS_EINVAL;
+    }
+
+    /* Attempt write */
+    lseek(file->fd, offset, SEEK_SET);
+    bytes_written = write(file->fd, buffer, length);
+
+    /* Translate errno on error */
+    if (bytes_written < 0)
+        return guac_rdp_fs_get_errorcode(errno);
+
+    file->bytes_written += bytes_written;
+    return bytes_written;
+
+}
+
+int guac_rdp_fs_rename(guac_rdp_fs* fs, int file_id,
+        const char* new_path) {
+
+    char real_path[GUAC_RDP_FS_MAX_PATH];
+    char normalized_path[GUAC_RDP_FS_MAX_PATH];
+
+    guac_rdp_fs_file* file = guac_rdp_fs_get_file(fs, file_id);
+    if (file == NULL) {
+        guac_client_log(fs->client, GUAC_LOG_DEBUG,
+                "%s: Rename of bad file_id: %i", __func__, file_id);
+        return GUAC_RDP_FS_EINVAL;
+    }
+
+    /* Normalize path, return no-such-file if invalid  */
+    if (guac_rdp_fs_normalize_path(new_path, normalized_path)) {
+        guac_client_log(fs->client, GUAC_LOG_DEBUG,
+                "%s: Normalization of path \"%s\" failed.",
+                __func__, new_path);
+        return GUAC_RDP_FS_ENOENT;
+    }
+
+    /* Translate normalized path to real path */
+    __guac_rdp_fs_translate_path(fs, normalized_path, real_path);
+
+    guac_client_log(fs->client, GUAC_LOG_DEBUG,
+            "%s: Renaming \"%s\" -> \"%s\"",
+            __func__, file->real_path, real_path);
+
+    /* Perform rename */
+    if (rename(file->real_path, real_path)) {
+        guac_client_log(fs->client, GUAC_LOG_DEBUG,
+                "%s: rename() failed: \"%s\" -> \"%s\"",
+                __func__, file->real_path, real_path);
+        return guac_rdp_fs_get_errorcode(errno);
+    }
+
+    return 0;
+
+}
+
+int guac_rdp_fs_delete(guac_rdp_fs* fs, int file_id) {
+
+    /* Get file */
+    guac_rdp_fs_file* file = guac_rdp_fs_get_file(fs, file_id);
+    if (file == NULL) {
+        guac_client_log(fs->client, GUAC_LOG_DEBUG,
+                "%s: Delete of bad file_id: %i", __func__, file_id);
+        return GUAC_RDP_FS_EINVAL;
+    }
+
+    /* If directory, attempt removal */
+    if (file->attributes & FILE_ATTRIBUTE_DIRECTORY) {
+        if (rmdir(file->real_path)) {
+            guac_client_log(fs->client, GUAC_LOG_DEBUG,
+                    "%s: rmdir() failed: \"%s\"", __func__, file->real_path);
+            return guac_rdp_fs_get_errorcode(errno);
+        }
+    }
+
+    /* Otherwise, attempt deletion */
+    else if (unlink(file->real_path)) {
+        guac_client_log(fs->client, GUAC_LOG_DEBUG,
+                "%s: unlink() failed: \"%s\"", __func__, file->real_path);
+        return guac_rdp_fs_get_errorcode(errno);
+    }
+
+    return 0;
+
+}
+
+int guac_rdp_fs_truncate(guac_rdp_fs* fs, int file_id, int length) {
+
+    /* Get file */
+    guac_rdp_fs_file* file = guac_rdp_fs_get_file(fs, file_id);
+    if (file == NULL) {
+        guac_client_log(fs->client, GUAC_LOG_DEBUG,
+                "%s: Delete of bad file_id: %i", __func__, file_id);
+        return GUAC_RDP_FS_EINVAL;
+    }
+
+    /* Attempt truncate */
+    if (ftruncate(file->fd, length)) {
+        guac_client_log(fs->client, GUAC_LOG_DEBUG,
+                "%s: ftruncate() to %i bytes failed: \"%s\"",
+                __func__, length, file->real_path);
+        return guac_rdp_fs_get_errorcode(errno);
+    }
+
+    return 0;
+
+}
+
+void guac_rdp_fs_close(guac_rdp_fs* fs, int file_id) {
+
+    guac_rdp_fs_file* file = guac_rdp_fs_get_file(fs, file_id);
+    if (file == NULL) {
+        guac_client_log(fs->client, GUAC_LOG_DEBUG,
+                "%s: Ignoring close for bad file_id: %i",
+                __func__, file_id);
+        return;
+    }
+
+    file = &(fs->files[file_id]);
+
+    guac_client_log(fs->client, GUAC_LOG_DEBUG,
+            "%s: Closed \"%s\" (file_id=%i)",
+            __func__, file->absolute_path, file_id);
+
+    /* Close directory, if open */
+    if (file->dir != NULL)
+        closedir(file->dir);
+
+    /* Close file */
+    close(file->fd);
+
+    /* Free name */
+    free(file->absolute_path);
+    free(file->real_path);
+
+    /* Free ID back to pool */
+    guac_pool_free_int(fs->file_id_pool, file_id);
+    fs->open_files--;
+
+}
+
+const char* guac_rdp_fs_read_dir(guac_rdp_fs* fs, int file_id) {
+
+    guac_rdp_fs_file* file;
+
+    struct dirent* result;
+
+    /* Only read if file ID is valid */
+    if (file_id < 0 || file_id >= GUAC_RDP_FS_MAX_FILES)
+        return NULL;
+
+    file = &(fs->files[file_id]);
+
+    /* Open directory if not yet open, stop if error */
+    if (file->dir == NULL) {
+        file->dir = fdopendir(file->fd);
+        if (file->dir == NULL)
+            return NULL;
+    }
+
+    /* Read next entry, stop if error */
+    if (readdir_r(file->dir, &(file->__dirent), &result))
+        return NULL;
+
+    /* If no more entries, return NULL */
+    if (result == NULL)
+        return NULL;
+
+    /* Return filename */
+    return file->__dirent.d_name;
+
+}
+
+int guac_rdp_fs_normalize_path(const char* path, char* abs_path) {
+
+    int i;
+    int path_depth = 0;
+    char path_component_data[GUAC_RDP_FS_MAX_PATH];
+    const char* path_components[64];
+
+    const char** current_path_component      = &(path_components[0]);
+    const char*  current_path_component_data = &(path_component_data[0]);
+
+    /* If original path is not absolute, normalization fails */
+    if (path[0] != '\\' && path[0] != '/')
+        return 1;
+
+    /* Skip past leading slash */
+    path++;
+
+    /* Copy path into component data for parsing */
+    strncpy(path_component_data, path, GUAC_RDP_FS_MAX_PATH-1);
+
+    /* Find path components within path */
+    for (i=0; i<GUAC_RDP_FS_MAX_PATH; i++) {
+
+        /* If current character is a path separator, parse as component */
+        char c = path_component_data[i];
+        if (c == '/' || c == '\\' || c == 0) {
+
+            /* Terminate current component */
+            path_component_data[i] = 0;
+
+            /* If component refers to parent, just move up in depth */
+            if (strcmp(current_path_component_data, "..") == 0) {
+                if (path_depth > 0)
+                    path_depth--;
+            }
+
+            /* Otherwise, if component not current directory, add to list */
+            else if (strcmp(current_path_component_data,   ".") != 0
+                     && strcmp(current_path_component_data, "") != 0)
+                path_components[path_depth++] = current_path_component_data;
+
+            /* If end of string, stop */
+            if (c == 0)
+                break;
+
+            /* Update start of next component */
+            current_path_component_data = &(path_component_data[i+1]);
+
+        } /* end if separator */
+
+    } /* end for each character */
+
+    /* If no components, the path is simply root */
+    if (path_depth == 0) {
+        strcpy(abs_path, "\\");
+        return 0;
+    }
+
+    /* Ensure last component is null-terminated */
+    path_component_data[i] = 0;
+
+    /* Convert components back into path */
+    for (; path_depth > 0; path_depth--) {
+
+        const char* filename = *(current_path_component++);
+
+        /* Add separator */
+        *(abs_path++) = '\\';
+
+        /* Copy string */
+        while (*filename != 0)
+            *(abs_path++) = *(filename++);
+
+    }
+
+    /* Terminate absolute path */
+    *(abs_path++) = 0;
+    return 0;
+
+}
+
+int guac_rdp_fs_convert_path(const char* parent, const char* rel_path, char* abs_path) {
+
+    int i;
+    char combined_path[GUAC_RDP_FS_MAX_PATH];
+    char* current = combined_path;
+
+    /* Copy parent path */
+    for (i=0; i<GUAC_RDP_FS_MAX_PATH; i++) {
+
+        char c = *(parent++);
+        if (c == 0)
+            break;
+
+        *(current++) = c;
+
+    }
+
+    /* Add trailing slash */
+    *(current++) = '\\';
+
+    /* Copy remaining path */
+    strncpy(current, rel_path, GUAC_RDP_FS_MAX_PATH-i-2);
+
+    /* Normalize into provided buffer */
+    return guac_rdp_fs_normalize_path(combined_path, abs_path);
+
+}
+
+guac_rdp_fs_file* guac_rdp_fs_get_file(guac_rdp_fs* fs, int file_id) {
+
+    /* Validate ID */
+    if (file_id < 0 || file_id >= GUAC_RDP_FS_MAX_FILES)
+        return NULL;
+
+    /* Return file at given ID */
+    return &(fs->files[file_id]);
+
+}
+
+int guac_rdp_fs_matches(const char* filename, const char* pattern) {
+    return fnmatch(pattern, filename, FNM_NOESCAPE) != 0;
+}
+
+int guac_rdp_fs_get_info(guac_rdp_fs* fs, guac_rdp_fs_info* info) {
+
+    /* Read FS information */
+    struct statvfs fs_stat;
+    if (statvfs(fs->drive_path, &fs_stat))
+        return guac_rdp_fs_get_status(errno);
+
+    /* Assign to structure */
+    info->blocks_available = fs_stat.f_bfree;
+    info->blocks_total = fs_stat.f_blocks;
+    info->block_size = fs_stat.f_bsize;
+    return 0;
+
+}
+
+int guac_rdp_fs_append_filename(char* fullpath, const char* path,
+        const char* filename) {
+
+    int i;
+
+    /* Disallow "." as a filename */
+    if (strcmp(filename, ".") == 0)
+        return 0;
+
+    /* Disallow ".." as a filename */
+    if (strcmp(filename, "..") == 0)
+        return 0;
+
+    /* Copy path, append trailing slash */
+    for (i=0; i<GUAC_RDP_FS_MAX_PATH; i++) {
+
+        /*
+         * Append trailing slash only if:
+         *  1) Trailing slash is not already present
+         *  2) Path is non-empty
+         */
+
+        char c = path[i];
+        if (c == '\0') {
+            if (i > 0 && path[i-1] != '/' && path[i-1] != '\\')
+                fullpath[i++] = '/';
+            break;
+        }
+
+        /* Copy character if not end of string */
+        fullpath[i] = c;
+
+    }
+
+    /* Append filename */
+    for (; i<GUAC_RDP_FS_MAX_PATH; i++) {
+
+        char c = *(filename++);
+        if (c == '\0')
+            break;
+
+        /* Filenames may not contain slashes */
+        if (c == '\\' || c == '/')
+            return 0;
+
+        /* Append each character within filename */
+        fullpath[i] = c;
+
+    }
+
+    /* Verify path length is within maximum */
+    if (i == GUAC_RDP_FS_MAX_PATH)
+        return 0;
+
+    /* Terminate path string */
+    fullpath[i] = '\0';
+
+    /* Append was successful */
+    return 1;
+
+}
+
diff --git a/src/protocols/rdp/rdp_fs.h b/src/protocols/rdp/rdp_fs.h
new file mode 100644
index 0000000..48cf6ea
--- /dev/null
+++ b/src/protocols/rdp/rdp_fs.h
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef __GUAC_RDP_FS_H
+#define __GUAC_RDP_FS_H
+
+/**
+ * Functions and macros specific to filesystem handling and initialization
+ * independent of RDP.  The functions here may deal with the filesystem device
+ * directly, but their semantics must not deal with RDP protocol messaging.
+ * Functions here represent a virtual Windows-style filesystem on top of UNIX
+ * system calls and structures, using the guac_rdp_fs structure as a home
+ * for common data.
+ *
+ * @file rdp_fs.h 
+ */
+
+#include "config.h"
+
+#include <guacamole/client.h>
+#include <guacamole/object.h>
+#include <guacamole/pool.h>
+
+#include <dirent.h>
+#include <stdint.h>
+
+/**
+ * The maximum number of file IDs to provide.
+ */
+#define GUAC_RDP_FS_MAX_FILES 128
+
+/**
+ * The maximum number of bytes in a path string.
+ */
+#define GUAC_RDP_FS_MAX_PATH 4096
+
+/**
+ * Error code returned when no more file IDs can be allocated.
+ */
+#define GUAC_RDP_FS_ENFILE -1
+
+/**
+ * Error code returned when no such file exists.
+ */
+#define GUAC_RDP_FS_ENOENT -2
+
+/**
+ * Error code returned when the operation required a directory
+ * but the file was not a directory.
+ */
+#define GUAC_RDP_FS_ENOTDIR -3
+
+/**
+ * Error code returned when insufficient space exists to complete
+ * the operation.
+ */
+#define GUAC_RDP_FS_ENOSPC -4
+
+/**
+ * Error code returned when the operation requires a normal file but
+ * a directory was given.
+ */
+#define GUAC_RDP_FS_EISDIR -5
+
+/**
+ * Error code returned when permission is denied.
+ */
+#define GUAC_RDP_FS_EACCES -6
+
+/**
+ * Error code returned when the operation cannot be completed because the
+ * file already exists.
+ */
+#define GUAC_RDP_FS_EEXIST -7
+
+/**
+ * Error code returned when invalid parameters were given.
+ */
+#define GUAC_RDP_FS_EINVAL -8
+
+/**
+ * Error code returned when the operation is not implemented.
+ */
+#define GUAC_RDP_FS_ENOSYS -9
+
+/**
+ * Error code returned when the operation is not supported.
+ */
+#define GUAC_RDP_FS_ENOTSUP -10
+
+/*
+ * Access constants.
+ */
+#define ACCESS_GENERIC_READ       0x80000000
+#define ACCESS_GENERIC_WRITE      0x40000000
+#define ACCESS_GENERIC_ALL        0x10000000
+#define ACCESS_FILE_READ_DATA     0x00000001
+#define ACCESS_FILE_WRITE_DATA    0x00000002
+#define ACCESS_FILE_APPEND_DATA   0x00000004
+#define ACCESS_DELETE             0x00010000
+
+/*
+ * Create disposition constants.
+ */
+
+#define DISP_FILE_SUPERSEDE    0x00000000
+#define DISP_FILE_OPEN         0x00000001
+#define DISP_FILE_CREATE       0x00000002
+#define DISP_FILE_OPEN_IF      0x00000003
+#define DISP_FILE_OVERWRITE    0x00000004
+#define DISP_FILE_OVERWRITE_IF 0x00000005
+
+/*
+ * Information constants.
+ * FreeRDP 1.1+ already defines those constants
+ */
+#ifdef LEGACY_FREERDP
+
+#define FILE_SUPERSEDED   0x00000000
+#define FILE_OPENED       0x00000001
+#define FILE_OVERWRITTEN  0x00000003
+
+#endif
+
+/*
+ * File attributes.
+ */
+
+#define FILE_ATTRIBUTE_READONLY  0x00000001 
+#define FILE_ATTRIBUTE_HIDDEN    0x00000002 
+#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
+#define FILE_ATTRIBUTE_ARCHIVE   0x00000020
+#define FILE_ATTRIBUTE_NORMAL    0x00000080
+
+/*
+ * Filesystem attributes.
+ */
+
+#define FILE_CASE_SENSITIVE_SEARCH 0x00000001
+#define FILE_CASE_PRESERVED_NAMES  0x00000002
+#define FILE_UNICODE_ON_DISK       0x00000004
+
+/*
+ * File create options.
+ */
+
+#define FILE_DIRECTORY_FILE     0x00000001
+#define FILE_NON_DIRECTORY_FILE 0x00000040
+
+/*
+ * File device types.
+ */
+
+#define FILE_DEVICE_DISK 0x00000007
+
+#define SEC_TO_UNIX_EPOCH 11644473600
+
+/**
+ * Converts a Windows timestamp (100 nanosecond intervals since Jan 1, 1601
+ * UTC) to UNIX timestamp (seconds since Jan 1, 1970 UTC).
+ *
+ * This conversion is lossy.
+ */
+#define UNIX_TIME(t)    ((time_t) ((t / 10000000 + ((uint64_t) 11644473600))))
+
+/**
+ * Converts a UNIX timestamp (seconds since Jan 1, 1970 UTC) to Windows
+ * timestamp (100 nanosecond intervals since Jan 1, 1601 UTC).
+ */
+#define WINDOWS_TIME(t) ((t - ((uint64_t) 11644473600)) * 10000000)
+
+/**
+ * An arbitrary file on the virtual filesystem of the Guacamole drive.
+ */
+typedef struct guac_rdp_fs_file {
+
+    /**
+     * The ID of this file.
+     */
+    int id;
+
+    /**
+     * The absolute path, including filename, of this file.
+     */
+    char* absolute_path;
+
+    /**
+     * The real path of this file on the local filesystem.
+     */
+    char* real_path;
+
+    /**
+     * Associated local file descriptor.
+     */
+    int fd;
+
+    /**
+     * Associated directory stream, if any. This field only applies
+     * if the file is being used as a directory.
+     */
+    DIR* dir;
+
+    /**
+     * The last read dirent structure. This is used if traversing the contents
+     * of a directory.
+     */
+    struct dirent __dirent;
+
+    /**
+     * The pattern the check directory contents against, if any.
+     */
+    char dir_pattern[GUAC_RDP_FS_MAX_PATH];
+
+    /**
+     * Bitwise OR of all associated Windows file attributes.
+     */
+    int attributes;
+
+    /**
+     * The size of this file, in bytes.
+     */
+    int size;
+
+    /**
+     * The time this file was created, as a Windows timestamp.
+     */
+    uint64_t ctime;
+
+    /**
+     * The time this file was last modified, as a Windows timestamp.
+     */
+    uint64_t mtime;
+
+    /**
+     * The time this file was last accessed, as a Windows timestamp.
+     */
+    uint64_t atime;
+
+    /**
+     * The number of bytes written to the file.
+     */
+    uint64_t bytes_written;
+
+} guac_rdp_fs_file;
+
+/**
+ * A virtual filesystem implementing RDP-style operations.
+ */
+typedef struct guac_rdp_fs {
+
+    /**
+     * The controlling client.
+     */
+    guac_client* client;
+
+    /**
+     * The underlying filesystem object.
+     */
+    guac_object* object;
+
+    /**
+     * The root of the filesystem.
+     */
+    char* drive_path;
+
+    /**
+     * The number of currently open files.
+     */
+    int open_files;
+
+    /**
+     * Pool of file IDs.
+     */
+    guac_pool* file_id_pool;
+
+    /**
+     * All available file structures.
+     */
+    guac_rdp_fs_file files[GUAC_RDP_FS_MAX_FILES];
+
+} guac_rdp_fs;
+
+/**
+ * Filesystem information structure.
+ */
+typedef struct guac_rdp_fs_info {
+
+    /**
+     * The number of free blocks available.
+     */
+    int blocks_available;
+
+    /**
+     * The number of blocks in the filesystem.
+     */
+    int blocks_total;
+
+    /**
+     * The number of bytes per block.
+     */
+    int block_size;
+
+} guac_rdp_fs_info;
+
+/**
+ * Allocates a new filesystem given a root path.
+ */
+guac_rdp_fs* guac_rdp_fs_alloc(guac_client* client, const char* drive_path, int create_drive_path);
+
+/**
+ * Frees the given filesystem.
+ */
+void guac_rdp_fs_free(guac_rdp_fs* fs);
+
+/**
+ * Converts the given relative path to an absolute path based on the given
+ * parent path. If the path cannot be converted, non-zero is returned.
+ */
+int guac_rdp_fs_convert_path(const char* parent, const char* rel_path, char* abs_path);
+
+/**
+ * Translates the given errno error code to a GUAC_RDP_FS error code.
+ */
+int guac_rdp_fs_get_errorcode(int err);
+
+/**
+ * Teanslates the given GUAC_RDP_FS error code to an RDPDR status code.
+ */
+int guac_rdp_fs_get_status(int err);
+
+/**
+ * Returns the next available file ID, or an error code less than zero
+ * if an error occurs.
+ */
+int guac_rdp_fs_open(guac_rdp_fs* fs, const char* path,
+        int access, int file_attributes, int create_disposition,
+        int create_options);
+
+/**
+ * Reads up to the given length of bytes from the given offset within the
+ * file having the given ID. Returns the number of bytes read, zero on EOF,
+ * and an error code if an error occurs.
+ */
+int guac_rdp_fs_read(guac_rdp_fs* fs, int file_id, int offset,
+        void* buffer, int length);
+
+/**
+ * Writes up to the given length of bytes from the given offset within the
+ * file having the given ID. Returns the number of bytes written, and an
+ * error code if an error occurs.
+ */
+int guac_rdp_fs_write(guac_rdp_fs* fs, int file_id, int offset,
+        void* buffer, int length);
+
+/**
+ * Renames (moves) the file with the given ID to the new path specified.
+ * Returns zero on success, or an error code if an error occurs.
+ */
+int guac_rdp_fs_rename(guac_rdp_fs* fs, int file_id,
+        const char* new_path);
+
+/**
+ * Deletes the file with the given ID.
+ */
+int guac_rdp_fs_delete(guac_rdp_fs* fs, int file_id);
+
+/**
+ * Truncates the file with the given ID to the given length (in bytes), which
+ * may be larger.
+ */
+int guac_rdp_fs_truncate(guac_rdp_fs* fs, int file_id, int length);
+
+/**
+ * Frees the given file ID, allowing future open operations to reuse it.
+ */
+void guac_rdp_fs_close(guac_rdp_fs* fs, int file_id);
+
+/**
+ * Given an arbitrary path, which may contain ".." and ".", creates an
+ * absolute path which does NOT contain ".." or ".".
+ */
+int guac_rdp_fs_normalize_path(const char* path, char* abs_path);
+
+/**
+ * Given a parent path and a relative path, produces a normalized absolute path.
+ */
+int guac_rdp_fs_convert_path(const char* parent, const char* rel_path, char* abs_path);
+
+/**
+ * Returns the next filename within the directory having the given file ID,
+ * or NULL if no more files.
+ */
+const char* guac_rdp_fs_read_dir(guac_rdp_fs* fs, int file_id);
+
+/**
+ * Returns the file having the given ID, or NULL if no such file exists.
+ */
+guac_rdp_fs_file* guac_rdp_fs_get_file(guac_rdp_fs* fs, int file_id);
+
+/**
+ * Returns whether the given filename matches the given pattern.
+ */
+int guac_rdp_fs_matches(const char* filename, const char* pattern);
+
+/**
+ * Populates the given structure with information about the filesystem,
+ * particularly the amount of space available.
+ */
+int guac_rdp_fs_get_info(guac_rdp_fs* fs, guac_rdp_fs_info* info);
+
+/**
+ * Concatenates the given filename with the given path, separating the two
+ * with a single forward slash. The full result must be no more than
+ * GUAC_RDP_FS_MAX_PATH bytes long, counting null terminator.
+ *
+ * @param fullpath
+ *     The buffer to store the result within. This buffer must be at least
+ *     GUAC_RDP_FS_MAX_PATH bytes long.
+ *
+ * @param path
+ *     The path to append the filename to.
+ *
+ * @param filename
+ *     The filename to append to the path.
+ *
+ * @return
+ *     Non-zero if the filename is valid and was successfully appended to the
+ *     path, zero otherwise.
+ */
+int guac_rdp_fs_append_filename(char* fullpath, const char* path,
+        const char* filename);
+
+#endif
+
diff --git a/src/protocols/rdp/rdp_gdi.c b/src/protocols/rdp/rdp_gdi.c
index abbfa60..74eb5ee 100644
--- a/src/protocols/rdp/rdp_gdi.c
+++ b/src/protocols/rdp/rdp_gdi.c
@@ -1,43 +1,37 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Original Code is libguac-client-rdp.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Contributor(s):
- * Matt Hortman
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
 
-#include <pthread.h>
+#include "client.h"
+#include "guac_surface.h"
+#include "rdp_bitmap.h"
+#include "rdp_color.h"
+#include "rdp_settings.h"
+
+#include <cairo/cairo.h>
 #include <freerdp/freerdp.h>
+#include <guacamole/client.h>
+#include <guacamole/protocol.h>
 
 #ifdef ENABLE_WINPR
 #include <winpr/wtypes.h>
@@ -45,11 +39,7 @@
 #include "compat/winpr-wtypes.h"
 #endif
 
-#include <guacamole/client.h>
-
-#include "client.h"
-#include "rdp_bitmap.h"
-
+#include <stddef.h>
 
 guac_transfer_function guac_rdp_rop3_transfer_function(guac_client* client,
         int rop3) {
@@ -101,7 +91,7 @@ guac_transfer_function guac_rdp_rop3_transfer_function(guac_client* client,
     }
 
     /* Log warning if ROP3 opcode not supported */
-    guac_client_log_info (client, "guac_rdp_rop3_transfer_function: "
+    guac_client_log(client, GUAC_LOG_INFO, "guac_rdp_rop3_transfer_function: "
             "UNSUPPORTED opcode = 0x%02X", rop3);
 
     /* Default to BINARY_SRC */
@@ -112,41 +102,26 @@ guac_transfer_function guac_rdp_rop3_transfer_function(guac_client* client,
 void guac_rdp_gdi_dstblt(rdpContext* context, DSTBLT_ORDER* dstblt) {
 
     guac_client* client = ((rdp_freerdp_context*) context)->client;
-    const guac_layer* current_layer = ((rdp_guac_client_data*) client->data)->current_surface;
+    guac_common_surface* current_surface = ((rdp_guac_client_data*) client->data)->current_surface;
 
     int x = dstblt->nLeftRect;
     int y = dstblt->nTopRect;
     int w = dstblt->nWidth;
     int h = dstblt->nHeight;
 
-    /* Clip operation to bounds */
-    rdp_guac_client_data* data = (rdp_guac_client_data*) client->data;
-    if (guac_rdp_clip_rect(data, &x, &y, &w, &h))
-        return;
-
     switch (dstblt->bRop) {
 
         /* Blackness */
         case 0:
 
             /* Send black rectangle */
-            guac_protocol_send_rect(client->socket, current_layer, x, y, w, h);
-
-            guac_protocol_send_cfill(client->socket,
-                    GUAC_COMP_OVER, current_layer,
-                    0, 0, 0, 255);
-
+            guac_common_surface_rect(current_surface, x, y, w, h, 0, 0, 0);
             break;
 
         /* DSTINVERT */
         case 0x55:
-
-            /* Invert */
-            guac_protocol_send_transfer(client->socket,
-                    current_layer, x, y, w, h,
-                    GUAC_TRANSFER_BINARY_NDEST,
-                    current_layer, x, y);
-
+            guac_common_surface_transfer(current_surface, x, y, w, h,
+                                         GUAC_TRANSFER_BINARY_NDEST, current_surface, x, y);
             break;
 
         /* NOP */
@@ -155,23 +130,18 @@ void guac_rdp_gdi_dstblt(rdpContext* context, DSTBLT_ORDER* dstblt) {
 
         /* Whiteness */
         case 0xFF:
-            guac_protocol_send_rect(client->socket, current_layer, x, y, w, h);
-
-            guac_protocol_send_cfill(client->socket,
-                    GUAC_COMP_OVER, current_layer,
-                    0xFF, 0xFF, 0xFF, 0xFF);
+            guac_common_surface_rect(current_surface, x, y, w, h, 0xFF, 0xFF, 0xFF);
             break;
 
         /* Unsupported ROP3 */
         default:
-            guac_client_log_info(client,
+            guac_client_log(client, GUAC_LOG_INFO,
                     "guac_rdp_gdi_dstblt(rop3=0x%x)", dstblt->bRop);
 
     }
 
 }
 
-
 void guac_rdp_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt) {
 
     /*
@@ -187,7 +157,7 @@ void guac_rdp_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt) {
 
     /* Get client and current layer */
     guac_client* client = ((rdp_freerdp_context*) context)->client;
-    const guac_layer* current_layer =
+    guac_common_surface* current_surface =
         ((rdp_guac_client_data*) client->data)->current_surface;
 
     int x = patblt->nLeftRect;
@@ -195,32 +165,19 @@ void guac_rdp_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt) {
     int w = patblt->nWidth;
     int h = patblt->nHeight;
 
-    rdp_guac_client_data* data = (rdp_guac_client_data*) client->data;
-
-    /* Layer for actual transfer */
-    guac_layer* buffer;
-
     /*
      * Warn that rendering is a fallback, as the server should not be sending
      * this order.
      */
-    guac_client_log_info(client, "Using fallback PATBLT (server is ignoring "
+    guac_client_log(client, GUAC_LOG_INFO, "Using fallback PATBLT (server is ignoring "
             "negotiated client capabilities)");
 
-    /* Clip operation to bounds */
-    if (guac_rdp_clip_rect(data, &x, &y, &w, &h))
-        return;
-
     /* Render rectangle based on ROP */
     switch (patblt->bRop) {
 
         /* If blackness, send black rectangle */
         case 0x00:
-            guac_protocol_send_rect(client->socket, current_layer, x, y, w, h);
-
-            guac_protocol_send_cfill(client->socket,
-                    GUAC_COMP_OVER, current_layer,
-                    0x00, 0x00, 0x00, 0xFF);
+            guac_common_surface_rect(current_surface, x, y, w, h, 0, 0, 0);
             break;
 
         /* If NOP, do nothing */
@@ -230,53 +187,21 @@ void guac_rdp_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt) {
         /* If operation is just a copy, send foreground only */
         case 0xCC:
         case 0xF0:
-            guac_protocol_send_rect(client->socket, current_layer, x, y, w, h);
-
-            guac_protocol_send_cfill(client->socket,
-                    GUAC_COMP_OVER, current_layer,
+            guac_common_surface_rect(current_surface, x, y, w, h,
                     (patblt->foreColor >> 16) & 0xFF,
                     (patblt->foreColor >> 8 ) & 0xFF,
-                    (patblt->foreColor      ) & 0xFF,
-                    0xFF);
+                    (patblt->foreColor      ) & 0xFF);
             break;
 
         /* If whiteness, send white rectangle */
         case 0xFF:
-            guac_protocol_send_rect(client->socket, current_layer, x, y, w, h);
-
-            guac_protocol_send_cfill(client->socket,
-                    GUAC_COMP_OVER, current_layer,
-                    0xFF, 0xFF, 0xFF, 0xFF);
+            guac_common_surface_rect(current_surface, x, y, w, h, 0xFF, 0xFF, 0xFF);
             break;
 
         /* Otherwise, invert entire rect */
         default:
-
-            /* Allocate buffer for transfer */
-            buffer = guac_client_alloc_buffer(client);
-
-            /* Send rectangle stroke */
-            guac_protocol_send_rect(client->socket, buffer,
-                    0, 0, w, h);
-
-            /* Fill rectangle with fore color only */
-            guac_protocol_send_cfill(client->socket, GUAC_COMP_OVER, buffer,
-                    0xFF, 0xFF, 0xFF, 0xFF);
-
-            /* Transfer */
-            guac_protocol_send_transfer(client->socket,
-
-                    /* ... from buffer */
-                    buffer, 0, 0, w, h,
-
-                    /* ... inverting */
-                    GUAC_TRANSFER_BINARY_XOR,
-
-                    /* ... to current layer */
-                    current_layer, x, y);
-
-            /* Done with buffer */
-            guac_client_free_buffer(client, buffer);
+            guac_common_surface_transfer(current_surface, x, y, w, h,
+                                         GUAC_TRANSFER_BINARY_NDEST, current_surface, x, y);
 
     }
 
@@ -285,7 +210,7 @@ void guac_rdp_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt) {
 void guac_rdp_gdi_scrblt(rdpContext* context, SCRBLT_ORDER* scrblt) {
 
     guac_client* client = ((rdp_freerdp_context*) context)->client;
-    const guac_layer* current_layer = ((rdp_guac_client_data*) client->data)->current_surface;
+    guac_common_surface* current_surface = ((rdp_guac_client_data*) client->data)->current_surface;
     
     int x = scrblt->nLeftRect;
     int y = scrblt->nTopRect;
@@ -295,27 +220,18 @@ void guac_rdp_gdi_scrblt(rdpContext* context, SCRBLT_ORDER* scrblt) {
     int x_src = scrblt->nXSrc;
     int y_src = scrblt->nYSrc;
 
-    /* Clip operation to bounds */
     rdp_guac_client_data* data = (rdp_guac_client_data*) client->data;
-    if (guac_rdp_clip_rect(data, &x, &y, &w, &h))
-        return;
-
-    /* Update source coordinates */
-    x_src += x - scrblt->nLeftRect;
-    y_src += y - scrblt->nTopRect;
 
     /* Copy screen rect to current surface */
-    guac_protocol_send_copy(client->socket,
-            GUAC_DEFAULT_LAYER, x_src, y_src, w, h,
-            GUAC_COMP_OVER, current_layer, x, y);
+    guac_common_surface_copy(data->default_surface, x_src, y_src, w, h,
+                             current_surface, x, y);
 
 }
 
 void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
 
     guac_client* client = ((rdp_freerdp_context*) context)->client;
-    const guac_layer* current_layer = ((rdp_guac_client_data*) client->data)->current_surface;
-    guac_socket* socket = client->socket;
+    guac_common_surface* current_surface = ((rdp_guac_client_data*) client->data)->current_surface;
     guac_rdp_bitmap* bitmap = (guac_rdp_bitmap*) memblt->bitmap;
 
     int x = memblt->nLeftRect;
@@ -326,31 +242,17 @@ void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
     int x_src = memblt->nXSrc;
     int y_src = memblt->nYSrc;
 
-    rdp_guac_client_data* data = (rdp_guac_client_data*) client->data;
-
     /* Make sure that the recieved bitmap is not NULL before processing */
     if (bitmap == NULL) {
-        guac_client_log_info(client, "NULL bitmap found in memblt instruction.");
+        guac_client_log(client, GUAC_LOG_INFO, "NULL bitmap found in memblt instruction.");
         return;
     }
 
-    /* Clip operation to bounds */
-    if (guac_rdp_clip_rect(data, &x, &y, &w, &h))
-        return;
-
-    /* Update source coordinates */
-    x_src += x - memblt->nLeftRect;
-    y_src += y - memblt->nTopRect;
-
     switch (memblt->bRop) {
 
         /* If blackness, send black rectangle */
         case 0x00:
-            guac_protocol_send_rect(client->socket, current_layer, x, y, w, h);
-
-            guac_protocol_send_cfill(client->socket,
-                    GUAC_COMP_OVER, current_layer,
-                    0x00, 0x00, 0x00, 0xFF);
+            guac_common_surface_rect(current_surface, x, y, w, h, 0, 0, 0);
             break;
 
         /* If NOP, do nothing */
@@ -361,12 +263,11 @@ void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
         case 0xCC: 
 
             /* If not cached, cache if necessary */
-            if (((guac_rdp_bitmap*) bitmap)->layer == NULL
-                    && ((guac_rdp_bitmap*) bitmap)->used >= 1)
+            if (bitmap->surface == NULL && bitmap->used >= 1)
                 guac_rdp_cache_bitmap(context, memblt->bitmap);
 
             /* If not cached, send as PNG */
-            if (bitmap->layer == NULL) {
+            if (bitmap->surface == NULL) {
                 if (memblt->bitmap->data != NULL) {
 
                     /* Create surface from image data */
@@ -375,9 +276,7 @@ void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
                         CAIRO_FORMAT_RGB24, w, h, 4*memblt->bitmap->width);
 
                     /* Send surface to buffer */
-                    guac_protocol_send_png(socket,
-                            GUAC_COMP_OVER, current_layer,
-                            x, y, surface);
+                    guac_common_surface_draw(current_surface, x, y, surface);
 
                     /* Free surface */
                     cairo_surface_destroy(surface);
@@ -387,9 +286,8 @@ void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
 
             /* Otherwise, copy */
             else
-                guac_protocol_send_copy(socket,
-                        bitmap->layer, x_src, y_src, w, h,
-                        GUAC_COMP_OVER, current_layer, x, y);
+                guac_common_surface_copy(bitmap->surface, x_src, y_src, w, h,
+                                         current_surface, x, y);
 
             /* Increment usage counter */
             ((guac_rdp_bitmap*) bitmap)->used++;
@@ -398,24 +296,19 @@ void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
 
         /* If whiteness, send white rectangle */
         case 0xFF:
-            guac_protocol_send_rect(client->socket, current_layer, x, y, w, h);
-
-            guac_protocol_send_cfill(client->socket,
-                    GUAC_COMP_OVER, current_layer,
-                    0xFF, 0xFF, 0xFF, 0xFF);
+            guac_common_surface_rect(current_surface, x, y, w, h, 0xFF, 0xFF, 0xFF);
             break;
 
         /* Otherwise, use transfer */
         default:
 
             /* If not available as a surface, make available. */
-            if (bitmap->layer == NULL)
+            if (bitmap->surface == NULL)
                 guac_rdp_cache_bitmap(context, memblt->bitmap);
 
-            guac_protocol_send_transfer(socket,
-                    bitmap->layer, x_src, y_src, w, h,
-                    guac_rdp_rop3_transfer_function(client, memblt->bRop),
-                    current_layer, x, y);
+            guac_common_surface_transfer(bitmap->surface, x_src, y_src, w, h,
+                                         guac_rdp_rop3_transfer_function(client, memblt->bRop),
+                                         current_surface, x, y);
 
             /* Increment usage counter */
             ((guac_rdp_bitmap*) bitmap)->used++;
@@ -428,48 +321,75 @@ void guac_rdp_gdi_opaquerect(rdpContext* context, OPAQUE_RECT_ORDER* opaque_rect
 
     /* Get client data */
     guac_client* client = ((rdp_freerdp_context*) context)->client;
-    rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
 
-    UINT32 color = freerdp_color_convert_var(opaque_rect->color,
-            client_data->settings.color_depth, 32,
-            ((rdp_freerdp_context*) context)->clrconv);
+    UINT32 color = guac_rdp_convert_color(context, opaque_rect->color);
 
-    const guac_layer* current_layer = ((rdp_guac_client_data*) client->data)->current_surface;
-
-    rdp_guac_client_data* data = (rdp_guac_client_data*) client->data;
+    guac_common_surface* current_surface = ((rdp_guac_client_data*) client->data)->current_surface;
 
     int x = opaque_rect->nLeftRect;
     int y = opaque_rect->nTopRect;
     int w = opaque_rect->nWidth;
     int h = opaque_rect->nHeight;
 
-    /* Clip operation to bounds */
-    if (guac_rdp_clip_rect(data, &x, &y, &w, &h))
-        return;
-
-    guac_protocol_send_rect(client->socket, current_layer, x, y, w, h);
-
-    guac_protocol_send_cfill(client->socket,
-            GUAC_COMP_OVER, current_layer,
+    guac_common_surface_rect(current_surface, x, y, w, h,
             (color >> 16) & 0xFF,
             (color >> 8 ) & 0xFF,
-            (color      ) & 0xFF,
-            255);
+            (color      ) & 0xFF);
 
 }
 
-void guac_rdp_gdi_palette_update(rdpContext* context, PALETTE_UPDATE* palette) {
+/**
+ * Updates the palette within a FreeRDP CLRCONV object using the new palette
+ * entries provided by an RDP palette update.
+ */
+static void guac_rdp_update_clrconv(CLRCONV* clrconv,
+        PALETTE_UPDATE* palette) {
 
-    CLRCONV* clrconv = ((rdp_freerdp_context*) context)->clrconv;
     clrconv->palette->count = palette->number;
 #ifdef LEGACY_RDPPALETTE
     clrconv->palette->entries = palette->entries;
 #else
-    memcpy(clrconv->palette->entries, palette->entries, sizeof(palette->entries));
+    memcpy(clrconv->palette->entries, palette->entries,
+            sizeof(palette->entries));
 #endif
 
 }
 
+/**
+ * Updates a raw ARGB32 palette using the new palette entries provided by an
+ * RDP palette update.
+ */
+static void guac_rdp_update_palette(UINT32* guac_palette,
+        PALETTE_UPDATE* palette) {
+
+    PALETTE_ENTRY* entry = palette->entries;
+    int i;
+
+    /* Copy each palette entry as ARGB32 */
+    for (i=0; i < palette->number; i++) {
+
+        *guac_palette = 0xFF000000
+                      | (entry->red   << 16)
+                      | (entry->green << 8)
+                      |  entry->blue;
+
+        guac_palette++;
+        entry++;
+    }
+
+}
+
+void guac_rdp_gdi_palette_update(rdpContext* context, PALETTE_UPDATE* palette) {
+
+    CLRCONV* clrconv = ((rdp_freerdp_context*) context)->clrconv;
+    UINT32* guac_palette = ((rdp_freerdp_context*) context)->palette;
+
+    /* Update internal palette representations */
+    guac_rdp_update_clrconv(clrconv, palette);
+    guac_rdp_update_palette(guac_palette, palette);
+
+}
+
 void guac_rdp_gdi_set_bounds(rdpContext* context, rdpBounds* bounds) {
 
     guac_client* client = ((rdp_freerdp_context*) context)->client;
@@ -477,16 +397,12 @@ void guac_rdp_gdi_set_bounds(rdpContext* context, rdpBounds* bounds) {
 
     /* If no bounds given, clear bounding rect */
     if (bounds == NULL)
-        data->bounded = FALSE;
+        guac_common_surface_reset_clip(data->default_surface);
 
     /* Otherwise, set bounding rectangle */
-    else {
-        data->bounded = TRUE;
-        data->bounds_left   = bounds->left;
-        data->bounds_top    = bounds->top;
-        data->bounds_right  = bounds->right;
-        data->bounds_bottom = bounds->bottom;
-    }
+    else
+        guac_common_surface_clip(data->default_surface, bounds->left, bounds->top,
+                                 bounds->right - bounds->left + 1, bounds->bottom - bounds->top + 1);
 
 }
 
@@ -494,3 +410,21 @@ void guac_rdp_gdi_end_paint(rdpContext* context) {
     /* IGNORE */
 }
 
+void guac_rdp_gdi_desktop_resize(rdpContext* context) {
+
+    guac_client* client = ((rdp_freerdp_context*) context)->client;
+    rdp_guac_client_data* data = (rdp_guac_client_data*) client->data;
+
+    guac_common_surface_resize(data->default_surface,
+            guac_rdp_get_width(context->instance),
+            guac_rdp_get_height(context->instance));
+
+    guac_common_surface_reset_clip(data->default_surface);
+
+    guac_client_log(client, GUAC_LOG_DEBUG, "Server resized display to %ix%i",
+            guac_rdp_get_width(context->instance),
+            guac_rdp_get_height(context->instance));
+
+}
+
+
diff --git a/src/protocols/rdp/rdp_gdi.h b/src/protocols/rdp/rdp_gdi.h
index 1f68352..9520c0f 100644
--- a/src/protocols/rdp/rdp_gdi.h
+++ b/src/protocols/rdp/rdp_gdi.h
@@ -1,56 +1,87 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef _GUAC_RDP_RDP_GDI_H
 #define _GUAC_RDP_RDP_GDI_H
 
-#include <guacamole/protocol.h>
+#include "config.h"
+
 #include <freerdp/freerdp.h>
+#include <guacamole/protocol.h>
 
-guac_composite_mode guac_rdp_rop3_transfer_function(guac_client* client,
-        int rop3);
+/**
+ * Translates a standard RDP ROP3 value into a guac_composite_mode.
+ */
+guac_composite_mode guac_rdp_rop3_transfer_function(guac_client* client, int rop3);
 
+/**
+ * Handler for RDP DSTBLT update.
+ */
 void guac_rdp_gdi_dstblt(rdpContext* context, DSTBLT_ORDER* dstblt);
+
+/**
+ * Handler for RDP PATBLT update.
+ */
 void guac_rdp_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt);
+
+/**
+ * Handler for RDP SCRBLT update.
+ */
 void guac_rdp_gdi_scrblt(rdpContext* context, SCRBLT_ORDER* scrblt);
+
+/**
+ * Handler for RDP MEMBLT update.
+ */
 void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt);
+
+/**
+ * Handler for RDP OPAQUE_RECT update.
+ */
 void guac_rdp_gdi_opaquerect(rdpContext* context, OPAQUE_RECT_ORDER* opaque_rect);
+
+/**
+ * Handler called when the remote color palette is changing.
+ */
 void guac_rdp_gdi_palette_update(rdpContext* context, PALETTE_UPDATE* palette);
+
+/**
+ * Handler called prior to calling the handlers for specific updates when
+ * those updatese are clipped by a bounding rectangle.
+ */
 void guac_rdp_gdi_set_bounds(rdpContext* context, rdpBounds* bounds);
+
+/**
+ * Handler called when a paint operation is complete. We don't actually
+ * use this, but FreeRDP requires it.
+ */
 void guac_rdp_gdi_end_paint(rdpContext* context);
 
+/**
+ * Handler called when the desktop dimensions change, either from a
+ * true desktop resize event received by the RDP client, or due to
+ * a revised size given by the server during initial connection
+ * negotiation.
+ */
+void guac_rdp_gdi_desktop_resize(rdpContext* context);
+
 #endif
diff --git a/src/protocols/rdp/rdp_glyph.c b/src/protocols/rdp/rdp_glyph.c
index 33dae37..735d2ed 100644
--- a/src/protocols/rdp/rdp_glyph.c
+++ b/src/protocols/rdp/rdp_glyph.c
@@ -1,43 +1,35 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Original Code is libguac-client-rdp.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Contributor(s):
- * Matt Hortman
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "client.h"
+#include "guac_surface.h"
+#include "rdp_color.h"
+#include "rdp_glyph.h"
+#include "rdp_settings.h"
 
-#include <pthread.h>
 #include <freerdp/freerdp.h>
+#include <guacamole/client.h>
 
 #ifdef ENABLE_WINPR
 #include <winpr/wtypes.h>
@@ -45,11 +37,9 @@
 #include "compat/winpr-wtypes.h"
 #endif
 
-#include <guacamole/client.h>
-#include <guacamole/error.h>
-
-#include "client.h"
-#include "rdp_glyph.h"
+#include <pthread.h>
+#include <stdint.h>
+#include <stdlib.h>
 
 /* Define cairo_format_stride_for_width() if missing */
 #ifndef HAVE_CAIRO_FORMAT_STRIDE_FOR_WIDTH
@@ -113,15 +103,14 @@ void guac_rdp_glyph_draw(rdpContext* context, rdpGlyph* glyph, int x, int y) {
 
     guac_client* client = ((rdp_freerdp_context*) context)->client;
     rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data;
+    guac_common_surface* current_surface = guac_client_data->current_surface;
+    uint32_t fgcolor = guac_client_data->glyph_color;
 
-    /* Do not attempt to draw glyphs if glyph drawing is not begun */
-    if (guac_client_data->glyph_cairo == NULL)
-        return;
-
-    /* Use glyph as mask */
-    cairo_mask_surface(
-            guac_client_data->glyph_cairo,
-            ((guac_rdp_glyph*) glyph)->surface, x, y);
+    /* Paint with glyph as mask */
+    guac_common_surface_paint(current_surface, x, y, ((guac_rdp_glyph*) glyph)->surface,
+                               (fgcolor & 0xFF0000) >> 16,
+                               (fgcolor & 0x00FF00) >> 8,
+                                fgcolor & 0x0000FF);
 
 }
 
@@ -143,114 +132,26 @@ void guac_rdp_glyph_begindraw(rdpContext* context,
     rdp_guac_client_data* guac_client_data =
         (rdp_guac_client_data*) client->data;
 
-    /* Convert foreground color */
-    fgcolor = freerdp_color_convert_var(fgcolor,
-            guac_client_data->settings.color_depth, 32,
-            ((rdp_freerdp_context*) context)->clrconv);
-
     /* Fill background with color if specified */
     if (width != 0 && height != 0) {
 
-        /* Prepare for opaque glyphs */
-        guac_client_data->glyph_surface = 
-            guac_client_data->opaque_glyph_surface;
-
-        /* Create cairo instance */
-        guac_client_data->glyph_cairo = cairo_create(
-            guac_client_data->glyph_surface);
-
         /* Convert background color */
-        bgcolor = freerdp_color_convert_var(bgcolor,
-                guac_client_data->settings.color_depth, 32,
-                ((rdp_freerdp_context*) context)->clrconv);
+        bgcolor = guac_rdp_convert_color(context, bgcolor);
 
-        /* Fill background */
-        cairo_rectangle(guac_client_data->glyph_cairo,
-                x, y, width, height);
-
-        cairo_set_source_rgb(guac_client_data->glyph_cairo,
-                ((bgcolor & 0xFF0000) >> 16) / 255.0,
-                ((bgcolor & 0x00FF00) >> 8 ) / 255.0,
-                ( bgcolor & 0x0000FF       ) / 255.0);
-
-        cairo_fill(guac_client_data->glyph_cairo);
+        guac_common_surface_rect(guac_client_data->current_surface, x, y, width, height,
+                                 (bgcolor & 0xFF0000) >> 16,
+                                 (bgcolor & 0x00FF00) >> 8,
+                                  bgcolor & 0x0000FF);
 
     }
 
-    /* Otherwise, prepare for transparent glyphs  */
-    else {
-
-        /* Select transparent glyph surface */
-        guac_client_data->glyph_surface = 
-            guac_client_data->trans_glyph_surface;
-
-        guac_client_data->glyph_cairo = cairo_create(
-            guac_client_data->glyph_surface);
-
-        /* Clear surface */
-        cairo_set_operator(guac_client_data->glyph_cairo,
-            CAIRO_OPERATOR_SOURCE);
-
-        cairo_set_source_rgba(guac_client_data->glyph_cairo, 0, 0, 0, 0);
-        cairo_paint(guac_client_data->glyph_cairo);
-
-        /* Restore operator */
-        cairo_set_operator(guac_client_data->glyph_cairo,
-            CAIRO_OPERATOR_OVER);
-
-    }
-
-    /* Prepare for glyph drawing */
-    cairo_set_source_rgb(guac_client_data->glyph_cairo,
-            ((fgcolor & 0xFF0000) >> 16) / 255.0,
-            ((fgcolor & 0x00FF00) >> 8 ) / 255.0,
-            ( fgcolor & 0x0000FF       ) / 255.0);
+    /* Convert foreground color */
+    guac_client_data->glyph_color = guac_rdp_convert_color(context, fgcolor);
 
 }
 
 void guac_rdp_glyph_enddraw(rdpContext* context,
         int x, int y, int width, int height, UINT32 fgcolor, UINT32 bgcolor) {
-
-    guac_client* client = ((rdp_freerdp_context*) context)->client;
-    rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data;
-    const guac_layer* current_layer = ((rdp_guac_client_data*) client->data)->current_surface;
-
-    /* Use glyph surface to provide image data for glyph rectangle */
-    cairo_surface_t* glyph_surface = guac_client_data->glyph_surface;
-    int stride = cairo_image_surface_get_stride(glyph_surface);
-
-    /* Calculate bounds */
-    int max_width = cairo_image_surface_get_width(glyph_surface) - x;
-    int max_height = cairo_image_surface_get_height(glyph_surface) - y;
-
-    /* Ensure dimensions of glyph do not exceed bounds */
-    if (width > max_width) width = max_width;
-    if (height > max_height) height = max_height;
-
-    /* Clip operation to clipping region, if any */
-    if (!guac_rdp_clip_rect(guac_client_data, &x, &y, &width, &height)) {
-
-        /* Ensure data is ready */
-        cairo_surface_flush(glyph_surface);
-
-        /* Create surface for subsection with text */
-        cairo_surface_t* surface = cairo_image_surface_create_for_data(
-                cairo_image_surface_get_data(glyph_surface) + 4*x + y*stride,
-                cairo_image_surface_get_format(glyph_surface),
-                width, height, stride);
-
-        /* Send surface with all glyphs to layer */
-        guac_protocol_send_png(client->socket,
-                GUAC_COMP_OVER, current_layer, x, y,
-                surface);
-
-        /* Destroy surface */
-        cairo_surface_destroy(surface);
-
-    }
-
-    /* Destroy cairo instance */
-    cairo_destroy(guac_client_data->glyph_cairo);
-
+    /* IGNORE */
 }
 
diff --git a/src/protocols/rdp/rdp_glyph.h b/src/protocols/rdp/rdp_glyph.h
index 4efa53d..b7a7f15 100644
--- a/src/protocols/rdp/rdp_glyph.h
+++ b/src/protocols/rdp/rdp_glyph.h
@@ -1,43 +1,32 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef _GUAC_RDP_RDP_GLYPH_H
 #define _GUAC_RDP_RDP_GLYPH_H
 
+#include "config.h"
+
+#include <cairo/cairo.h>
 #include <freerdp/freerdp.h>
 
 #ifdef ENABLE_WINPR
@@ -46,8 +35,6 @@
 #include "compat/winpr-wtypes.h"
 #endif
 
-#include <guacamole/protocol.h>
-
 typedef struct guac_rdp_glyph {
 
     /**
diff --git a/src/protocols/rdp/rdp_keymap.c b/src/protocols/rdp/rdp_keymap.c
index 82d4941..63dc3a8 100644
--- a/src/protocols/rdp/rdp_keymap.c
+++ b/src/protocols/rdp/rdp_keymap.c
@@ -1,42 +1,31 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
 
 #include "rdp_keymap.h"
 
+#include <string.h>
+
 const int GUAC_KEYSYMS_SHIFT[] = {0xFFE1, 0};
 const int GUAC_KEYSYMS_ALL_SHIFT[] = {0xFFE1, 0xFFE2, 0};
 
@@ -59,3 +48,21 @@ const int GUAC_KEYSYMS_ALL_MODIFIERS[] = {
     0
 };
 
+const guac_rdp_keymap* guac_rdp_keymap_find(const char* name) {
+
+    /* For each keymap */
+    const guac_rdp_keymap** current = GUAC_KEYMAPS;
+    while (*current != NULL) {
+
+        /* If name matches, done */
+        if (strcmp((*current)->name, name) == 0)
+            return *current;
+
+        current++;
+    }
+
+    /* Failure */
+    return NULL;
+
+}
+
diff --git a/src/protocols/rdp/rdp_keymap.h b/src/protocols/rdp/rdp_keymap.h
index 1b18c0c..52840e1 100644
--- a/src/protocols/rdp/rdp_keymap.h
+++ b/src/protocols/rdp/rdp_keymap.h
@@ -1,48 +1,30 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef _GUAC_RDP_RDP_KEYMAP_H
 #define _GUAC_RDP_RDP_KEYMAP_H
 
-#ifdef HAVE_FREERDP_LOCALE_KEYBOARD_H
-#include <freerdp/locale/keyboard.h>
-#else
-#include <freerdp/kbd/layouts.h>
-#endif
+#include "config.h"
 
 #ifdef ENABLE_WINPR
 #include <winpr/wtypes.h>
@@ -128,31 +110,6 @@ typedef guac_rdp_keysym_desc guac_rdp_static_keymap[0x200][0x100];
 typedef int guac_rdp_keysym_state_map[0x200][0x100];
 
 /**
- * US English keymap.
- */
-extern const guac_rdp_keymap guac_rdp_keymap_en_us;
-
-/**
- * German keymap.
- */
-extern const guac_rdp_keymap guac_rdp_keymap_de_de;
-
-/**
- * French keymap.
- */
-extern const guac_rdp_keymap guac_rdp_keymap_fr_fr;
-
-/**
- * Failsafe (Unicode events for all printable characters) keymap.
- */
-extern const guac_rdp_keymap guac_rdp_keymap_failsafe;
-
-/**
- * Common, base keymap for non-printable keys.
- */
-extern const guac_rdp_keymap guac_rdp_keymap_base;
-
-/**
  * Simple macro for determing whether a keysym can be stored (or retrieved)
  * from any keymap.
  */
@@ -171,6 +128,11 @@ extern const guac_rdp_keymap guac_rdp_keymap_base;
         )
 
 /**
+ * The name of the default keymap, which MUST exist.
+ */
+#define GUAC_DEFAULT_KEYMAP "en-us-qwerty"
+
+/**
  * Keysym string containing only the left "shift" key.
  */
 extern const int GUAC_KEYSYMS_SHIFT[];
@@ -232,5 +194,10 @@ extern const int GUAC_KEYSYMS_ALL_MODIFIERS[];
  */
 extern const guac_rdp_keymap* GUAC_KEYMAPS[];
 
+/**
+ * Return the keymap having the given name, if any, or NULL otherwise.
+ */
+const guac_rdp_keymap* guac_rdp_keymap_find(const char* name);
+
 #endif
 
diff --git a/src/protocols/rdp/rdp_keymap_base.c b/src/protocols/rdp/rdp_keymap_base.c
deleted file mode 100644
index dd99e5a..0000000
--- a/src/protocols/rdp/rdp_keymap_base.c
+++ /dev/null
@@ -1,220 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Matt Hortman
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include <freerdp/input.h>
-
-#include "rdp_keymap.h"
-
-static guac_rdp_keysym_desc __guac_rdp_keymap_mapping[] = {
-
-    /* BackSpace */
-    { .keysym = 0xff08, .scancode = 0x0E },
-
-    /* Tab */
-    { .keysym = 0xff09, .scancode = 0x0F },
-
-    /* Return */
-    { .keysym = 0xff0d, .scancode = 0x1C },
-
-    /* Scroll_Lock */
-    { .keysym = 0xff14, .scancode = 0x46 },
-
-    /* Escape */
-    { .keysym = 0xff1b, .scancode = 0x01 },
-
-    /* Home */
-    { .keysym = 0xff50, .scancode = 0x47,
-        .flags = KBD_FLAGS_EXTENDED },
-
-    /* Left */
-    { .keysym = 0xff51, .scancode = 0x4B,
-        .flags = KBD_FLAGS_EXTENDED },
-
-    /* Up */
-    { .keysym = 0xff52, .scancode = 0x48,
-        .flags = KBD_FLAGS_EXTENDED },
-
-    /* Right */
-    { .keysym = 0xff53, .scancode = 0x4D,
-        .flags = KBD_FLAGS_EXTENDED },
-
-    /* Down */
-    { .keysym = 0xff54, .scancode = 0x50,
-        .flags = KBD_FLAGS_EXTENDED },
-
-    /* Page_Up */
-    { .keysym = 0xff55, .scancode = 0x49,
-        .flags = KBD_FLAGS_EXTENDED },
-
-    /* Menu */
-    { .keysym = 0xff67, .scancode = 0x5D,
-        .flags = KBD_FLAGS_EXTENDED },
-
-    /* Page_Down */
-    { .keysym = 0xff56, .scancode = 0x51,
-        .flags = KBD_FLAGS_EXTENDED },
-
-    /* End */
-    { .keysym = 0xff57, .scancode = 0x4F,
-        .flags = KBD_FLAGS_EXTENDED },
-
-    /* Insert */
-    { .keysym = 0xff63, .scancode = 0x52,
-        .flags = KBD_FLAGS_EXTENDED },
-
-    /* Num_Lock */
-    { .keysym = 0xff7f, .scancode = 0x45 },
-
-    /* KP_0 */
-    { .keysym = 0xffb0, .scancode = 0x52 },
-
-    /* KP_1 */
-    { .keysym = 0xffb1, .scancode = 0x4F },
-
-    /* KP_2 */
-    { .keysym = 0xffb2, .scancode = 0x50 },
-
-    /* KP_3 */
-    { .keysym = 0xffb3, .scancode = 0x51 },
-
-    /* KP_4 */
-    { .keysym = 0xffb4, .scancode = 0x4B },
-
-    /* KP_5 */
-    { .keysym = 0xffb5, .scancode = 0x4C },
-
-    /* KP_6 */
-    { .keysym = 0xffb6, .scancode = 0x4D },
-
-    /* KP_7 */
-    { .keysym = 0xffb7, .scancode = 0x47 },
-
-    /* KP_8 */
-    { .keysym = 0xffb8, .scancode = 0x48 },
-
-    /* KP_9 */
-    { .keysym = 0xffb9, .scancode = 0x49 },
-
-    /* F1 */
-    { .keysym = 0xffbe, .scancode = 0x3B },
-
-    /* F2 */
-    { .keysym = 0xffbf, .scancode = 0x3C },
-
-    /* F3 */
-    { .keysym = 0xffc0, .scancode = 0x3D },
-
-    /* F4 */
-    { .keysym = 0xffc1, .scancode = 0x3E },
-
-    /* F5 */
-    { .keysym = 0xffc2, .scancode = 0x3F },
-
-    /* F6 */
-    { .keysym = 0xffc3, .scancode = 0x40 },
-
-    /* F7 */
-    { .keysym = 0xffc4, .scancode = 0x41 },
-
-    /* F8 */
-    { .keysym = 0xffc5, .scancode = 0x42 },
-
-    /* F9 */
-    { .keysym = 0xffc6, .scancode = 0x43 },
-
-    /* F10 */
-    { .keysym = 0xffc7, .scancode = 0x44 },
-
-    /* F11 */
-    { .keysym = 0xffc8, .scancode = 0x57 },
-
-    /* F12 */
-    { .keysym = 0xffc9, .scancode = 0x58 },
-
-    /* Shift_L */
-    { .keysym = 0xffe1, .scancode = 0x2A },
-
-    /* Shift_R */
-    { .keysym = 0xffe2, .scancode = 0x36 },
-
-    /* Control_L */
-    { .keysym = 0xffe3, .scancode = 0x1D },
-
-    /* Control_R */
-    { .keysym = 0xffe4, .scancode = 0x9D },
-
-    /* Caps_Lock */
-    { .keysym = 0xffe5, .scancode = 0x3A,
-        .flags = KBD_FLAGS_EXTENDED },
-
-    /* Alt_L */
-    { .keysym = 0xffe9, .scancode = 0x38 },
-
-    /* Alt_R */
-    { .keysym = 0xffea, .scancode = 0x38,
-        .flags = KBD_FLAGS_EXTENDED },
-
-    /* ISO_Level3_Shift */
-    { .keysym = 0xfe03, .scancode = 0x38,
-        .flags = KBD_FLAGS_EXTENDED },
-
-    /* Super_L */
-    { .keysym = 0xffeb, .scancode = 0x5B,
-        .flags = KBD_FLAGS_EXTENDED },
-
-    /* Super_R */
-    { .keysym = 0xffec, .scancode = 0x5C,
-        .flags = KBD_FLAGS_EXTENDED },
-
-    /* Delete */
-    { .keysym = 0xffff, .scancode = 0x53,
-        .flags = KBD_FLAGS_EXTENDED },
-
-    {0}
-
-};
-
-const guac_rdp_keymap guac_rdp_keymap_base = {
-
-    .name = "base",
-
-    .parent = NULL,
-    .mapping = __guac_rdp_keymap_mapping
-
-};
-
diff --git a/src/protocols/rdp/rdp_keymap_de_de.c b/src/protocols/rdp/rdp_keymap_de_de.c
deleted file mode 100644
index 8b2143f..0000000
--- a/src/protocols/rdp/rdp_keymap_de_de.c
+++ /dev/null
@@ -1,552 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include <freerdp/input.h>
-
-#ifdef HAVE_FREERDP_LOCALE_KEYBOARD_H
-#include <freerdp/locale/keyboard.h>
-#else
-#include <freerdp/kbd/layouts.h>
-#endif
-
-#include "rdp_keymap.h"
-
-static guac_rdp_keysym_desc __guac_rdp_keymap_mapping[] = {
-
-    /* space */
-    { .keysym = 0x0020, .scancode = 0x39 },
-
-    /* exclam */
-    { .keysym = 0x0021, .scancode = 0x02,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* quotedbl */
-    { .keysym = 0x0022, .scancode = 0x03,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* numbersign */
-    { .keysym = 0x0023, .scancode = 0x2b,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* dollar */
-    { .keysym = 0x0024, .scancode = 0x05,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* percent */
-    { .keysym = 0x0025, .scancode = 0x06,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* ampersand */
-    { .keysym = 0x0026, .scancode = 0x07,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* quoteright */
-    { .keysym = 0x0027, .scancode = 0x2b,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* parenleft */
-    { .keysym = 0x0028, .scancode = 0x09,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* parenright */
-    { .keysym = 0x0029, .scancode = 0x0A,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* asterisk */
-    { .keysym = 0x002a, .scancode = 0x1b,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* plus */
-    { .keysym = 0x002b, .scancode = 0x1b,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* comma */
-    { .keysym = 0x002c, .scancode = 0x33,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* minus */
-    { .keysym = 0x002d, .scancode = 0x35,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* period */
-    { .keysym = 0x002e, .scancode = 0x34,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* slash */
-    { .keysym = 0x002f, .scancode = 0x08,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* 0 */
-    { .keysym = 0x0030, .scancode = 0x0B,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* 1 */
-    { .keysym = 0x0031, .scancode = 0x02,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* 2 */
-    { .keysym = 0x0032, .scancode = 0x03,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* 3 */
-    { .keysym = 0x0033, .scancode = 0x04,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* 4 */
-    { .keysym = 0x0034, .scancode = 0x05,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* 5 */
-    { .keysym = 0x0035, .scancode = 0x06,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* 6 */
-    { .keysym = 0x0036, .scancode = 0x07,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* 7 */
-    { .keysym = 0x0037, .scancode = 0x08,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* 8 */
-    { .keysym = 0x0038, .scancode = 0x09,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* 9 */
-    { .keysym = 0x0039, .scancode = 0x0A,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* colon */
-    { .keysym = 0x003a, .scancode = 0x34,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* semicolon */
-    { .keysym = 0x003b, .scancode = 0x33,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* less */
-    { .keysym = 0x003c, .scancode = 0x56,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* equal */
-    { .keysym = 0x003d, .scancode = 0x0B,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* greater */
-    { .keysym = 0x003e, .scancode = 0x56,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* question */
-    { .keysym = 0x003f, .scancode = 0x0c,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* at */
-    { .keysym = 0x0040, .scancode = 0x10,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT,
-        .set_keysyms = GUAC_KEYSYMS_ALTGR },
-
-    /* A */
-    { .keysym = 0x0041, .scancode = 0x1E,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* B */
-    { .keysym = 0x0042, .scancode = 0x30,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* C */
-    { .keysym = 0x0043, .scancode = 0x2E,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* D */
-    { .keysym = 0x0044, .scancode = 0x20,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* E */
-    { .keysym = 0x0045, .scancode = 0x12,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* F */
-    { .keysym = 0x0046, .scancode = 0x21,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* G */
-    { .keysym = 0x0047, .scancode = 0x22,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* H */
-    { .keysym = 0x0048, .scancode = 0x23,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* I */
-    { .keysym = 0x0049, .scancode = 0x17,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* J */
-    { .keysym = 0x004a, .scancode = 0x24,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* K */
-    { .keysym = 0x004b, .scancode = 0x25,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* L */
-    { .keysym = 0x004c, .scancode = 0x26,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* M */
-    { .keysym = 0x004d, .scancode = 0x32,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* N */
-    { .keysym = 0x004e, .scancode = 0x31,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* O */
-    { .keysym = 0x004f, .scancode = 0x18,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* P */
-    { .keysym = 0x0050, .scancode = 0x19,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* Q */
-    { .keysym = 0x0051, .scancode = 0x10,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* R */
-    { .keysym = 0x0052, .scancode = 0x13,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* S */
-    { .keysym = 0x0053, .scancode = 0x1F,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* T */
-    { .keysym = 0x0054, .scancode = 0x14,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* U */
-    { .keysym = 0x0055, .scancode = 0x16,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* V */
-    { .keysym = 0x0056, .scancode = 0x2F,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* W */
-    { .keysym = 0x0057, .scancode = 0x11,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* X */
-    { .keysym = 0x0058, .scancode = 0x2D,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* Y */
-    { .keysym = 0x0059, .scancode = 0x2c,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* Z */
-    { .keysym = 0x005a, .scancode = 0x15,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* bracketleft */
-    { .keysym = 0x005b, .scancode = 0x09,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT,
-        .set_keysyms = GUAC_KEYSYMS_ALTGR },
-
-    /* backslash */
-    { .keysym = 0x005c, .scancode = 0x0c,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT,
-        .set_keysyms = GUAC_KEYSYMS_ALTGR },
-
-    /* bracketright */
-    { .keysym = 0x005d, .scancode = 0x0a,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT,
-        .set_keysyms = GUAC_KEYSYMS_ALTGR },
-
-    /* underscore */
-    { .keysym = 0x005f, .scancode = 0x35,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* dead grave */
-    { .keysym = 0xfe50, .scancode = 0x0d,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* dead acute */
-    { .keysym = 0xfe51, .scancode = 0x0d,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* dead circum */
-    { .keysym = 0xfe52, .scancode = 0x29,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* degree */
-    { .keysym = 0x00b0, .scancode = 0x29,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* a */
-    { .keysym = 0x0061, .scancode = 0x1E,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* b */
-    { .keysym = 0x0062, .scancode = 0x30,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* c */
-    { .keysym = 0x0063, .scancode = 0x2E,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* d */
-    { .keysym = 0x0064, .scancode = 0x20,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* e */
-    { .keysym = 0x0065, .scancode = 0x12,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* f */
-    { .keysym = 0x0066, .scancode = 0x21,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* g */
-    { .keysym = 0x0067, .scancode = 0x22,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* h */
-    { .keysym = 0x0068, .scancode = 0x23,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* i */
-    { .keysym = 0x0069, .scancode = 0x17,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* j */
-    { .keysym = 0x006a, .scancode = 0x24,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* k */
-    { .keysym = 0x006b, .scancode = 0x25,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* l */
-    { .keysym = 0x006c, .scancode = 0x26,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* m */
-    { .keysym = 0x006d, .scancode = 0x32,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* n */
-    { .keysym = 0x006e, .scancode = 0x31,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* o */
-    { .keysym = 0x006f, .scancode = 0x18,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* p */
-    { .keysym = 0x0070, .scancode = 0x19,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* q */
-    { .keysym = 0x0071, .scancode = 0x10,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* r */
-    { .keysym = 0x0072, .scancode = 0x13,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* s */
-    { .keysym = 0x0073, .scancode = 0x1F,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* t */
-    { .keysym = 0x0074, .scancode = 0x14,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* u */
-    { .keysym = 0x0075, .scancode = 0x16,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* v */
-    { .keysym = 0x0076, .scancode = 0x2F,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* w */
-    { .keysym = 0x0077, .scancode = 0x11,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* x */
-    { .keysym = 0x0078, .scancode = 0x2D,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* y */
-    { .keysym = 0x0079, .scancode = 0x2c,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* z */
-    { .keysym = 0x007a, .scancode = 0x15,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* ä */
-    { .keysym = 0x00e4, .scancode = 0x28,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* Ä */
-    { .keysym = 0x00c4, .scancode = 0x28,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* ö */
-    { .keysym = 0x00f6, .scancode = 0x27,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* Ö */
-    { .keysym = 0x00d6, .scancode = 0x27,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* ü */
-    { .keysym = 0x00fc, .scancode = 0x1a,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* Ü */
-    { .keysym = 0x00dc, .scancode = 0x1a,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* ß */
-    { .keysym = 0x00df, .scancode = 0x0c,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* braceleft */
-    { .keysym = 0x007b, .scancode = 0x08,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT,
-        .set_keysyms = GUAC_KEYSYMS_ALTGR },
-
-    /* bar */
-    { .keysym = 0x007c, .scancode = 0x56,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT,
-        .set_keysyms = GUAC_KEYSYMS_ALTGR },
-
-    /* braceright */
-    { .keysym = 0x007d, .scancode = 0x0b,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT,
-        .set_keysyms = GUAC_KEYSYMS_ALTGR },
-
-    /* dead tilde */
-    { .keysym = 0xfe53, .scancode = 0x1b,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT,
-        .set_keysyms = GUAC_KEYSYMS_ALTGR },
-
-    /* euro */
-    { .keysym = 0x10020ac, .scancode = 0x12,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT,
-        .set_keysyms = GUAC_KEYSYMS_ALTGR },
-
-    /* mu */
-    { .keysym = 0x00b5, .scancode = 0x32,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT,
-        .set_keysyms = GUAC_KEYSYMS_ALTGR },
-
-    /* two superior */
-    { .keysym = 0x00b2, .scancode = 0x03,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT,
-        .set_keysyms = GUAC_KEYSYMS_ALTGR },
-
-    /* three superior */
-    { .keysym = 0x00b3, .scancode = 0x04,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT,
-        .set_keysyms = GUAC_KEYSYMS_ALTGR },
-
-    {0}
-
-};
-
-const guac_rdp_keymap guac_rdp_keymap_de_de = {
-
-    .name = "de-de-qwertz",
-
-    .parent = &guac_rdp_keymap_base,
-    .mapping = __guac_rdp_keymap_mapping,
-    .freerdp_keyboard_layout = KBD_GERMAN
-
-};
-
diff --git a/src/protocols/rdp/rdp_keymap_en_us.c b/src/protocols/rdp/rdp_keymap_en_us.c
deleted file mode 100644
index 2e6dc6f..0000000
--- a/src/protocols/rdp/rdp_keymap_en_us.c
+++ /dev/null
@@ -1,442 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Matt Hortman
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include <freerdp/input.h>
-
-#ifdef HAVE_FREERDP_LOCALE_KEYBOARD_H
-#include <freerdp/locale/keyboard.h>
-#else
-#include <freerdp/kbd/layouts.h>
-#endif
-
-#include "rdp_keymap.h"
-
-static guac_rdp_keysym_desc __guac_rdp_keymap_mapping[] = {
-
-    /* space */
-    { .keysym = 0x0020, .scancode = 0x39 },
-
-    /* exclam */
-    { .keysym = 0x0021, .scancode = 0x02,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* quotedbl */
-    { .keysym = 0x0022, .scancode = 0x28,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* numbersign */
-    { .keysym = 0x0023, .scancode = 0x04,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* dollar */
-    { .keysym = 0x0024, .scancode = 0x05,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* percent */
-    { .keysym = 0x0025, .scancode = 0x06,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* ampersand */
-    { .keysym = 0x0026, .scancode = 0x08,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* quoteright */
-    { .keysym = 0x0027, .scancode = 0x28 },
-
-    /* parenleft */
-    { .keysym = 0x0028, .scancode = 0x0A,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* parenright */
-    { .keysym = 0x0029, .scancode = 0x0B,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* asterisk */
-    { .keysym = 0x002a, .scancode = 0x09,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* plus */
-    { .keysym = 0x002b, .scancode = 0x0D,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* comma */
-    { .keysym = 0x002c, .scancode = 0x33,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* minus */
-    { .keysym = 0x002d, .scancode = 0x0C,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* period */
-    { .keysym = 0x002e, .scancode = 0x34,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* slash */
-    { .keysym = 0x002f, .scancode = 0x35,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* 0 */
-    { .keysym = 0x0030, .scancode = 0x0B,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* 1 */
-    { .keysym = 0x0031, .scancode = 0x02,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* 2 */
-    { .keysym = 0x0032, .scancode = 0x03,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* 3 */
-    { .keysym = 0x0033, .scancode = 0x04,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* 4 */
-    { .keysym = 0x0034, .scancode = 0x05,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* 5 */
-    { .keysym = 0x0035, .scancode = 0x06,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* 6 */
-    { .keysym = 0x0036, .scancode = 0x07,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* 7 */
-    { .keysym = 0x0037, .scancode = 0x08,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* 8 */
-    { .keysym = 0x0038, .scancode = 0x09,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* 9 */
-    { .keysym = 0x0039, .scancode = 0x0A,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* colon */
-    { .keysym = 0x003a, .scancode = 0x27,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* semicolon */
-    { .keysym = 0x003b, .scancode = 0x27,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* less */
-    { .keysym = 0x003c, .scancode = 0x33,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* equal */
-    { .keysym = 0x003d, .scancode = 0x0D,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* greater */
-    { .keysym = 0x003e, .scancode = 0x34,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* question */
-    { .keysym = 0x003f, .scancode = 0x35,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* at */
-    { .keysym = 0x0040, .scancode = 0x03,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* A */
-    { .keysym = 0x0041, .scancode = 0x1E,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* B */
-    { .keysym = 0x0042, .scancode = 0x30,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* C */
-    { .keysym = 0x0043, .scancode = 0x2E,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* D */
-    { .keysym = 0x0044, .scancode = 0x20,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* E */
-    { .keysym = 0x0045, .scancode = 0x12,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* F */
-    { .keysym = 0x0046, .scancode = 0x21,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* G */
-    { .keysym = 0x0047, .scancode = 0x22,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* H */
-    { .keysym = 0x0048, .scancode = 0x23,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* I */
-    { .keysym = 0x0049, .scancode = 0x17,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* J */
-    { .keysym = 0x004a, .scancode = 0x24,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* K */
-    { .keysym = 0x004b, .scancode = 0x25,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* L */
-    { .keysym = 0x004c, .scancode = 0x26,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* M */
-    { .keysym = 0x004d, .scancode = 0x32,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* N */
-    { .keysym = 0x004e, .scancode = 0x31,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* O */
-    { .keysym = 0x004f, .scancode = 0x18,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* P */
-    { .keysym = 0x0050, .scancode = 0x19,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* Q */
-    { .keysym = 0x0051, .scancode = 0x10,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* R */
-    { .keysym = 0x0052, .scancode = 0x13,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* S */
-    { .keysym = 0x0053, .scancode = 0x1F,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* T */
-    { .keysym = 0x0054, .scancode = 0x14,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* U */
-    { .keysym = 0x0055, .scancode = 0x16,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* V */
-    { .keysym = 0x0056, .scancode = 0x2F,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* W */
-    { .keysym = 0x0057, .scancode = 0x11,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* X */
-    { .keysym = 0x0058, .scancode = 0x2D,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* Y */
-    { .keysym = 0x0059, .scancode = 0x15,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* Z */
-    { .keysym = 0x005a, .scancode = 0x2C,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* bracketleft */
-    { .keysym = 0x005b, .scancode = 0x1A,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* backslash */
-    { .keysym = 0x005c, .scancode = 0x2B,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* bracketright */
-    { .keysym = 0x005d, .scancode = 0x1B,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* asciicircum */
-    { .keysym = 0x005e, .scancode = 0x07,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* underscore */
-    { .keysym = 0x005f, .scancode = 0x0C,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* quoteleft */
-    { .keysym = 0x0060, .scancode = 0x29,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* a */
-    { .keysym = 0x0061, .scancode = 0x1E,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* b */
-    { .keysym = 0x0062, .scancode = 0x30,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* c */
-    { .keysym = 0x0063, .scancode = 0x2E,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* d */
-    { .keysym = 0x0064, .scancode = 0x20,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* e */
-    { .keysym = 0x0065, .scancode = 0x12,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* f */
-    { .keysym = 0x0066, .scancode = 0x21,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* g */
-    { .keysym = 0x0067, .scancode = 0x22,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* h */
-    { .keysym = 0x0068, .scancode = 0x23,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* i */
-    { .keysym = 0x0069, .scancode = 0x17,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* j */
-    { .keysym = 0x006a, .scancode = 0x24,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* k */
-    { .keysym = 0x006b, .scancode = 0x25,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* l */
-    { .keysym = 0x006c, .scancode = 0x26,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* m */
-    { .keysym = 0x006d, .scancode = 0x32,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* n */
-    { .keysym = 0x006e, .scancode = 0x31,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* o */
-    { .keysym = 0x006f, .scancode = 0x18,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* p */
-    { .keysym = 0x0070, .scancode = 0x19,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* q */
-    { .keysym = 0x0071, .scancode = 0x10,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* r */
-    { .keysym = 0x0072, .scancode = 0x13,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* s */
-    { .keysym = 0x0073, .scancode = 0x1F,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* t */
-    { .keysym = 0x0074, .scancode = 0x14,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* u */
-    { .keysym = 0x0075, .scancode = 0x16,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* v */
-    { .keysym = 0x0076, .scancode = 0x2F,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* w */
-    { .keysym = 0x0077, .scancode = 0x11,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* x */
-    { .keysym = 0x0078, .scancode = 0x2D,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* y */
-    { .keysym = 0x0079, .scancode = 0x15,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* z */
-    { .keysym = 0x007a, .scancode = 0x2C,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT },
-
-    /* braceleft */
-    { .keysym = 0x007b, .scancode = 0x1A,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* bar */
-    { .keysym = 0x007c, .scancode = 0x2B,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* braceright */
-    { .keysym = 0x007d, .scancode = 0x1B,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* asciitilde */
-    { .keysym = 0x007e, .scancode = 0x29,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    {0}
-
-};
-
-const guac_rdp_keymap guac_rdp_keymap_en_us = {
-
-    .name = "en-us-qwerty",
-
-    .parent = &guac_rdp_keymap_base,
-    .mapping = __guac_rdp_keymap_mapping,
-    .freerdp_keyboard_layout = KBD_US
-
-};
-
diff --git a/src/protocols/rdp/rdp_keymap_failsafe.c b/src/protocols/rdp/rdp_keymap_failsafe.c
deleted file mode 100644
index e4ecc67..0000000
--- a/src/protocols/rdp/rdp_keymap_failsafe.c
+++ /dev/null
@@ -1,63 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include <freerdp/input.h>
-
-#ifdef HAVE_FREERDP_LOCALE_KEYBOARD_H
-#include <freerdp/locale/keyboard.h>
-#else
-#include <freerdp/kbd/layouts.h>
-#endif
-
-#include "rdp_keymap.h"
-
-static guac_rdp_keysym_desc __guac_rdp_keymap_mapping[] = {
-
-    {0}
-
-};
-
-const guac_rdp_keymap guac_rdp_keymap_failsafe = {
-
-    .name = "failsafe",
-
-    .parent = &guac_rdp_keymap_base,
-    .mapping = __guac_rdp_keymap_mapping,
-    .freerdp_keyboard_layout = KBD_US
-
-};
-
diff --git a/src/protocols/rdp/rdp_keymap_fr_fr.c b/src/protocols/rdp/rdp_keymap_fr_fr.c
deleted file mode 100644
index 64b6392..0000000
--- a/src/protocols/rdp/rdp_keymap_fr_fr.c
+++ /dev/null
@@ -1,560 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Alexandre Devely
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include <freerdp/input.h>
-
-#ifdef HAVE_FREERDP_LOCALE_KEYBOARD_H
-#include <freerdp/locale/keyboard.h>
-#else
-#include <freerdp/kbd/layouts.h>
-#endif
-
-#include "rdp_keymap.h"
-
-
-static guac_rdp_keysym_desc __guac_rdp_keymap_mapping[] = {
-
-    /* space */
-    { .keysym = 0x0020, .scancode = 0x39 },
-
-    /* exclam */
-    { .keysym = 0x0021, .scancode = 0x35,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* quotedbl */
-    { .keysym = 0x0022, .scancode = 0x04,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* numbersign */
-    { .keysym = 0x0023, .scancode = 0x04,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT,
-        .set_keysyms = GUAC_KEYSYMS_ALTGR },
-
-    /* dollar */
-    { .keysym = 0x0024, .scancode = 0x1b,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* percent */
-    { .keysym = 0x0025, .scancode = 0x28,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* ampersand */
-    { .keysym = 0x0026, .scancode = 0x02,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR  },
-
-    /* quoteright */
-    { .keysym = 0x0027, .scancode = 0x05,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* parenleft */
-    { .keysym = 0x0028, .scancode = 0x06,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* parenright */
-    { .keysym = 0x0029, .scancode = 0x0c,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* asterisk */
-    { .keysym = 0x002a, .scancode = 0x2b,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* plus */
-    { .keysym = 0x002b, .scancode = 0x0D,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* comma */
-    { .keysym = 0x002c, .scancode = 0x32,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* minus */
-    { .keysym = 0x002d, .scancode = 0x07,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* period */
-    { .keysym = 0x002e, .scancode = 0x33,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT  },
-
-    /* slash */
-    { .keysym = 0x002f, .scancode = 0x34,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* 0 */
-    { .keysym = 0x0030, .scancode = 0x0B,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* 1 */
-    { .keysym = 0x0031, .scancode = 0x02,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* 2 */
-    { .keysym = 0x0032, .scancode = 0x03,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* 3 */
-    { .keysym = 0x0033, .scancode = 0x04,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* 4 */
-    { .keysym = 0x0034, .scancode = 0x05,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* 5 */
-    { .keysym = 0x0035, .scancode = 0x06,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* 6 */
-    { .keysym = 0x0036, .scancode = 0x07,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* 7 */
-    { .keysym = 0x0037, .scancode = 0x08,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* 8 */
-    { .keysym = 0x0038, .scancode = 0x09,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* 9 */
-    { .keysym = 0x0039, .scancode = 0x0A,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* colon */
-    { .keysym = 0x003a, .scancode = 0x34,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* semicolon */
-    { .keysym = 0x003b, .scancode = 0x33,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* less */
-    { .keysym = 0x003c, .scancode = 0x56,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* equal */
-    { .keysym = 0x003d, .scancode = 0x0D,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* greater */
-    { .keysym = 0x003e, .scancode = 0x56,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT  },
-
-    /* question */
-    { .keysym = 0x003f, .scancode = 0x32,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* at */
-    { .keysym = 0x0040, .scancode = 0x0B,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT,
-        .set_keysyms = GUAC_KEYSYMS_ALTGR },
-
-    /* A */
-    { .keysym = 0x0041, .scancode = 0x10,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* B */
-    { .keysym = 0x0042, .scancode = 0x30,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* C */
-    { .keysym = 0x0043, .scancode = 0x2E,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* D */
-    { .keysym = 0x0044, .scancode = 0x20,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* E */
-    { .keysym = 0x0045, .scancode = 0x12,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* F */
-    { .keysym = 0x0046, .scancode = 0x21,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* G */
-    { .keysym = 0x0047, .scancode = 0x22,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* H */
-    { .keysym = 0x0048, .scancode = 0x23,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* I */
-    { .keysym = 0x0049, .scancode = 0x17,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* J */
-    { .keysym = 0x004a, .scancode = 0x24,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* K */
-    { .keysym = 0x004b, .scancode = 0x25,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* L */
-    { .keysym = 0x004c, .scancode = 0x26,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* M */
-    { .keysym = 0x004d, .scancode = 0x27,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* N */
-    { .keysym = 0x004e, .scancode = 0x31,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* O */
-    { .keysym = 0x004f, .scancode = 0x18,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* P */
-    { .keysym = 0x0050, .scancode = 0x19,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* Q */
-    { .keysym = 0x0051, .scancode = 0x1E,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* R */
-    { .keysym = 0x0052, .scancode = 0x13,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* S */
-    { .keysym = 0x0053, .scancode = 0x1F,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* T */
-    { .keysym = 0x0054, .scancode = 0x14,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* U */
-    { .keysym = 0x0055, .scancode = 0x16,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* V */
-    { .keysym = 0x0056, .scancode = 0x2F,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* W */
-    { .keysym = 0x0057, .scancode = 0x2C,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* X */
-    { .keysym = 0x0058, .scancode = 0x2D,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* Y */
-    { .keysym = 0x0059, .scancode = 0x15,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* Z */
-    { .keysym = 0x005a, .scancode = 0x11,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* bracketleft */
-    { .keysym = 0x005b, .scancode = 0x06,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT,
-        .set_keysyms = GUAC_KEYSYMS_ALTGR },
-
-    /* backslash */
-    { .keysym = 0x005c, .scancode = 0x09,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT,
-        .set_keysyms = GUAC_KEYSYMS_ALTGR },
-
-    /* bracketright */
-    { .keysym = 0x005d, .scancode = 0x0c,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT,
-        .set_keysyms = GUAC_KEYSYMS_ALTGR },
-
-    /* asciicircum */
-    { .keysym = 0x005e, .scancode = 0x0a,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT,
-        .set_keysyms = GUAC_KEYSYMS_ALTGR },
-
-    /* underscore */
-    { .keysym = 0x005f, .scancode = 0x09,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* quoteleft */
-    { .keysym = 0x0060, .scancode = 0x08,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT,
-        .set_keysyms = GUAC_KEYSYMS_ALTGR },
-
-    /* a */
-    { .keysym = 0x0061, .scancode = 0x10,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* b */
-    { .keysym = 0x0062, .scancode = 0x30,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* c */
-    { .keysym = 0x0063, .scancode = 0x2E,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* d */
-    { .keysym = 0x0064, .scancode = 0x20,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* e */
-    { .keysym = 0x0065, .scancode = 0x12,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* f */
-    { .keysym = 0x0066, .scancode = 0x21,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* g */
-    { .keysym = 0x0067, .scancode = 0x22,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* h */
-    { .keysym = 0x0068, .scancode = 0x23,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* i */
-    { .keysym = 0x0069, .scancode = 0x17,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* j */
-    { .keysym = 0x006a, .scancode = 0x24,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* k */
-    { .keysym = 0x006b, .scancode = 0x25,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* l */
-    { .keysym = 0x006c, .scancode = 0x26,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* m */
-    { .keysym = 0x006d, .scancode = 0x27,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* n */
-    { .keysym = 0x006e, .scancode = 0x31,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* o */
-    { .keysym = 0x006f, .scancode = 0x18,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* p */
-    { .keysym = 0x0070, .scancode = 0x19,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* q */
-    { .keysym = 0x0071, .scancode = 0x1E,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* r */
-    { .keysym = 0x0072, .scancode = 0x13,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* s */
-    { .keysym = 0x0073, .scancode = 0x1F,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* t */
-    { .keysym = 0x0074, .scancode = 0x14,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* u */
-    { .keysym = 0x0075, .scancode = 0x16,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* v */
-    { .keysym = 0x0076, .scancode = 0x2F,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* w */
-    { .keysym = 0x0077, .scancode = 0x2C,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* x */
-    { .keysym = 0x0078, .scancode = 0x2D,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* y */
-    { .keysym = 0x0079, .scancode = 0x15,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* z */
-    { .keysym = 0x007a, .scancode = 0x11,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* braceleft */
-    { .keysym = 0x007b, .scancode = 0x05,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT,
-        .set_keysyms = GUAC_KEYSYMS_ALTGR },
-
-    /* bar */
-    { .keysym = 0x007c, .scancode = 0x07,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT,
-        .set_keysyms = GUAC_KEYSYMS_ALTGR },
-
-    /* braceright */
-    { .keysym = 0x007d, .scancode = 0x0D,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT,
-        .set_keysyms = GUAC_KEYSYMS_ALTGR },
-
-    /* asciitilde */
-    { .keysym = 0x007e, .scancode = 0x03,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT,
-        .set_keysyms = GUAC_KEYSYMS_ALTGR },
-
-    /* pound */
-    { .keysym = 0x00a3, .scancode = 0x1b,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* currency */
-    { .keysym = 0x00a4, .scancode = 0x1b,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT,
-        .set_keysyms = GUAC_KEYSYMS_ALTGR },
-
-    /* section */
-    { .keysym = 0x00a7, .scancode = 0x35,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* degree */
-    { .keysym = 0x00b0, .scancode = 0x0c,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* micro */
-    { .keysym = 0x00b5, .scancode = 0x2b,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* dead umlaut */
-    { .keysym = 0xfe57, .scancode = 0x1a,
-        .clear_keysyms = GUAC_KEYSYMS_ALTGR,
-        .set_keysyms = GUAC_KEYSYMS_SHIFT },
-
-    /* dead circum */
-    { .keysym = 0xfe52, .scancode = 0x1a,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* à */
-    { .keysym = 0x00e0, .scancode = 0x0b,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* ç */
-    { .keysym = 0x00e7, .scancode = 0x0a,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* è */
-    { .keysym = 0x00e8, .scancode = 0x08,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* é */
-    { .keysym = 0x00e9, .scancode = 0x03,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* ù */
-    { .keysym = 0x00f9, .scancode = 0x28,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    /* euro */
-    { .keysym = 0x10020ac, .scancode = 0x12,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT,
-        .set_keysyms = GUAC_KEYSYMS_ALTGR },
-
-    /* two superior */
-    { .keysym = 0x00b2, .scancode = 0x29,
-        .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR },
-
-    {0}
-
-};
-
-const guac_rdp_keymap guac_rdp_keymap_fr_fr = {
-
-    .name = "fr-fr-azerty",
-
-    .parent = &guac_rdp_keymap_base,
-    .mapping = __guac_rdp_keymap_mapping,
-    .freerdp_keyboard_layout = KBD_FRENCH
-
-};
-
diff --git a/src/protocols/rdp/rdp_pointer.c b/src/protocols/rdp/rdp_pointer.c
index 09ee22a..1d214df 100644
--- a/src/protocols/rdp/rdp_pointer.c
+++ b/src/protocols/rdp/rdp_pointer.c
@@ -1,49 +1,37 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Original Code is libguac-client-rdp.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Contributor(s):
- * David PHAM-VAN <d.pham-van at ulteo.com> Ulteo SAS - http://www.ulteo.com
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include <pthread.h>
-#include <freerdp/freerdp.h>
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <guacamole/client.h>
+#include "config.h"
 
 #include "client.h"
 #include "rdp_pointer.h"
-#include "default_pointer.h"
+
+#include <cairo/cairo.h>
+#include <freerdp/freerdp.h>
+#include <guacamole/client.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+
+#include <stdlib.h>
 
 void guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer) {
 
@@ -72,7 +60,8 @@ void guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer) {
         pointer->width, pointer->height, 4*pointer->width);
 
     /* Send surface to buffer */
-    guac_protocol_send_png(socket, GUAC_COMP_SRC, buffer, 0, 0, surface);
+    guac_client_stream_png(client, socket, GUAC_COMP_SRC, buffer,
+            0, 0, surface);
 
     /* Free surface */
     cairo_surface_destroy(surface);
diff --git a/src/protocols/rdp/rdp_pointer.h b/src/protocols/rdp/rdp_pointer.h
index 7731628..f488e48 100644
--- a/src/protocols/rdp/rdp_pointer.h
+++ b/src/protocols/rdp/rdp_pointer.h
@@ -1,47 +1,33 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
- * David PHAM-VAN <d.pham-van at ulteo.com> Ulteo SAS - http://www.ulteo.com
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef _GUAC_RDP_RDP_POINTER_H
 #define _GUAC_RDP_RDP_POINTER_H
 
-#include <freerdp/freerdp.h>
+#include "config.h"
 
-#include <guacamole/protocol.h>
+#include <freerdp/freerdp.h>
+#include <guacamole/layer.h>
 
 typedef struct guac_rdp_pointer {
 
diff --git a/src/protocols/rdp/rdp_rail.c b/src/protocols/rdp/rdp_rail.c
new file mode 100644
index 0000000..d2b108b
--- /dev/null
+++ b/src/protocols/rdp/rdp_rail.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "client.h"
+#include "rdp_rail.h"
+#include "rdp_settings.h"
+
+#include <freerdp/channels/channels.h>
+#include <freerdp/freerdp.h>
+#include <freerdp/utils/event.h>
+#include <guacamole/client.h>
+
+#ifdef ENABLE_WINPR
+#include <winpr/wtypes.h>
+#else
+#include "compat/winpr-wtypes.h"
+#endif
+
+#ifdef LEGACY_FREERDP
+#include "compat/rail.h"
+#else
+#include <freerdp/rail.h>
+#endif
+
+#include <stddef.h>
+
+void guac_rdp_process_rail_event(guac_client* client, wMessage* event) {
+
+#ifdef LEGACY_EVENT
+        switch (event->event_type) {
+#else
+        switch (GetMessageType(event->id)) {
+#endif
+
+            /* Get system parameters */
+            case RailChannel_GetSystemParam:
+                guac_rdp_process_rail_get_sysparam(client, event);
+                break;
+
+            /* Currently ignored events */
+            case RailChannel_ServerSystemParam:
+            case RailChannel_ServerExecuteResult:
+            case RailChannel_ServerMinMaxInfo:
+            case RailChannel_ServerLocalMoveSize:
+            case RailChannel_ServerGetAppIdResponse:
+            case RailChannel_ServerLanguageBarInfo:
+                break;
+
+            default:
+#ifdef LEGACY_EVENT
+                guac_client_log(client, GUAC_LOG_INFO,
+                        "Unknown rail event type: 0x%x",
+                        event->event_type);
+#else
+                guac_client_log(client, GUAC_LOG_INFO,
+                        "Unknown rail event type: 0x%x",
+                        GetMessageType(event->id));
+#endif
+
+        }
+
+}
+
+void guac_rdp_process_rail_get_sysparam(guac_client* client, wMessage* event) {
+
+    wMessage* response;
+    RAIL_SYSPARAM_ORDER* sysparam;
+
+    /* Get channels */
+    rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
+    rdpChannels* channels = client_data->rdp_inst->context->channels;
+
+    /* Get sysparam structure */
+#ifdef LEGACY_EVENT
+    sysparam = (RAIL_SYSPARAM_ORDER*) event->user_data;
+#else
+    sysparam = (RAIL_SYSPARAM_ORDER*) event->wParam;
+#endif
+
+    response = freerdp_event_new(RailChannel_Class,
+                                 RailChannel_ClientSystemParam,
+                                 NULL,
+                                 sysparam);
+
+    /* Work area */
+    sysparam->workArea.left   = 0;
+    sysparam->workArea.top    = 0;
+    sysparam->workArea.right  = client_data->settings.width;
+    sysparam->workArea.bottom = client_data->settings.height;
+
+    /* Taskbar */
+    sysparam->taskbarPos.left   = 0;
+    sysparam->taskbarPos.top    = 0;
+    sysparam->taskbarPos.right  = 0;
+    sysparam->taskbarPos.bottom = 0;
+
+    sysparam->dragFullWindows = FALSE;
+
+    /* Send response */
+    freerdp_channels_send_event(channels, response);
+
+}
+
diff --git a/src/protocols/rdp/rdp_rail.h b/src/protocols/rdp/rdp_rail.h
new file mode 100644
index 0000000..9eadf47
--- /dev/null
+++ b/src/protocols/rdp/rdp_rail.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef __GUAC_RDP_RDP_RAIL_H
+#define __GUAC_RDP_RDP_RAIL_H
+
+#include "config.h"
+
+#include <guacamole/client.h>
+
+#ifdef ENABLE_WINPR
+#include <winpr/stream.h>
+#else
+#include "compat/winpr-stream.h"
+#endif
+
+/**
+ * Dispatches a given RAIL event to the appropriate handler.
+ */
+void guac_rdp_process_rail_event(guac_client* client, wMessage* event);
+
+/**
+ * Handles the event sent when updating system parameters.
+ */
+void guac_rdp_process_rail_get_sysparam(guac_client* client, wMessage* event);
+
+#endif
+
diff --git a/src/protocols/rdp/rdp_settings.c b/src/protocols/rdp/rdp_settings.c
index 92d9088..ef12e15 100644
--- a/src/protocols/rdp/rdp_settings.c
+++ b/src/protocols/rdp/rdp_settings.c
@@ -1,56 +1,107 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
 
-#include <freerdp/constants.h>
 #include "rdp_settings.h"
 
-void guac_rdp_pull_settings(freerdp* rdp, guac_rdp_settings* guac_settings) {
+#include <freerdp/constants.h>
+#include <freerdp/settings.h>
 
-    rdpSettings* rdp_settings = rdp->settings;
+#ifdef ENABLE_WINPR
+#include <winpr/wtypes.h>
+#else
+#include "compat/winpr-wtypes.h"
+#endif
 
+#include <stddef.h>
+#include <string.h>
+
+int guac_rdp_get_width(freerdp* rdp) {
 #ifdef LEGACY_RDPSETTINGS
-    guac_settings->color_depth = rdp_settings->color_depth;
-    guac_settings->width       = rdp_settings->width;
-    guac_settings->height      = rdp_settings->height;
+    return rdp->settings->width;
 #else
-    guac_settings->color_depth = rdp_settings->ColorDepth;
-    guac_settings->width       = rdp_settings->DesktopWidth;
-    guac_settings->height      = rdp_settings->DesktopHeight;
+    return rdp->settings->DesktopWidth;
 #endif
+}
+
+int guac_rdp_get_height(freerdp* rdp) {
+#ifdef LEGACY_RDPSETTINGS
+    return rdp->settings->height;
+#else
+    return rdp->settings->DesktopHeight;
+#endif
+}
+
+int guac_rdp_get_depth(freerdp* rdp) {
+#ifdef LEGACY_RDPSETTINGS
+    return rdp->settings->color_depth;
+#else
+    return rdp->settings->ColorDepth;
+#endif
+}
+
+/**
+ * Given the settings structure of the Guacamole RDP client, calculates the
+ * standard performance flag value to send to the RDP server. The value of
+ * these flags is dictated by the RDP standard.
+ *
+ * @param guac_settings
+ *     The settings structure to read performance settings from.
+ *
+ * @returns
+ *     The standard RDP performance flag value representing the union of all
+ *     performance settings within the given settings structure.
+ */
+static int guac_rdp_get_performance_flags(guac_rdp_settings* guac_settings) {
+
+    /* No performance flags initially */
+    int flags = PERF_FLAG_NONE;
+
+    /* Desktop wallpaper */
+    if (!guac_settings->wallpaper_enabled)
+        flags |= PERF_DISABLE_WALLPAPER;
+
+    /* Theming of desktop/windows */
+    if (!guac_settings->theming_enabled)
+        flags |= PERF_DISABLE_THEMING;
+
+    /* Font smoothing (ClearType) */
+    if (guac_settings->font_smoothing_enabled)
+        flags |= PERF_ENABLE_FONT_SMOOTHING;
+
+    /* Full-window drag */
+    if (!guac_settings->full_window_drag_enabled)
+        flags |= PERF_DISABLE_FULLWINDOWDRAG;
+
+    /* Desktop composition (Aero) */
+    if (guac_settings->desktop_composition_enabled)
+        flags |= PERF_ENABLE_DESKTOP_COMPOSITION;
+
+    /* Menu animations */
+    if (!guac_settings->menu_animations_enabled)
+        flags |= PERF_DISABLE_MENUANIMATIONS;
+
+    return flags;
 
 }
 
@@ -94,6 +145,24 @@ void guac_rdp_push_settings(guac_rdp_settings* guac_settings, freerdp* rdp) {
     rdp_settings->KeyboardLayout = guac_settings->server_layout->freerdp_keyboard_layout;
 #endif
 
+    /* Performance flags */
+#ifdef LEGACY_RDPSETTINGS
+    rdp_settings->performance_flags = guac_rdp_get_performance_flags(guac_settings);
+#else
+    rdp_settings->PerformanceFlags = guac_rdp_get_performance_flags(guac_settings);
+#endif
+
+    /* Client name */
+    if (guac_settings->client_name != NULL) {
+#ifdef LEGACY_RDPSETTINGS
+        strncpy(rdp_settings->client_hostname, guac_settings->client_name,
+                RDP_CLIENT_HOSTNAME_SIZE - 1);
+#else
+        strncpy(rdp_settings->ClientHostname, guac_settings->client_name,
+                RDP_CLIENT_HOSTNAME_SIZE - 1);
+#endif
+    }
+
     /* Console */
 #ifdef LEGACY_RDPSETTINGS
     rdp_settings->console_session = guac_settings->console;
@@ -103,6 +172,32 @@ void guac_rdp_push_settings(guac_rdp_settings* guac_settings, freerdp* rdp) {
     rdp_settings->RemoteConsoleAudio = guac_settings->console_audio;
 #endif
 
+    /* Audio */
+#ifdef LEGACY_RDPSETTINGS
+#ifdef HAVE_RDPSETTINGS_AUDIOPLAYBACK
+    rdp_settings->audio_playback = guac_settings->audio_enabled;
+#endif
+#else
+#ifdef HAVE_RDPSETTINGS_AUDIOPLAYBACK
+    rdp_settings->AudioPlayback = guac_settings->audio_enabled;
+#endif
+#endif
+
+    /* Device redirection */
+#ifdef LEGACY_RDPSETTINGS
+#ifdef HAVE_RDPSETTINGS_DEVICEREDIRECTION
+    rdp_settings->device_redirection =  guac_settings->audio_enabled
+                                     || guac_settings->drive_enabled
+                                     || guac_settings->printing_enabled;
+#endif
+#else
+#ifdef HAVE_RDPSETTINGS_DEVICEREDIRECTION
+    rdp_settings->DeviceRedirection =  guac_settings->audio_enabled
+                                    || guac_settings->drive_enabled
+                                    || guac_settings->printing_enabled;
+#endif
+#endif
+
     /* Security */
     switch (guac_settings->security_mode) {
 
@@ -181,11 +276,44 @@ void guac_rdp_push_settings(guac_rdp_settings* guac_settings, freerdp* rdp) {
     rdp_settings->DisableEncryption = FALSE;
 #endif
 
+    /* RemoteApp */
+    if (guac_settings->remote_app != NULL) {
+#ifdef LEGACY_RDPSETTINGS
+        rdp_settings->workarea = TRUE;
+        rdp_settings->remote_app = TRUE;
+        rdp_settings->rail_langbar_supported = TRUE;
+#else
+        rdp_settings->Workarea = TRUE;
+        rdp_settings->RemoteApplicationMode = TRUE;
+        rdp_settings->RemoteAppLanguageBarSupported = TRUE;
+        rdp_settings->RemoteApplicationProgram = guac_settings->remote_app;
+        rdp_settings->ShellWorkingDirectory = guac_settings->remote_app_dir;
+        rdp_settings->RemoteApplicationCmdLine = guac_settings->remote_app_args;
+#endif
+    }
+
+#ifdef HAVE_RDPSETTINGS_SENDPRECONNECTIONPDU
+    /* Preconnection ID */
+    if (guac_settings->preconnection_id != -1) {
+        rdp_settings->NegotiateSecurityLayer = FALSE;
+        rdp_settings->SendPreconnectionPdu = TRUE;
+        rdp_settings->PreconnectionId = guac_settings->preconnection_id;
+    }
+
+    /* Preconnection BLOB */
+    if (guac_settings->preconnection_blob != NULL) {
+        rdp_settings->NegotiateSecurityLayer = FALSE;
+        rdp_settings->SendPreconnectionPdu = TRUE;
+        rdp_settings->PreconnectionBlob = guac_settings->preconnection_blob;
+    }
+#endif
+
     /* Order support */
 #ifdef LEGACY_RDPSETTINGS
     bitmap_cache = rdp_settings->bitmap_cache;
     rdp_settings->os_major_type = OSMAJORTYPE_UNSPECIFIED;
     rdp_settings->os_minor_type = OSMINORTYPE_UNSPECIFIED;
+    rdp_settings->desktop_resize = TRUE;
     rdp_settings->order_support[NEG_DSTBLT_INDEX] = TRUE;
     rdp_settings->order_support[NEG_PATBLT_INDEX] = FALSE; /* PATBLT not yet supported */
     rdp_settings->order_support[NEG_SCRBLT_INDEX] = TRUE;
@@ -214,6 +342,7 @@ void guac_rdp_push_settings(guac_rdp_settings* guac_settings, freerdp* rdp) {
     bitmap_cache = rdp_settings->BitmapCacheEnabled;
     rdp_settings->OsMajorType = OSMAJORTYPE_UNSPECIFIED;
     rdp_settings->OsMinorType = OSMINORTYPE_UNSPECIFIED;
+    rdp_settings->DesktopResize = TRUE;
     rdp_settings->OrderSupport[NEG_DSTBLT_INDEX] = TRUE;
     rdp_settings->OrderSupport[NEG_PATBLT_INDEX] = FALSE; /* PATBLT not yet supported */
     rdp_settings->OrderSupport[NEG_SCRBLT_INDEX] = TRUE;
diff --git a/src/protocols/rdp/rdp_settings.h b/src/protocols/rdp/rdp_settings.h
index e61a2ea..aa60b56 100644
--- a/src/protocols/rdp/rdp_settings.h
+++ b/src/protocols/rdp/rdp_settings.h
@@ -1,48 +1,41 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef __GUAC_RDP_SETTINGS_H
 #define __GUAC_RDP_SETTINGS_H
 
-#include <freerdp/freerdp.h>
-#include <guacamole/client.h>
+#include "config.h"
 
 #include "rdp_keymap.h"
 
+#include <freerdp/freerdp.h>
+
+/**
+ * The maximum number of bytes in the client hostname claimed during
+ * connection.
+ */
+#define RDP_CLIENT_HOSTNAME_SIZE 32
+
 /**
  * The default RDP port.
  */
@@ -136,6 +129,12 @@ typedef struct guac_rdp_settings {
     int height;
 
     /**
+     * The DPI of the remote display to assume when converting between
+     * client pixels and remote pixels.
+     */
+    int resolution;
+
+    /**
      * Whether audio is enabled.
      */
     int audio_enabled;
@@ -146,6 +145,23 @@ typedef struct guac_rdp_settings {
     int printing_enabled;
 
     /**
+     * Whether the virtual drive is enabled.
+     */
+    int drive_enabled;
+
+    /**
+     * The local system path which will be used to persist the
+     * virtual drive.
+     */
+    char* drive_path;
+
+    /**
+     * Whether to automatically create the local system path if it does not
+     * exist.
+     */
+    int create_drive_path;
+
+    /**
      * Whether this session is a console session.
      */
     int console;
@@ -166,6 +182,12 @@ typedef struct guac_rdp_settings {
     char* initial_program;
 
     /**
+     * The name of the client to submit to the RDP server upon connection, or
+     * NULL if the name is not specified.
+     */
+    char* client_name;
+
+    /**
      * The type of security to use for the connection.
      */
     guac_rdp_security security_mode;
@@ -182,8 +204,83 @@ typedef struct guac_rdp_settings {
      */
     int disable_authentication;
 
-} guac_rdp_settings;
+    /**
+     * The application to launch, if RemoteApp is in use.
+     */
+    char* remote_app;
 
+    /**
+     * The working directory of the remote application, if RemoteApp is in use.
+     */
+    char* remote_app_dir;
+
+    /**
+     * The arguments to pass to the remote application, if RemoteApp is in use.
+     */
+    char* remote_app_args;
+
+    /**
+     * NULL-terminated list of all static virtual channel names, or NULL if
+     * no channels whatsoever.
+     */
+    char** svc_names;
+
+    /**
+     * Whether the desktop wallpaper should be visible. If unset, the desktop
+     * wallpaper will be hidden, reducing the amount of bandwidth required.
+     */
+    int wallpaper_enabled;
+
+    /**
+     * Whether desktop and window theming should be allowed. If unset, theming
+     * is temporarily disabled on the desktop of the RDP server for the sake of
+     * performance, reducing the amount of bandwidth required.
+     */
+    int theming_enabled;
+
+    /**
+     * Whether glyphs should be smoothed with antialiasing (ClearType). If
+     * unset, glyphs will be rendered with sharp edges and using single colors,
+     * effectively 1-bit images, reducing the amount of bandwidth required.
+     */
+    int font_smoothing_enabled;
+
+    /**
+     * Whether windows contents should be shown as they are moved. If unset,
+     * only a window border will be shown during window move operations,
+     * reducing the amount of bandwidth required.
+     */
+    int full_window_drag_enabled;
+
+    /**
+     * Whether desktop composition (Aero) should be enabled during the session.
+     * As desktop composition provides alpha blending and other special
+     * effects, this increases the amount of bandwidth used. If unset, desktop
+     * composition will be disabled.
+     */
+    int desktop_composition_enabled;
+
+    /**
+     * Whether menu animations should be shown. If unset, menus will not be
+     * animated, reducing the amount of bandwidth required.
+     */
+    int menu_animations_enabled;
+
+    /**
+     * The preconnection ID to send within the preconnection PDU when
+     * initiating an RDP connection, if any. If no preconnection ID is
+     * specified, this will be -1.
+     */
+    int preconnection_id;
+
+    /**
+     * The preconnection BLOB (PCB) to send to the RDP server prior to full RDP
+     * connection negotiation. This value is used by Hyper-V to select the
+     * destination VM.
+     */
+    char* preconnection_blob;
+
+} guac_rdp_settings;
 
 /**
  * Save all given settings to the given freerdp instance.
@@ -191,10 +288,19 @@ typedef struct guac_rdp_settings {
 void guac_rdp_push_settings(guac_rdp_settings* guac_settings, freerdp* rdp);
 
 /**
- * Pull all settings from the given freerdp instance into the client
- * stored settings.
+ * Returns the width of the RDP session display.
+ */
+int guac_rdp_get_width(freerdp* rdp);
+
+/**
+ * Returns the height of the RDP session display.
+ */
+int guac_rdp_get_height(freerdp* rdp);
+
+/**
+ * Returns the depth of the RDP session display.
  */
-void guac_rdp_pull_settings(freerdp* rdp, guac_rdp_settings* guac_settings);
+int guac_rdp_get_depth(freerdp* rdp);
 
 #endif
 
diff --git a/src/protocols/rdp/rdp_status.h b/src/protocols/rdp/rdp_status.h
new file mode 100644
index 0000000..77be1b0
--- /dev/null
+++ b/src/protocols/rdp/rdp_status.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef __GUAC_RDP_STATUS_H
+#define __GUAC_RDP_STATUS_H
+
+/**
+ * RDP-specific status constants.
+ *
+ * @file rdp_status.h 
+ */
+
+#include "config.h"
+
+/* Include any constants from winpr/file.h, if available */
+
+#ifdef ENABLE_WINPR
+#include <winpr/file.h>
+#endif
+
+/* Constants which MAY be defined within FreeRDP */
+
+#ifndef STATUS_SUCCESS
+#define STATUS_SUCCESS                  0x00000000
+#define STATUS_NO_MORE_FILES            0x80000006
+#define STATUS_DEVICE_OFF_LINE          0x80000010
+#define STATUS_NOT_IMPLEMENTED          0xC0000002
+#define STATUS_INVALID_PARAMETER        0xC000000D
+#define STATUS_NO_SUCH_FILE             0xC000000F
+#define STATUS_END_OF_FILE              0xC0000011
+#define STATUS_ACCESS_DENIED            0xC0000022
+#define STATUS_OBJECT_NAME_COLLISION    0xC0000035
+#define STATUS_DISK_FULL                0xC000007F
+#define STATUS_FILE_INVALID             0xC0000098  
+#define STATUS_FILE_IS_A_DIRECTORY      0xC00000BA
+#define STATUS_NOT_SUPPORTED            0xC00000BB
+#define STATUS_NOT_A_DIRECTORY          0xC0000103
+#define STATUS_TOO_MANY_OPENED_FILES    0xC000011F
+#define STATUS_CANNOT_DELETE            0xC0000121
+#define STATUS_FILE_DELETED             0xC0000123
+#define STATUS_FILE_CLOSED              0xC0000128
+#endif
+
+/* Constants which are NEVER defined within FreeRDP */
+
+#define STATUS_FILE_SYSTEM_LIMITATION   0xC0000427
+#define STATUS_FILE_TOO_LARGE           0xC0000904
+
+#endif
diff --git a/src/protocols/rdp/rdp_stream.c b/src/protocols/rdp/rdp_stream.c
new file mode 100644
index 0000000..16aa560
--- /dev/null
+++ b/src/protocols/rdp/rdp_stream.c
@@ -0,0 +1,571 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#include "config.h"
+#include "client.h"
+#include "guac_clipboard.h"
+#include "rdp_fs.h"
+#include "rdp_svc.h"
+#include "rdp_stream.h"
+
+#include <freerdp/freerdp.h>
+#include <freerdp/channels/channels.h>
+#include <guacamole/client.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+#include <guacamole/stream.h>
+
+#ifdef HAVE_FREERDP_CLIENT_CLIPRDR_H
+#include <freerdp/client/cliprdr.h>
+#else
+#include "compat/client-cliprdr.h"
+#endif
+
+#ifdef ENABLE_WINPR
+#include <winpr/stream.h>
+#include <winpr/wtypes.h>
+#else
+#include "compat/winpr-stream.h"
+#include "compat/winpr-wtypes.h"
+#endif
+
+#include <stdlib.h>
+
+/**
+ * Writes the given filename to the given upload path, sanitizing the filename
+ * and translating the filename to the root directory.
+ */
+static void __generate_upload_path(const char* filename, char* path) {
+
+    int i;
+
+    /* Add initial backslash */
+    *(path++) = '\\';
+
+    for (i=1; i<GUAC_RDP_FS_MAX_PATH; i++) {
+
+        /* Get current, stop at end */
+        char c = *(filename++);
+        if (c == '\0')
+            break;
+
+        /* Replace special characters with underscores */
+        if (c == '/' || c == '\\')
+            c = '_';
+
+        *(path++) = c;
+
+    }
+
+    /* Terminate path */
+    *path = '\0';
+
+}
+
+int guac_rdp_upload_file_handler(guac_client* client, guac_stream* stream,
+        char* mimetype, char* filename) {
+
+    int file_id;
+    guac_rdp_stream* rdp_stream;
+    char file_path[GUAC_RDP_FS_MAX_PATH];
+
+    /* Get filesystem, return error if no filesystem */
+    guac_rdp_fs* fs = ((rdp_guac_client_data*) client->data)->filesystem;
+    if (fs == NULL) {
+        guac_protocol_send_ack(client->socket, stream, "FAIL (NO FS)",
+                GUAC_PROTOCOL_STATUS_SERVER_ERROR);
+        guac_socket_flush(client->socket);
+        return 0;
+    }
+
+    /* Translate name */
+    __generate_upload_path(filename, file_path);
+
+    /* Open file */
+    file_id = guac_rdp_fs_open(fs, file_path, ACCESS_GENERIC_WRITE, 0,
+            DISP_FILE_OVERWRITE_IF, 0);
+    if (file_id < 0) {
+        guac_protocol_send_ack(client->socket, stream, "FAIL (CANNOT OPEN)",
+                GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
+        guac_socket_flush(client->socket);
+        return 0;
+    }
+
+    /* Init upload status */
+    rdp_stream = malloc(sizeof(guac_rdp_stream));
+    rdp_stream->type = GUAC_RDP_UPLOAD_STREAM;
+    rdp_stream->upload_status.offset = 0;
+    rdp_stream->upload_status.file_id = file_id;
+    stream->data = rdp_stream;
+    stream->blob_handler = guac_rdp_upload_blob_handler;
+    stream->end_handler = guac_rdp_upload_end_handler;
+
+    guac_protocol_send_ack(client->socket, stream, "OK (STREAM BEGIN)",
+            GUAC_PROTOCOL_STATUS_SUCCESS);
+    guac_socket_flush(client->socket);
+    return 0;
+
+}
+
+int guac_rdp_svc_pipe_handler(guac_client* client, guac_stream* stream,
+        char* mimetype, char* name) {
+
+    guac_rdp_stream* rdp_stream;
+    guac_rdp_svc* svc = guac_rdp_get_svc(client, name);
+
+    /* Fail if no such SVC */
+    if (svc == NULL) {
+        guac_client_log(client, GUAC_LOG_ERROR,
+                "Requested non-existent pipe: \"%s\".",
+                name);
+        guac_protocol_send_ack(client->socket, stream, "FAIL (NO SUCH PIPE)",
+                GUAC_PROTOCOL_STATUS_CLIENT_BAD_REQUEST);
+        guac_socket_flush(client->socket);
+        return 0;
+    }
+    else
+        guac_client_log(client, GUAC_LOG_ERROR,
+                "Inbound half of channel \"%s\" connected.",
+                name);
+
+    /* Init stream data */
+    stream->data = rdp_stream = malloc(sizeof(guac_rdp_stream));
+    stream->blob_handler = guac_rdp_svc_blob_handler;
+    rdp_stream->type = GUAC_RDP_INBOUND_SVC_STREAM;
+    rdp_stream->svc = svc;
+    svc->input_pipe = stream;
+
+    return 0;
+
+}
+
+int guac_rdp_clipboard_handler(guac_client* client, guac_stream* stream,
+        char* mimetype) {
+
+    rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
+    guac_rdp_stream* rdp_stream;
+
+    /* Init stream data */
+    stream->data = rdp_stream = malloc(sizeof(guac_rdp_stream));
+    stream->blob_handler = guac_rdp_clipboard_blob_handler;
+    stream->end_handler = guac_rdp_clipboard_end_handler;
+    rdp_stream->type = GUAC_RDP_INBOUND_CLIPBOARD_STREAM;
+
+    guac_common_clipboard_reset(client_data->clipboard, mimetype);
+    return 0;
+
+}
+
+int guac_rdp_upload_blob_handler(guac_client* client, guac_stream* stream,
+        void* data, int length) {
+
+    int bytes_written;
+    guac_rdp_stream* rdp_stream = (guac_rdp_stream*) stream->data;
+
+    /* Get filesystem, return error if no filesystem 0*/
+    guac_rdp_fs* fs = ((rdp_guac_client_data*) client->data)->filesystem;
+    if (fs == NULL) {
+        guac_protocol_send_ack(client->socket, stream, "FAIL (NO FS)",
+                GUAC_PROTOCOL_STATUS_SERVER_ERROR);
+        guac_socket_flush(client->socket);
+        return 0;
+    }
+
+    /* Write entire block */
+    while (length > 0) {
+
+        /* Attempt write */
+        bytes_written = guac_rdp_fs_write(fs,
+                rdp_stream->upload_status.file_id,
+                rdp_stream->upload_status.offset,
+                data, length);
+
+        /* On error, abort */
+        if (bytes_written < 0) {
+            guac_protocol_send_ack(client->socket, stream,
+                    "FAIL (BAD WRITE)",
+                    GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
+            guac_socket_flush(client->socket);
+            return 0;
+        }
+
+        /* Update counters */
+        rdp_stream->upload_status.offset += bytes_written;
+        data += bytes_written;
+        length -= bytes_written;
+
+    }
+
+    guac_protocol_send_ack(client->socket, stream, "OK (DATA RECEIVED)",
+            GUAC_PROTOCOL_STATUS_SUCCESS);
+    guac_socket_flush(client->socket);
+    return 0;
+
+}
+
+int guac_rdp_svc_blob_handler(guac_client* client, guac_stream* stream,
+        void* data, int length) {
+
+    guac_rdp_stream* rdp_stream = (guac_rdp_stream*) stream->data;
+
+    /* Write blob data to SVC directly */
+    guac_rdp_svc_write(rdp_stream->svc, data, length);
+
+    guac_protocol_send_ack(client->socket, stream, "OK (DATA RECEIVED)",
+            GUAC_PROTOCOL_STATUS_SUCCESS);
+    guac_socket_flush(client->socket);
+    return 0;
+
+}
+
+int guac_rdp_clipboard_blob_handler(guac_client* client, guac_stream* stream,
+        void* data, int length) {
+
+    rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
+    guac_common_clipboard_append(client_data->clipboard, (char*) data, length);
+
+    return 0;
+}
+
+int guac_rdp_upload_end_handler(guac_client* client, guac_stream* stream) {
+
+    guac_rdp_stream* rdp_stream = (guac_rdp_stream*) stream->data;
+
+    /* Get filesystem, return error if no filesystem */
+    guac_rdp_fs* fs = ((rdp_guac_client_data*) client->data)->filesystem;
+    if (fs == NULL) {
+        guac_protocol_send_ack(client->socket, stream, "FAIL (NO FS)",
+                GUAC_PROTOCOL_STATUS_SERVER_ERROR);
+        guac_socket_flush(client->socket);
+        return 0;
+    }
+
+    /* Close file */
+    guac_rdp_fs_close(fs, rdp_stream->upload_status.file_id);
+
+    /* Acknowledge stream end */
+    guac_protocol_send_ack(client->socket, stream, "OK (STREAM END)",
+            GUAC_PROTOCOL_STATUS_SUCCESS);
+    guac_socket_flush(client->socket);
+
+    free(rdp_stream);
+    return 0;
+
+}
+
+int guac_rdp_clipboard_end_handler(guac_client* client, guac_stream* stream) {
+
+    rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
+    rdpChannels* channels = client_data->rdp_inst->context->channels;
+
+    RDP_CB_FORMAT_LIST_EVENT* format_list =
+        (RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new(
+            CliprdrChannel_Class,
+            CliprdrChannel_FormatList,
+            NULL, NULL);
+
+    /* Terminate clipboard data with NULL */
+    guac_common_clipboard_append(client_data->clipboard, "", 1);
+
+    /* Notify server that text data is now available */
+    format_list->formats = (UINT32*) malloc(sizeof(UINT32));
+    format_list->formats[0] = CB_FORMAT_TEXT;
+    format_list->formats[1] = CB_FORMAT_UNICODETEXT;
+    format_list->num_formats = 2;
+
+    freerdp_channels_send_event(channels, (wMessage*) format_list);
+
+    return 0;
+}
+
+int guac_rdp_download_ack_handler(guac_client* client, guac_stream* stream,
+        char* message, guac_protocol_status status) {
+
+    guac_rdp_stream* rdp_stream = (guac_rdp_stream*) stream->data;
+
+    /* Get filesystem, return error if no filesystem */
+    guac_rdp_fs* fs = ((rdp_guac_client_data*) client->data)->filesystem;
+    if (fs == NULL) {
+        guac_protocol_send_ack(client->socket, stream, "FAIL (NO FS)",
+                GUAC_PROTOCOL_STATUS_SERVER_ERROR);
+        guac_socket_flush(client->socket);
+        return 0;
+    }
+
+    /* If successful, read data */
+    if (status == GUAC_PROTOCOL_STATUS_SUCCESS) {
+
+        /* Attempt read into buffer */
+        char buffer[4096];
+        int bytes_read = guac_rdp_fs_read(fs,
+                rdp_stream->download_status.file_id,
+                rdp_stream->download_status.offset, buffer, sizeof(buffer));
+
+        /* If bytes read, send as blob */
+        if (bytes_read > 0) {
+            rdp_stream->download_status.offset += bytes_read;
+            guac_protocol_send_blob(client->socket, stream,
+                    buffer, bytes_read);
+        }
+
+        /* If EOF, send end */
+        else if (bytes_read == 0) {
+            guac_protocol_send_end(client->socket, stream);
+            guac_client_free_stream(client, stream);
+            free(rdp_stream);
+        }
+
+        /* Otherwise, fail stream */
+        else {
+            guac_client_log(client, GUAC_LOG_ERROR,
+                    "Error reading file for download");
+            guac_protocol_send_end(client->socket, stream);
+            guac_client_free_stream(client, stream);
+            free(rdp_stream);
+        }
+
+        guac_socket_flush(client->socket);
+
+    }
+
+    /* Otherwise, return stream to client */
+    else
+        guac_client_free_stream(client, stream);
+
+    return 0;
+
+}
+
+int guac_rdp_ls_ack_handler(guac_client* client, guac_stream* stream,
+        char* message, guac_protocol_status status) {
+
+    int blob_written = 0;
+    const char* filename;
+
+    guac_rdp_stream* rdp_stream = (guac_rdp_stream*) stream->data;
+
+    /* If unsuccessful, free stream and abort */
+    if (status != GUAC_PROTOCOL_STATUS_SUCCESS) {
+        guac_rdp_fs_close(rdp_stream->ls_status.fs,
+                rdp_stream->ls_status.file_id);
+        guac_client_free_stream(client, stream);
+        free(rdp_stream);
+        return 0;
+    }
+
+    /* While directory entries remain */
+    while ((filename = guac_rdp_fs_read_dir(rdp_stream->ls_status.fs,
+                    rdp_stream->ls_status.file_id)) != NULL
+            && !blob_written) {
+
+        char absolute_path[GUAC_RDP_FS_MAX_PATH];
+
+        /* Skip current and parent directory entries */
+        if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0)
+            continue;
+
+        /* Concatenate into absolute path - skip if invalid */
+        if (!guac_rdp_fs_append_filename(absolute_path,
+                    rdp_stream->ls_status.directory_name, filename)) {
+
+            guac_client_log(client, GUAC_LOG_DEBUG,
+                    "Skipping filename \"%s\" - filename is invalid or "
+                    "resulting path is too long", filename);
+
+            continue;
+        }
+
+        /* Attempt to open file to determine type */
+        int file_id = guac_rdp_fs_open(rdp_stream->ls_status.fs, absolute_path,
+                ACCESS_GENERIC_READ, 0, DISP_FILE_OPEN, 0);
+        if (file_id < 0)
+            continue;
+
+        /* Get opened file */
+        guac_rdp_fs_file* file = guac_rdp_fs_get_file(rdp_stream->ls_status.fs,
+                file_id);
+        if (file == NULL) {
+            guac_client_log(rdp_stream->ls_status.fs->client, GUAC_LOG_DEBUG,
+                    "%s: Successful open produced bad file_id: %i",
+                    __func__, file_id);
+            return 0;
+        }
+
+        /* Determine mimetype */
+        const char* mimetype;
+        if (file->attributes & FILE_ATTRIBUTE_DIRECTORY)
+            mimetype = GUAC_CLIENT_STREAM_INDEX_MIMETYPE;
+        else
+            mimetype = "application/octet-stream";
+
+        /* Write entry */
+        blob_written |= guac_common_json_write_property(client, stream,
+                &rdp_stream->ls_status.json_state, absolute_path, mimetype);
+
+        guac_rdp_fs_close(rdp_stream->ls_status.fs, file_id);
+
+    }
+
+    /* Complete JSON and cleanup at end of directory */
+    if (filename == NULL) {
+
+        /* Complete JSON object */
+        guac_common_json_end_object(client, stream,
+                &rdp_stream->ls_status.json_state);
+        guac_common_json_flush(client, stream,
+                &rdp_stream->ls_status.json_state);
+
+        /* Clean up resources */
+        guac_rdp_fs_close(rdp_stream->ls_status.fs,
+                rdp_stream->ls_status.file_id);
+        free(rdp_stream);
+
+        /* Signal of stream */
+        guac_protocol_send_end(client->socket, stream);
+        guac_client_free_stream(client, stream);
+
+    }
+
+    guac_socket_flush(client->socket);
+    return 0;
+
+}
+
+int guac_rdp_download_get_handler(guac_client* client, guac_object* object,
+        char* name) {
+
+    /* Get filesystem, ignore request if no filesystem */
+    guac_rdp_fs* fs = ((rdp_guac_client_data*) client->data)->filesystem;
+    if (fs == NULL)
+        return 0;
+
+    /* Attempt to open file for reading */
+    int file_id = guac_rdp_fs_open(fs, name, ACCESS_GENERIC_READ, 0,
+            DISP_FILE_OPEN, 0);
+    if (file_id < 0) {
+        guac_client_log(client, GUAC_LOG_INFO, "Unable to read file \"%s\"",
+                name);
+        return 0;
+    }
+
+    /* Get opened file */
+    guac_rdp_fs_file* file = guac_rdp_fs_get_file(fs, file_id);
+    if (file == NULL) {
+        guac_client_log(fs->client, GUAC_LOG_DEBUG,
+                "%s: Successful open produced bad file_id: %i",
+                __func__, file_id);
+        return 0;
+    }
+
+    /* If directory, send contents of directory */
+    if (file->attributes & FILE_ATTRIBUTE_DIRECTORY) {
+
+        /* Create stream data */
+        guac_rdp_stream* rdp_stream = malloc(sizeof(guac_rdp_stream));
+        rdp_stream->type = GUAC_RDP_LS_STREAM;
+        rdp_stream->ls_status.fs = fs;
+        rdp_stream->ls_status.file_id = file_id;
+        strncpy(rdp_stream->ls_status.directory_name, name,
+                sizeof(rdp_stream->ls_status.directory_name) - 1);
+
+        /* Allocate stream for body */
+        guac_stream* stream = guac_client_alloc_stream(client);
+        stream->ack_handler = guac_rdp_ls_ack_handler;
+        stream->data = rdp_stream;
+
+        /* Init JSON object state */
+        guac_common_json_begin_object(client, stream,
+                &rdp_stream->ls_status.json_state);
+
+        /* Associate new stream with get request */
+        guac_protocol_send_body(client->socket, object, stream,
+                GUAC_CLIENT_STREAM_INDEX_MIMETYPE, name);
+
+    }
+
+    /* Otherwise, send file contents */
+    else {
+
+        /* Create stream data */
+        guac_rdp_stream* rdp_stream = malloc(sizeof(guac_rdp_stream));
+        rdp_stream->type = GUAC_RDP_DOWNLOAD_STREAM;
+        rdp_stream->download_status.file_id = file_id;
+        rdp_stream->download_status.offset = 0;
+
+        /* Allocate stream for body */
+        guac_stream* stream = guac_client_alloc_stream(client);
+        stream->data = rdp_stream;
+        stream->ack_handler = guac_rdp_download_ack_handler;
+
+        /* Associate new stream with get request */
+        guac_protocol_send_body(client->socket, object, stream,
+                "application/octet-stream", name);
+
+    }
+
+    guac_socket_flush(client->socket);
+    return 0;
+}
+
+int guac_rdp_upload_put_handler(guac_client* client, guac_object* object,
+        guac_stream* stream, char* mimetype, char* name) {
+
+    /* Get filesystem, return error if no filesystem */
+    guac_rdp_fs* fs = ((rdp_guac_client_data*) client->data)->filesystem;
+    if (fs == NULL) {
+        guac_protocol_send_ack(client->socket, stream, "FAIL (NO FS)",
+                GUAC_PROTOCOL_STATUS_SERVER_ERROR);
+        guac_socket_flush(client->socket);
+        return 0;
+    }
+
+    /* Open file */
+    int file_id = guac_rdp_fs_open(fs, name, ACCESS_GENERIC_WRITE, 0,
+            DISP_FILE_OVERWRITE_IF, 0);
+
+    /* Abort on failure */
+    if (file_id < 0) {
+        guac_protocol_send_ack(client->socket, stream, "FAIL (CANNOT OPEN)",
+                GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
+        guac_socket_flush(client->socket);
+        return 0;
+    }
+
+    /* Init upload stream data */
+    guac_rdp_stream* rdp_stream = malloc(sizeof(guac_rdp_stream));
+    rdp_stream->type = GUAC_RDP_UPLOAD_STREAM;
+    rdp_stream->upload_status.offset = 0;
+    rdp_stream->upload_status.file_id = file_id;
+
+    /* Allocate stream, init for file upload */
+    stream->data = rdp_stream;
+    stream->blob_handler = guac_rdp_upload_blob_handler;
+    stream->end_handler = guac_rdp_upload_end_handler;
+
+    /* Acknowledge stream creation */
+    guac_protocol_send_ack(client->socket, stream, "OK (STREAM BEGIN)",
+            GUAC_PROTOCOL_STATUS_SUCCESS);
+    guac_socket_flush(client->socket);
+    return 0;
+}
+
diff --git a/src/protocols/rdp/rdp_stream.h b/src/protocols/rdp/rdp_stream.h
new file mode 100644
index 0000000..fe0b039
--- /dev/null
+++ b/src/protocols/rdp/rdp_stream.h
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef _GUAC_RDP_STREAM_H
+#define _GUAC_RDP_STREAM_H
+
+#include "config.h"
+#include "guac_json.h"
+#include "rdp_svc.h"
+
+#include <guacamole/client.h>
+#include <guacamole/protocol.h>
+#include <guacamole/stream.h>
+
+#include <stdint.h>
+
+/**
+ * The transfer status of a file being downloaded.
+ */
+typedef struct guac_rdp_download_status {
+
+    /**
+     * The file ID of the file being downloaded.
+     */
+    int file_id;
+
+    /**
+     * The current position within the file.
+     */
+    uint64_t offset;
+
+} guac_rdp_download_status;
+
+/**
+ * Structure which represents the current state of an upload.
+ */
+typedef struct guac_rdp_upload_status {
+
+    /**
+     * The overall offset within the file that the next write should
+     * occur at.
+     */
+    int offset;
+
+    /**
+     * The ID of the file being written to.
+     */
+    int file_id;
+
+} guac_rdp_upload_status;
+
+/**
+ * The current state of a directory listing operation.
+ */
+typedef struct guac_rdp_ls_status {
+
+    /**
+     * The filesystem associated with the directory being listed.
+     */
+    guac_rdp_fs* fs;
+
+    /**
+     * The file ID of the directory being listed.
+     */
+    int file_id;
+
+    /**
+     * The absolute path of the directory being listed.
+     */
+    char directory_name[GUAC_RDP_FS_MAX_PATH];
+
+    /**
+     * The current state of the JSON directory object being written.
+     */
+    guac_common_json_state json_state;
+
+} guac_rdp_ls_status;
+
+/**
+ * All available stream types.
+ */
+typedef enum guac_rdp_stream_type {
+
+    /**
+     * An in-progress file upload.
+     */
+    GUAC_RDP_UPLOAD_STREAM,
+
+    /**
+     * An in-progress file download.
+     */
+    GUAC_RDP_DOWNLOAD_STREAM,
+
+    /**
+     * An in-progress stream of a directory listing.
+     */
+    GUAC_RDP_LS_STREAM,
+
+    /**
+     * The inbound half of a static virtual channel.
+     */
+    GUAC_RDP_INBOUND_SVC_STREAM,
+
+    /**
+     * An inbound stream of clipboard data.
+     */
+    GUAC_RDP_INBOUND_CLIPBOARD_STREAM
+
+} guac_rdp_stream_type;
+
+/**
+ * Variable-typed stream data.
+ */
+typedef struct guac_rdp_stream {
+
+    /**
+     * The type of this stream.
+     */
+    guac_rdp_stream_type type;
+
+    /**
+     * The file upload status. Only valid for GUAC_RDP_UPLOAD_STREAM.
+     */
+    guac_rdp_upload_status upload_status;
+
+    /**
+     * The file upload status. Only valid for GUAC_RDP_DOWNLOAD_STREAM.
+     */
+    guac_rdp_download_status download_status;
+
+    /**
+     * The directory list status. Only valid for GUAC_RDP_LS_STREAM.
+     */
+    guac_rdp_ls_status ls_status;
+
+    /**
+     * Associated SVC instance. Only valid for GUAC_RDP_INBOUND_SVC_STREAM.
+     */
+    guac_rdp_svc* svc;
+
+} guac_rdp_stream;
+
+/**
+ * Handler for inbound files related to file uploads.
+ */
+int guac_rdp_upload_file_handler(guac_client* client, guac_stream* stream,
+        char* mimetype, char* filename);
+
+/**
+ * Handler for inbound pipes related to static virtual channels.
+ */
+int guac_rdp_svc_pipe_handler(guac_client* client, guac_stream* stream,
+        char* mimetype, char* name);
+
+/**
+ * Handler for inbound clipboard data.
+ */
+int guac_rdp_clipboard_handler(guac_client* client, guac_stream* stream,
+        char* mimetype);
+
+/**
+ * Handler for stream data related to file uploads.
+ */
+int guac_rdp_upload_blob_handler(guac_client* client, guac_stream* stream,
+        void* data, int length);
+
+/**
+ * Handler for stream data related to static virtual channels.
+ */
+int guac_rdp_svc_blob_handler(guac_client* client, guac_stream* stream,
+        void* data, int length);
+
+/**
+ * Handler for stream data related to clipboard.
+ */
+int guac_rdp_clipboard_blob_handler(guac_client* client, guac_stream* stream,
+        void* data, int length);
+
+/**
+ * Handler for end-of-stream related to file uploads.
+ */
+int guac_rdp_upload_end_handler(guac_client* client, guac_stream* stream);
+
+/**
+ * Handler for end-of-stream related to clipboard.
+ */
+int guac_rdp_clipboard_end_handler(guac_client* client, guac_stream* stream);
+
+/**
+ * Handler for acknowledgements of receipt of data related to file downloads.
+ */
+int guac_rdp_download_ack_handler(guac_client* client, guac_stream* stream,
+        char* message, guac_protocol_status status);
+
+/**
+ * Handler for ack messages received due to receipt of a "body" or "blob"
+ * instruction associated with a directory list operation.
+ *
+ * @param client
+ *     The client receiving the ack message.
+ *
+ * @param stream
+ *     The Guacamole protocol stream associated with the received ack message.
+ *
+ * @param message
+ *     An arbitrary human-readable message describing the nature of the
+ *     success or failure denoted by this ack message.
+ *
+ * @param status
+ *     The status code associated with this ack message, which may indicate
+ *     success or an error.
+ *
+ * @return
+ *     Zero on success, non-zero on error.
+ */
+int guac_rdp_ls_ack_handler(guac_client* client, guac_stream* stream,
+        char* message, guac_protocol_status status);
+
+/**
+ * Handler for get messages. In context of downloads and the filesystem exposed
+ * via the Guacamole protocol, get messages request the body of a file within
+ * the filesystem.
+ *
+ * @param client
+ *     The client receiving the get message.
+ *
+ * @param object
+ *     The Guacamole protocol object associated with the get request itself.
+ *
+ * @param name
+ *     The name of the input stream (file) being requested.
+ *
+ * @return
+ *     Zero on success, non-zero on error.
+ */
+int guac_rdp_download_get_handler(guac_client* client, guac_object* object,
+        char* name);
+
+/**
+ * Handler for put messages. In context of uploads and the filesystem exposed
+ * via the Guacamole protocol, put messages request write access to a file
+ * within the filesystem.
+ *
+ * @param client
+ *     The client receiving the put message.
+ *
+ * @param object
+ *     The Guacamole protocol object associated with the put request itself.
+ *
+ * @param stream
+ *     The Guacamole protocol stream along which the client will be sending
+ *     file data.
+ *
+ * @param mimetype
+ *     The mimetype of the data being send along the stream.
+ *
+ * @param name
+ *     The name of the input stream (file) being requested.
+ *
+ * @return
+ *     Zero on success, non-zero on error.
+ */
+int guac_rdp_upload_put_handler(guac_client* client, guac_object* object,
+        guac_stream* stream, char* mimetype, char* name);
+
+#endif
+
diff --git a/src/protocols/rdp/rdp_svc.c b/src/protocols/rdp/rdp_svc.c
new file mode 100644
index 0000000..3a95406
--- /dev/null
+++ b/src/protocols/rdp/rdp_svc.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+#include "client.h"
+#include "guac_list.h"
+#include "rdp_svc.h"
+
+#include <freerdp/utils/svc_plugin.h>
+#include <guacamole/client.h>
+
+#ifdef ENABLE_WINPR
+#include <winpr/stream.h>
+#else
+#include "compat/winpr-stream.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+guac_rdp_svc* guac_rdp_alloc_svc(guac_client* client, char* name) {
+
+    guac_rdp_svc* svc = malloc(sizeof(guac_rdp_svc));
+
+    /* Init SVC */
+    svc->client = client;
+    svc->plugin = NULL;
+    svc->input_pipe = NULL;
+    svc->output_pipe = NULL;
+
+    /* Warn about name length */
+    if (strnlen(name, GUAC_RDP_SVC_MAX_LENGTH+1) > GUAC_RDP_SVC_MAX_LENGTH)
+        guac_client_log(client, GUAC_LOG_INFO,
+                "Static channel name \"%s\" exceeds maximum of %i characters "
+                "and will be truncated",
+                name, GUAC_RDP_SVC_MAX_LENGTH);
+
+    /* Init name */
+    strncpy(svc->name, name, GUAC_RDP_SVC_MAX_LENGTH);
+    svc->name[GUAC_RDP_SVC_MAX_LENGTH] = '\0';
+
+    return svc;
+}
+
+void guac_rdp_free_svc(guac_rdp_svc* svc) {
+    free(svc);
+}
+
+void guac_rdp_add_svc(guac_client* client, guac_rdp_svc* svc) {
+
+    rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
+
+    /* Add to list of available SVC */
+    guac_common_list_lock(client_data->available_svc);
+    guac_common_list_add(client_data->available_svc, svc);
+    guac_common_list_unlock(client_data->available_svc);
+
+}
+
+guac_rdp_svc* guac_rdp_get_svc(guac_client* client, const char* name) {
+
+    rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
+    guac_common_list_element* current;
+    guac_rdp_svc* found = NULL;
+
+    /* For each available SVC */
+    guac_common_list_lock(client_data->available_svc);
+    current = client_data->available_svc->head;
+    while (current != NULL) {
+
+        /* If name matches, found */
+        guac_rdp_svc* current_svc = (guac_rdp_svc*) current->data;
+        if (strcmp(current_svc->name, name) == 0) {
+            found = current_svc;
+            break;
+        }
+
+        current = current->next;
+
+    }
+    guac_common_list_unlock(client_data->available_svc);
+
+    return found;
+
+}
+
+guac_rdp_svc* guac_rdp_remove_svc(guac_client* client, const char* name) {
+
+    rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
+    guac_common_list_element* current;
+    guac_rdp_svc* found = NULL;
+
+    /* For each available SVC */
+    guac_common_list_lock(client_data->available_svc);
+    current = client_data->available_svc->head;
+    while (current != NULL) {
+
+        /* If name matches, remove entry */
+        guac_rdp_svc* current_svc = (guac_rdp_svc*) current->data;
+        if (strcmp(current_svc->name, name) == 0) {
+            guac_common_list_remove(client_data->available_svc, current);
+            found = current_svc;
+            break;
+        }
+
+        current = current->next;
+
+    }
+    guac_common_list_unlock(client_data->available_svc);
+
+    /* Return removed entry, if any */
+    return found;
+
+}
+
+void guac_rdp_svc_write(guac_rdp_svc* svc, void* data, int length) {
+
+    wStream* output_stream;
+
+    /* Do not write of plugin not associated */
+    if (svc->plugin == NULL) {
+        guac_client_log(svc->client, GUAC_LOG_ERROR,
+                "Channel \"%s\" output dropped.",
+                svc->name);
+        return;
+    }
+
+    /* Build packet */
+    output_stream = Stream_New(NULL, length);
+    Stream_Write(output_stream, data, length);
+
+    /* Send packet */
+    svc_plugin_send(svc->plugin, output_stream);
+
+}
+
diff --git a/src/protocols/rdp/rdp_svc.h b/src/protocols/rdp/rdp_svc.h
new file mode 100644
index 0000000..7b6e591
--- /dev/null
+++ b/src/protocols/rdp/rdp_svc.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef __GUAC_RDP_RDP_SVC_H
+#define __GUAC_RDP_RDP_SVC_H
+
+#include "config.h"
+
+#include <freerdp/utils/svc_plugin.h>
+#include <guacamole/client.h>
+#include <guacamole/stream.h>
+
+/**
+ * The maximum number of characters to allow for each channel name.
+ */
+#define GUAC_RDP_SVC_MAX_LENGTH 7
+
+/**
+ * Structure describing a static virtual channel, and the corresponding
+ * Guacamole pipes.
+ */
+typedef struct guac_rdp_svc {
+
+    /**
+     * Reference to the client owning this static channel.
+     */
+    guac_client* client;
+
+    /**
+     * Reference to associated SVC plugin.
+     */
+    rdpSvcPlugin* plugin;
+
+    /**
+     * The name of the RDP channel in use, and the name to use for each pipe.
+     */
+    char name[GUAC_RDP_SVC_MAX_LENGTH+1];
+
+    /**
+     * The pipe opened by the Guacamole client, if any. This should be
+     * opened in response to the output pipe.
+     */
+    guac_stream* input_pipe;
+
+    /**
+     * The output pipe, opened when the RDP server receives a connection to
+     * the static channel.
+     */
+    guac_stream* output_pipe;
+
+} guac_rdp_svc;
+
+/**
+ * Allocate a new SVC with the given name.
+ */
+guac_rdp_svc* guac_rdp_alloc_svc(guac_client* client, char* name);
+
+/**
+ * Free the given SVC.
+ */
+void guac_rdp_free_svc(guac_rdp_svc* svc);
+
+/**
+ * Add the given SVC to the list of all available SVCs.
+ */
+void guac_rdp_add_svc(guac_client* client, guac_rdp_svc* svc);
+
+/**
+ * Retrieve the SVC with the given name from the list stored in the client.
+ */
+guac_rdp_svc* guac_rdp_get_svc(guac_client* client, const char* name);
+
+/**
+ * Remove the SVC with the given name from the list stored in the client.
+ */
+guac_rdp_svc* guac_rdp_remove_svc(guac_client* client, const char* name);
+
+/**
+ * Write the given blob of data to the virtual channel.
+ */
+void guac_rdp_svc_write(guac_rdp_svc* svc, void* data, int length);
+
+#endif
+
diff --git a/src/protocols/rdp/resolution.c b/src/protocols/rdp/resolution.c
new file mode 100644
index 0000000..1d36a4f
--- /dev/null
+++ b/src/protocols/rdp/resolution.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "client.h"
+#include "resolution.h"
+
+#include <guacamole/client.h>
+
+int guac_rdp_resolution_reasonable(guac_client* client, int resolution) {
+
+    int width  = client->info.optimal_width;
+    int height = client->info.optimal_height;
+
+    /* Convert client pixels to remote pixels */
+    width  = width  * resolution / client->info.optimal_resolution;
+    height = height * resolution / client->info.optimal_resolution;
+
+    /*
+     * Resolution is reasonable if the same as the client optimal resolution
+     * OR if the resulting display area is reasonable
+     */
+    return client->info.optimal_resolution == resolution
+        || width*height >= GUAC_RDP_REASONABLE_AREA;
+
+}
+
+int guac_rdp_suggest_resolution(guac_client* client) {
+
+    /* Prefer RDP's native resolution */
+    if (guac_rdp_resolution_reasonable(client, GUAC_RDP_NATIVE_RESOLUTION))
+        return GUAC_RDP_NATIVE_RESOLUTION;
+
+    /* If native resolution is too tiny, try higher resolution */
+    if (guac_rdp_resolution_reasonable(client, GUAC_RDP_HIGH_RESOLUTION))
+        return GUAC_RDP_HIGH_RESOLUTION;
+
+    /* Fallback to client-suggested resolution */
+    return client->info.optimal_resolution;
+
+}
+
diff --git a/src/protocols/rdp/resolution.h b/src/protocols/rdp/resolution.h
new file mode 100644
index 0000000..7f637d0
--- /dev/null
+++ b/src/protocols/rdp/resolution.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GUAC_RDP_RESOLUTION_H
+#define GUAC_RDP_RESOLUTION_H
+
+#include <guacamole/client.h>
+
+/**
+ * Returns whether the given resolution is reasonable for the given client,
+ * based on arbitrary criteria for reasonability.
+ *
+ * @param client The guac_client to test the given resolution against.
+ * @param resolution The resolution to test, in DPI.
+ * @return Non-zero if the resolution is reasonable, zero otherwise.
+ */
+int guac_rdp_resolution_reasonable(guac_client* client, int resolution);
+
+/**
+ * Returns a reasonable resolution for the remote display, given the size and
+ * resolution of a guac_client.
+ *
+ * @param client The guac_client whose size and resolution shall be used to
+ *               determine an appropriate remote display resolution.
+ * @return A reasonable resolution for the remote display, in DPI.
+ */
+int guac_rdp_suggest_resolution(guac_client* client);
+
+#endif
+
diff --git a/src/protocols/rdp/sftp.c b/src/protocols/rdp/sftp.c
new file mode 100644
index 0000000..a394ee6
--- /dev/null
+++ b/src/protocols/rdp/sftp.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "client.h"
+#include "guac_sftp.h"
+#include "sftp.h"
+
+#include <guacamole/client.h>
+#include <guacamole/stream.h>
+
+int guac_rdp_sftp_file_handler(guac_client* client, guac_stream* stream,
+        char* mimetype, char* filename) {
+
+    rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
+    guac_object* filesystem = client_data->sftp_filesystem;
+
+    /* Handle file upload */
+    return guac_common_ssh_sftp_handle_file_stream(filesystem, stream,
+            mimetype, filename);
+
+}
+
diff --git a/src/protocols/rdp/sftp.h b/src/protocols/rdp/sftp.h
new file mode 100644
index 0000000..8172ec5
--- /dev/null
+++ b/src/protocols/rdp/sftp.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GUAC_RDP_SFTP_H
+#define GUAC_RDP_SFTP_H
+
+#include "config.h"
+
+#include <guacamole/client.h>
+#include <guacamole/stream.h>
+
+/**
+ * Handles an incoming stream from a Guacamole "file" instruction, saving the
+ * contents of that stream to the file having the given name.
+ *
+ * @param client
+ *     The client receiving the uploaded file.
+ *
+ * @param stream
+ *     The stream through which the uploaded file data will be received.
+ *
+ * @param mimetype
+ *     The mimetype of the data being received.
+ *
+ * @param filename
+ *     The filename of the file to write to.
+ *
+ * @return
+ *     Zero if the incoming stream has been handled successfully, non-zero on
+ *     failure.
+ */
+int guac_rdp_sftp_file_handler(guac_client* client, guac_stream* stream,
+        char* mimetype, char* filename);
+
+#endif
+
diff --git a/src/protocols/rdp/unicode.c b/src/protocols/rdp/unicode.c
new file mode 100644
index 0000000..bcee36a
--- /dev/null
+++ b/src/protocols/rdp/unicode.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <stdint.h>
+
+#include <guacamole/unicode.h>
+
+void guac_rdp_utf16_to_utf8(const unsigned char* utf16, int length,
+        char* utf8, int size) {
+
+    int i;
+    const uint16_t* in_codepoint = (const uint16_t*) utf16;
+
+    /* For each UTF-16 character */
+    for (i=0; i<length; i++) {
+
+        /* Get next codepoint */
+        uint16_t codepoint = *(in_codepoint++);
+
+        /* Save codepoint as UTF-8 */
+        int bytes_written = guac_utf8_write(codepoint, utf8, size);
+        size -= bytes_written;
+        utf8 += bytes_written;
+
+    }
+
+    /* Save NULL terminator */
+    *(utf8++) = 0;
+
+}
+
+void guac_rdp_utf8_to_utf16(const unsigned char* utf8, int length,
+        char* utf16, int size) {
+
+    int i;
+    uint16_t* out_codepoint = (uint16_t*) utf16;
+
+    /* For each UTF-8 character */
+    for (i=0; i<length; i++) {
+
+        /* Get next codepoint */
+        int codepoint;
+        utf8 += guac_utf8_read((const char*) utf8, 4, &codepoint);
+
+        /* Save codepoint as UTF-16 */
+        *(out_codepoint++) = codepoint;
+
+        /* Stop if buffer full */
+        size -= 2;
+        if (size < 2)
+            break;
+
+    }
+
+}
+
diff --git a/src/protocols/rdp/unicode.h b/src/protocols/rdp/unicode.h
new file mode 100644
index 0000000..f6d24b9
--- /dev/null
+++ b/src/protocols/rdp/unicode.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#include "config.h"
+
+/**
+ * Convert the given number of UTF-16 characters to UTF-8 characters.
+ */
+void guac_rdp_utf16_to_utf8(const unsigned char* utf16, int length,
+        char* utf8, int size);
+
+/**
+ * Convert the given number of UTF-8 characters to UTF-16 characters.
+ */
+void guac_rdp_utf8_to_utf16(const unsigned char* utf8, int length,
+        char* utf16, int size);
+
diff --git a/src/protocols/rdp/wav_encoder.c b/src/protocols/rdp/wav_encoder.c
deleted file mode 100644
index 0a30667..0000000
--- a/src/protocols/rdp/wav_encoder.c
+++ /dev/null
@@ -1,201 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#define WAV_BUFFER_SIZE 0x4000
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <guacamole/client.h>
-#include <guacamole/protocol.h>
-
-#include "audio.h"
-#include "wav_encoder.h"
-
-void wav_encoder_begin_handler(audio_stream* audio) {
-
-    /* Allocate stream state */
-    wav_encoder_state* state = (wav_encoder_state*)
-        malloc(sizeof(wav_encoder_state));
-
-    /* Initialize buffer */
-    state->length = WAV_BUFFER_SIZE;
-    state->used = 0;
-    state->data_buffer = (unsigned char*) malloc(state->length);
-
-    audio->data = state;
-
-}
-
-void _wav_encoder_write_le(unsigned char* buffer, int value, int length) {
-
-    int offset;
-
-    /* Write all bytes in the given value in little-endian byte order */
-    for (offset=0; offset<length; offset++) {
-
-        /* Store byte */
-        *buffer = value & 0xFF;
-
-        /* Move to next byte */
-        value >>= 8;
-        buffer++;
-
-    }
-
-}
-
-void wav_encoder_end_handler(audio_stream* audio) {
-
-    /*
-     * Static header init
-     */
-
-    wav_encoder_riff_header riff_header = {
-        .chunk_id     = "RIFF",
-        .chunk_format = "WAVE"
-    };
-
-    wav_encoder_fmt_header fmt_header = {
-        .subchunk_id     = "fmt ",
-        .subchunk_size   = {0x10, 0x00, 0x00, 0x00}, /* 16 */
-        .subchunk_format = {0x01, 0x00}              /* 1 = PCM */
-    };
-
-    wav_encoder_data_header data_header = {
-        .subchunk_id = "data"
-    };
-
-    /* Get state */
-    wav_encoder_state* state = (wav_encoder_state*) audio->data;
-
-    /*
-     * RIFF HEADER
-     */
-
-    /* Chunk size */
-    _wav_encoder_write_le(riff_header.chunk_size,
-            4 + sizeof(fmt_header) + sizeof(data_header) + state->used,
-            sizeof(riff_header.chunk_size));
-
-    audio_stream_write_encoded(audio,
-            (unsigned char*) &riff_header,
-            sizeof(riff_header));
-
-    /*
-     * FMT HEADER
-     */
-
-    /* Channels */
-    _wav_encoder_write_le(fmt_header.subchunk_channels,
-            audio->channels, sizeof(fmt_header.subchunk_channels));
-
-    /* Sample rate */
-    _wav_encoder_write_le(fmt_header.subchunk_sample_rate,
-            audio->rate, sizeof(fmt_header.subchunk_sample_rate));
-
-    /* Byte rate */
-    _wav_encoder_write_le(fmt_header.subchunk_byte_rate,
-            audio->rate * audio->channels * audio->bps / 8,
-            sizeof(fmt_header.subchunk_byte_rate));
-
-    /* Block align */
-    _wav_encoder_write_le(fmt_header.subchunk_block_align,
-            audio->channels * audio->bps / 8,
-            sizeof(fmt_header.subchunk_block_align));
-
-    /* Bits per second */
-    _wav_encoder_write_le(fmt_header.subchunk_bps,
-            audio->bps, sizeof(fmt_header.subchunk_bps));
-
-    audio_stream_write_encoded(audio,
-            (unsigned char*) &fmt_header,
-            sizeof(fmt_header));
-
-    /*
-     * DATA HEADER
-     */
-
-    /* PCM data size */
-    _wav_encoder_write_le(data_header.subchunk_size,
-            state->used, sizeof(data_header.subchunk_size));
-
-    audio_stream_write_encoded(audio,
-            (unsigned char*) &data_header,
-            sizeof(data_header));
-
-    /* Write .wav data */
-    audio_stream_write_encoded(audio, state->data_buffer, state->used);
-
-    /* Free stream state */
-    free(state);
-
-}
-
-void wav_encoder_write_handler(audio_stream* audio, 
-        unsigned char* pcm_data, int length) {
-
-    /* Get state */
-    wav_encoder_state* state = (wav_encoder_state*) audio->data;
-
-    /* Increase size of buffer if necessary */
-    if (state->used + length > state->length) {
-
-        /* Increase to double concatenated size to accomodate */
-        state->length = (state->length + length)*2;
-        state->data_buffer = realloc(state->data_buffer,
-                state->length);
-
-    }
-
-    /* Append to buffer */
-    memcpy(&(state->data_buffer[state->used]), pcm_data, length);
-    state->used += length;
-
-}
-
-/* Encoder handlers */
-audio_encoder _wav_encoder = {
-    .mimetype      = "audio/wav",
-    .begin_handler = wav_encoder_begin_handler,
-    .write_handler = wav_encoder_write_handler,
-    .end_handler   = wav_encoder_end_handler
-};
-
-/* Actual encoder */
-audio_encoder* wav_encoder = &_wav_encoder;
-
diff --git a/src/protocols/rdp/wav_encoder.h b/src/protocols/rdp/wav_encoder.h
deleted file mode 100644
index ce575ae..0000000
--- a/src/protocols/rdp/wav_encoder.h
+++ /dev/null
@@ -1,144 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-rdp.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef __GUAC_WAV_ENCODER_H
-#define __GUAC_WAV_ENCODER_H
-
-#include "audio.h"
-
-typedef struct wav_encoder_riff_header {
-
-    /**
-     * The RIFF chunk header, normally the string "RIFF".
-     */
-    unsigned char chunk_id[4];
-
-    /**
-     * Size of the entire file, not including chunk_id or chunk_size.
-     */
-    unsigned char chunk_size[4];
-
-    /**
-     * The format of this file, normally the string "WAVE".
-     */
-    unsigned char chunk_format[4];
-
-} wav_encoder_riff_header;
-
-typedef struct wav_encoder_fmt_header {
-
-    /**
-     * ID of this subchunk. For the fmt subchunk, this should be "fmt ".
-     */
-    unsigned char subchunk_id[4];
-
-    /**
-     * The size of the rest of this subchunk. For PCM, this will be 16.
-     */
-    unsigned char subchunk_size[4];
-
-    /**
-     * Format of this subchunk. For PCM, this will be 1.
-     */
-    unsigned char subchunk_format[2];
-
-    /**
-     * The number of channels in the PCM data.
-     */
-    unsigned char subchunk_channels[2];
-
-    /**
-     * The sample rate of the PCM data.
-     */
-    unsigned char subchunk_sample_rate[4];
-
-    /**
-     * The sample rate of the PCM data in bytes per second.
-     */
-    unsigned char subchunk_byte_rate[4];
-
-    /**
-     * The number of bytes per sample.
-     */
-    unsigned char subchunk_block_align[2];
-
-    /**
-     * The number of bits per sample.
-     */
-    unsigned char subchunk_bps[2];
-
-} wav_encoder_fmt_header;
-
-typedef struct wav_encoder_state {
-
-    /**
-     * Arbitrary PCM data available for writing when the overall WAV is
-     * flushed.
-     */
-    unsigned char* data_buffer;
-
-    /**
-     * The number of bytes currently present in the data buffer.
-     */
-    int used;
-
-    /**
-     * The total number of bytes that can be written into the data buffer
-     * without requiring resizing.
-     */
-    int length;
-
-} wav_encoder_state;
-
-typedef struct wav_encoder_data_header {
-
-    /**
-     * ID of this subchunk. For the data subchunk, this should be "data".
-     */
-    unsigned char subchunk_id[4];
-
-    /**
-     * The number of bytes in the PCM data.
-     */
-    unsigned char subchunk_size[4];
-
-} wav_encoder_data_header;
-
-extern audio_encoder* wav_encoder;
-
-#endif
-
diff --git a/src/protocols/ssh/Makefile.am b/src/protocols/ssh/Makefile.am
index da8d1ea..ad1a2f5 100644
--- a/src/protocols/ssh/Makefile.am
+++ b/src/protocols/ssh/Makefile.am
@@ -1,76 +1,65 @@
-# ***** BEGIN LICENSE BLOCK *****
-# Version: MPL 1.1/GPL 2.0/LGPL 2.1
 #
-# The contents of this file are subject to the Mozilla Public License Version
-# 1.1 (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.mozilla.org/MPL/
+# Copyright (C) 2015 Glyptodon LLC
 #
-# Software distributed under the License is distributed on an "AS IS" basis,
-# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-# for the specific language governing rights and limitations under the
-# License.
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
 #
-# The Original Code is libguac-client-ssh.
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
 #
-# The Initial Developer of the Original Code is
-# Michael Jumper.
-# Portions created by the Initial Developer are Copyright (C) 2011
-# the Initial Developer. All Rights Reserved.
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
 #
-# Contributor(s):
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either the GNU General Public License Version 2 or later (the "GPL"), or
-# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-# in which case the provisions of the GPL or the LGPL are applicable instead
-# of those above. If you wish to allow use of your version of this file only
-# under the terms of either the GPL or the LGPL, and not to allow others to
-# use your version of this file under the terms of the MPL, indicate your
-# decision by deleting the provisions above and replace them with the notice
-# and other provisions required by the GPL or the LGPL. If you do not delete
-# the provisions above, a recipient may use your version of this file under
-# the terms of any one of the MPL, the GPL or the LGPL.
-#
-# ***** END LICENSE BLOCK *****
 
 AUTOMAKE_OPTIONS = foreign
-
 ACLOCAL_AMFLAGS = -I m4
 
 lib_LTLIBRARIES = libguac-client-ssh.la
 
 libguac_client_ssh_la_SOURCES = \
-    blank.c                     \
-    buffer.c                    \
-    char_mappings.c             \
     client.c                    \
-    common.c                    \
-    cursor.c                    \
-    display.c                   \
+    clipboard.c                 \
     guac_handlers.c             \
-    ibar.c                      \
-    ssh_client.c                \
-    terminal.c                  \
-    terminal_handlers.c
+    sftp.c                      \
+    ssh_client.c
 
 noinst_HEADERS =                \
-    blank.h                     \
-    buffer.h                    \
-    char_mappings.h             \
     client.h                    \
-    common.h                    \
-    cursor.h                    \
-    display.h                   \
+    clipboard.h                 \
     guac_handlers.h             \
-    ibar.h                      \
-    libssh_compat.h             \
-    ssh_client.h                \
-    terminal.h                  \
-    terminal_handlers.h         \
-    types.h
+    sftp.h                      \
+    ssh_client.h
+
+# Add agent sources if enabled
+if ENABLE_SSH_AGENT
+libguac_client_ssh_la_SOURCES += ssh_agent.c
+noinst_HEADERS += ssh_agent.h
+endif
+
+libguac_client_ssh_la_CFLAGS = \
+    -Werror -Wall -Iinclude    \
+    @COMMON_SSH_INCLUDE@       \
+    @LIBGUAC_INCLUDE@          \
+    @TERMINAL_INCLUDE@
+
+libguac_client_ssh_la_LIBADD = \
+    @COMMON_LTLIB@             \
+    @COMMON_SSH_LTLIB@         \
+    @LIBGUAC_LTLIB@            \
+    @TERMINAL_LTLIB@
 
-libguac_client_ssh_la_CFLAGS = -Werror -Wall -pedantic -Iinclude @PANGO_CFLAGS@ @PANGOCAIRO_CFLAGS@ @LIBGUAC_INCLUDE@
-libguac_client_ssh_la_LIBADD = @LIBGUAC_LTLIB@
-libguac_client_ssh_la_LDFLAGS = -version-info 0:0:0 @SSH_LIBS@ @PTHREAD_LIBS@ @PANGO_LIBS@ @PANGOCAIRO_LIBS@ @CAIRO_LIBS@
+libguac_client_ssh_la_LDFLAGS = \
+    -version-info 0:0:0         \
+    @PTHREAD_LIBS@              \
+    @SSH_LIBS@                  \
+    @SSL_LIBS@
 
diff --git a/src/protocols/ssh/Makefile.in b/src/protocols/ssh/Makefile.in
index 180719b..813d5a4 100644
--- a/src/protocols/ssh/Makefile.in
+++ b/src/protocols/ssh/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.6 from Makefile.am.
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software
-# Foundation, Inc.
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
@@ -15,61 +14,75 @@
 
 @SET_MAKE@
 
-# ***** BEGIN LICENSE BLOCK *****
-# Version: MPL 1.1/GPL 2.0/LGPL 2.1
-#
-# The contents of this file are subject to the Mozilla Public License Version
-# 1.1 (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.mozilla.org/MPL/
 #
-# Software distributed under the License is distributed on an "AS IS" basis,
-# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-# for the specific language governing rights and limitations under the
-# License.
+# Copyright (C) 2015 Glyptodon LLC
 #
-# The Original Code is libguac-client-ssh.
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
 #
-# The Initial Developer of the Original Code is
-# Michael Jumper.
-# Portions created by the Initial Developer are Copyright (C) 2011
-# the Initial Developer. All Rights Reserved.
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
 #
-# Contributor(s):
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
 #
-# Alternatively, the contents of this file may be used under the terms of
-# either the GNU General Public License Version 2 or later (the "GPL"), or
-# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-# in which case the provisions of the GPL or the LGPL are applicable instead
-# of those above. If you wish to allow use of your version of this file only
-# under the terms of either the GPL or the LGPL, and not to allow others to
-# use your version of this file under the terms of the MPL, indicate your
-# decision by deleting the provisions above and replace them with the notice
-# and other provisions required by the GPL or the LGPL. If you do not delete
-# the provisions above, a recipient may use your version of this file under
-# the terms of any one of the MPL, the GPL or the LGPL.
-#
-# ***** END LICENSE BLOCK *****
 
 
 VPATH = @srcdir@
-am__make_dryrun = \
-  { \
-    am__dry=no; \
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
     case $$MAKEFLAGS in \
       *\\[\ \	]*) \
-        echo 'am--echo: ; @echo "AM"  OK' | $(MAKE) -f - 2>/dev/null \
-          | grep '^AM OK$$' >/dev/null || am__dry=yes;; \
-      *) \
-        for am__flg in $$MAKEFLAGS; do \
-          case $$am__flg in \
-            *=*|--*) ;; \
-            *n*) am__dry=yes; break;; \
-          esac; \
-        done;; \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
     esac; \
-    test $$am__dry = yes; \
-  }
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
 pkgdatadir = $(datadir)/@PACKAGE@
 pkgincludedir = $(includedir)/@PACKAGE@
 pkglibdir = $(libdir)/@PACKAGE@
@@ -88,9 +101,13 @@ PRE_UNINSTALL = :
 POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
+
+# Add agent sources if enabled
+ at ENABLE_SSH_AGENT_TRUE@am__append_1 = ssh_agent.c
+ at ENABLE_SSH_AGENT_TRUE@am__append_2 = ssh_agent.h
 subdir = src/protocols/ssh
-DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
-	$(srcdir)/Makefile.in
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(top_srcdir)/depcomp $(am__noinst_HEADERS_DIST)
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
 	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
@@ -99,6 +116,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
 CONFIG_CLEAN_FILES =
 CONFIG_CLEAN_VPATH_FILES =
 am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
@@ -131,49 +149,91 @@ am__uninstall_files_from_dir = { \
 am__installdirs = "$(DESTDIR)$(libdir)"
 LTLIBRARIES = $(lib_LTLIBRARIES)
 libguac_client_ssh_la_DEPENDENCIES =
-am_libguac_client_ssh_la_OBJECTS = libguac_client_ssh_la-blank.lo \
-	libguac_client_ssh_la-buffer.lo \
-	libguac_client_ssh_la-char_mappings.lo \
-	libguac_client_ssh_la-client.lo \
-	libguac_client_ssh_la-common.lo \
-	libguac_client_ssh_la-cursor.lo \
-	libguac_client_ssh_la-display.lo \
+am__libguac_client_ssh_la_SOURCES_DIST = client.c clipboard.c \
+	guac_handlers.c sftp.c ssh_client.c ssh_agent.c
+ at ENABLE_SSH_AGENT_TRUE@am__objects_1 =  \
+ at ENABLE_SSH_AGENT_TRUE@	libguac_client_ssh_la-ssh_agent.lo
+am_libguac_client_ssh_la_OBJECTS = libguac_client_ssh_la-client.lo \
+	libguac_client_ssh_la-clipboard.lo \
 	libguac_client_ssh_la-guac_handlers.lo \
-	libguac_client_ssh_la-ibar.lo \
-	libguac_client_ssh_la-ssh_client.lo \
-	libguac_client_ssh_la-terminal.lo \
-	libguac_client_ssh_la-terminal_handlers.lo
+	libguac_client_ssh_la-sftp.lo \
+	libguac_client_ssh_la-ssh_client.lo $(am__objects_1)
 libguac_client_ssh_la_OBJECTS = $(am_libguac_client_ssh_la_OBJECTS)
-libguac_client_ssh_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
-	$(LIBTOOLFLAGS) --mode=link $(CCLD) \
+AM_V_lt = $(am__v_lt_ at AM_V@)
+am__v_lt_ = $(am__v_lt_ at AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+libguac_client_ssh_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
 	$(libguac_client_ssh_la_CFLAGS) $(CFLAGS) \
 	$(libguac_client_ssh_la_LDFLAGS) $(LDFLAGS) -o $@
-DEFAULT_INCLUDES = -I. at am__isrc@
+AM_V_P = $(am__v_P_ at AM_V@)
+am__v_P_ = $(am__v_P_ at AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_ at AM_V@)
+am__v_GEN_ = $(am__v_GEN_ at AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_ at AM_V@)
+am__v_at_ = $(am__v_at_ at AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I. at am__isrc@ -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/depcomp
 am__depfiles_maybe = depfiles
 am__mv = mv -f
 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
 	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
-	--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
-	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_ at AM_V@)
+am__v_CC_ = $(am__v_CC_ at AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
 CCLD = $(CC)
-LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
-	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
-	$(LDFLAGS) -o $@
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_ at AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_ at AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
 SOURCES = $(libguac_client_ssh_la_SOURCES)
-DIST_SOURCES = $(libguac_client_ssh_la_SOURCES)
+DIST_SOURCES = $(am__libguac_client_ssh_la_SOURCES_DIST)
 am__can_run_installinfo = \
   case $$AM_UPDATE_INFO_DIR in \
     n|no|NO) false;; \
     *) (install-info --version) >/dev/null 2>&1;; \
   esac
+am__noinst_HEADERS_DIST = client.h clipboard.h guac_handlers.h sftp.h \
+	ssh_client.h ssh_agent.h
 HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
 ETAGS = etags
 CTAGS = ctags
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 ACLOCAL = @ACLOCAL@
 AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -183,6 +243,10 @@ CAIRO_LIBS = @CAIRO_LIBS@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+COMMON_INCLUDE = @COMMON_INCLUDE@
+COMMON_LTLIB = @COMMON_LTLIB@
+COMMON_SSH_INCLUDE = @COMMON_SSH_INCLUDE@
+COMMON_SSH_LTLIB = @COMMON_SSH_LTLIB@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
 CUNIT_LIBS = @CUNIT_LIBS@
@@ -190,7 +254,6 @@ CYGPATH_W = @CYGPATH_W@
 DEFS = @DEFS@
 DEPDIR = @DEPDIR@
 DLLTOOL = @DLLTOOL@
-DL_LIBS = @DL_LIBS@
 DSYMUTIL = @DSYMUTIL@
 DUMPBIN = @DUMPBIN@
 ECHO_C = @ECHO_C@
@@ -205,8 +268,10 @@ INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
 INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+JPEG_LIBS = @JPEG_LIBS@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
+LIBADD_DLOPEN = @LIBADD_DLOPEN@
 LIBGUAC_INCLUDE = @LIBGUAC_INCLUDE@
 LIBGUAC_LTLIB = @LIBGUAC_LTLIB@
 LIBOBJS = @LIBOBJS@
@@ -217,6 +282,7 @@ LN_S = @LN_S@
 LTLIBOBJS = @LTLIBOBJS@
 MAKEINFO = @MAKEINFO@
 MANIFEST_TOOL = @MANIFEST_TOOL@
+MATH_LIBS = @MATH_LIBS@
 MKDIR_P = @MKDIR_P@
 NM = @NM@
 NMEDIT = @NMEDIT@
@@ -250,9 +316,14 @@ SHELL = @SHELL@
 SSH_LIBS = @SSH_LIBS@
 SSL_LIBS = @SSL_LIBS@
 STRIP = @STRIP@
+TELNET_LIBS = @TELNET_LIBS@
+TERMINAL_INCLUDE = @TERMINAL_INCLUDE@
+TERMINAL_LTLIB = @TERMINAL_LTLIB@
+UUID_LIBS = @UUID_LIBS@
 VERSION = @VERSION@
 VNC_LIBS = @VNC_LIBS@
 VORBIS_LIBS = @VORBIS_LIBS@
+WEBP_LIBS = @WEBP_LIBS@
 abs_builddir = @abs_builddir@
 abs_srcdir = @abs_srcdir@
 abs_top_builddir = @abs_top_builddir@
@@ -309,39 +380,28 @@ top_srcdir = @top_srcdir@
 AUTOMAKE_OPTIONS = foreign
 ACLOCAL_AMFLAGS = -I m4
 lib_LTLIBRARIES = libguac-client-ssh.la
-libguac_client_ssh_la_SOURCES = \
-    blank.c                     \
-    buffer.c                    \
-    char_mappings.c             \
-    client.c                    \
-    common.c                    \
-    cursor.c                    \
-    display.c                   \
-    guac_handlers.c             \
-    ibar.c                      \
-    ssh_client.c                \
-    terminal.c                  \
-    terminal_handlers.c
-
-noinst_HEADERS = \
-    blank.h                     \
-    buffer.h                    \
-    char_mappings.h             \
-    client.h                    \
-    common.h                    \
-    cursor.h                    \
-    display.h                   \
-    guac_handlers.h             \
-    ibar.h                      \
-    libssh_compat.h             \
-    ssh_client.h                \
-    terminal.h                  \
-    terminal_handlers.h         \
-    types.h
-
-libguac_client_ssh_la_CFLAGS = -Werror -Wall -pedantic -Iinclude @PANGO_CFLAGS@ @PANGOCAIRO_CFLAGS@ @LIBGUAC_INCLUDE@
-libguac_client_ssh_la_LIBADD = @LIBGUAC_LTLIB@
-libguac_client_ssh_la_LDFLAGS = -version-info 0:0:0 @SSH_LIBS@ @PTHREAD_LIBS@ @PANGO_LIBS@ @PANGOCAIRO_LIBS@ @CAIRO_LIBS@
+libguac_client_ssh_la_SOURCES = client.c clipboard.c guac_handlers.c \
+	sftp.c ssh_client.c $(am__append_1)
+noinst_HEADERS = client.h clipboard.h guac_handlers.h sftp.h \
+	ssh_client.h $(am__append_2)
+libguac_client_ssh_la_CFLAGS = \
+    -Werror -Wall -Iinclude    \
+    @COMMON_SSH_INCLUDE@       \
+    @LIBGUAC_INCLUDE@          \
+    @TERMINAL_INCLUDE@
+
+libguac_client_ssh_la_LIBADD = \
+    @COMMON_LTLIB@             \
+    @COMMON_SSH_LTLIB@         \
+    @LIBGUAC_LTLIB@            \
+    @TERMINAL_LTLIB@
+
+libguac_client_ssh_la_LDFLAGS = \
+    -version-info 0:0:0         \
+    @PTHREAD_LIBS@              \
+    @SSH_LIBS@                  \
+    @SSL_LIBS@
+
 all: all-am
 
 .SUFFIXES:
@@ -376,6 +436,7 @@ $(top_srcdir)/configure:  $(am__configure_deps)
 $(ACLOCAL_M4):  $(am__aclocal_m4_deps)
 	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
 $(am__aclocal_m4_deps):
+
 install-libLTLIBRARIES: $(lib_LTLIBRARIES)
 	@$(NORMAL_INSTALL)
 	@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
@@ -402,14 +463,17 @@ uninstall-libLTLIBRARIES:
 
 clean-libLTLIBRARIES:
 	-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
-	@list='$(lib_LTLIBRARIES)'; for p in $$list; do \
-	  dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
-	  test "$$dir" != "$$p" || dir=.; \
-	  echo "rm -f \"$${dir}/so_locations\""; \
-	  rm -f "$${dir}/so_locations"; \
-	done
+	@list='$(lib_LTLIBRARIES)'; \
+	locs=`for p in $$list; do echo $$p; done | \
+	      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+	      sort -u`; \
+	test -z "$$locs" || { \
+	  echo rm -f $${locs}; \
+	  rm -f $${locs}; \
+	}
+
 libguac-client-ssh.la: $(libguac_client_ssh_la_OBJECTS) $(libguac_client_ssh_la_DEPENDENCIES) $(EXTRA_libguac_client_ssh_la_DEPENDENCIES) 
-	$(libguac_client_ssh_la_LINK) -rpath $(libdir) $(libguac_client_ssh_la_OBJECTS) $(libguac_client_ssh_la_LIBADD) $(LIBS)
+	$(AM_V_CCLD)$(libguac_client_ssh_la_LINK) -rpath $(libdir) $(libguac_client_ssh_la_OBJECTS) $(libguac_client_ssh_la_LIBADD) $(LIBS)
 
 mostlyclean-compile:
 	-rm -f *.$(OBJEXT)
@@ -417,123 +481,78 @@ mostlyclean-compile:
 distclean-compile:
 	-rm -f *.tab.c
 
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_ssh_la-blank.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_ssh_la-buffer.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_ssh_la-char_mappings.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_ssh_la-client.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_ssh_la-common.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_ssh_la-cursor.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_ssh_la-display.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_ssh_la-clipboard.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_ssh_la-guac_handlers.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_ssh_la-ibar.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_ssh_la-sftp.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_ssh_la-ssh_agent.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_ssh_la-ssh_client.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_ssh_la-terminal.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_ssh_la-terminal_handlers.Plo at am__quote@
 
 .c.o:
- at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+ at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(COMPILE) -c $<
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(COMPILE) -c -o $@ $<
 
 .c.obj:
- at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+ at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(COMPILE) -c `$(CYGPATH_W) '$<'`
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
 
 .c.lo:
- at am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
-
-libguac_client_ssh_la-blank.lo: blank.c
- at am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -MT libguac_client_ssh_la-blank.lo -MD -MP -MF $(DEPDIR)/libguac_client_ssh_la-blank.Tpo -c -o libguac_client_ssh_la-blank.lo `test -f 'blank.c' || echo '$(srcdir)/'`blank.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libguac_client_ssh_la-blank.Tpo $(DEPDIR)/libguac_client_ssh_la-blank.Plo
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='blank.c' object='libguac_client_ssh_la-blank.lo' libtool=yes @AMDEPBACKSLASH@
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+ at am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -c -o libguac_client_ssh_la-blank.lo `test -f 'blank.c' || echo '$(srcdir)/'`blank.c
-
-libguac_client_ssh_la-buffer.lo: buffer.c
- at am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -MT libguac_client_ssh_la-buffer.lo -MD -MP -MF $(DEPDIR)/libguac_client_ssh_la-buffer.Tpo -c -o libguac_client_ssh_la-buffer.lo `test -f 'buffer.c' || echo '$(srcdir)/'`buffer.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libguac_client_ssh_la-buffer.Tpo $(DEPDIR)/libguac_client_ssh_la-buffer.Plo
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='buffer.c' object='libguac_client_ssh_la-buffer.lo' libtool=yes @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -c -o libguac_client_ssh_la-buffer.lo `test -f 'buffer.c' || echo '$(srcdir)/'`buffer.c
-
-libguac_client_ssh_la-char_mappings.lo: char_mappings.c
- at am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -MT libguac_client_ssh_la-char_mappings.lo -MD -MP -MF $(DEPDIR)/libguac_client_ssh_la-char_mappings.Tpo -c -o libguac_client_ssh_la-char_mappings.lo `test -f 'char_mappings.c' || echo '$(srcdir)/'`char_mappings.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libguac_client_ssh_la-char_mappings.Tpo $(DEPDIR)/libguac_client_ssh_la-char_mappings.Plo
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='char_mappings.c' object='libguac_client_ssh_la-char_mappings.lo' libtool=yes @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -c -o libguac_client_ssh_la-char_mappings.lo `test -f 'char_mappings.c' || echo '$(srcdir)/'`char_mappings.c
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LTCOMPILE) -c -o $@ $<
 
 libguac_client_ssh_la-client.lo: client.c
- at am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -MT libguac_client_ssh_la-client.lo -MD -MP -MF $(DEPDIR)/libguac_client_ssh_la-client.Tpo -c -o libguac_client_ssh_la-client.lo `test -f 'client.c' || echo '$(srcdir)/'`client.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libguac_client_ssh_la-client.Tpo $(DEPDIR)/libguac_client_ssh_la-client.Plo
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='client.c' object='libguac_client_ssh_la-client.lo' libtool=yes @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -c -o libguac_client_ssh_la-client.lo `test -f 'client.c' || echo '$(srcdir)/'`client.c
-
-libguac_client_ssh_la-common.lo: common.c
- at am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -MT libguac_client_ssh_la-common.lo -MD -MP -MF $(DEPDIR)/libguac_client_ssh_la-common.Tpo -c -o libguac_client_ssh_la-common.lo `test -f 'common.c' || echo '$(srcdir)/'`common.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libguac_client_ssh_la-common.Tpo $(DEPDIR)/libguac_client_ssh_la-common.Plo
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='common.c' object='libguac_client_ssh_la-common.lo' libtool=yes @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -c -o libguac_client_ssh_la-common.lo `test -f 'common.c' || echo '$(srcdir)/'`common.c
-
-libguac_client_ssh_la-cursor.lo: cursor.c
- at am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -MT libguac_client_ssh_la-cursor.lo -MD -MP -MF $(DEPDIR)/libguac_client_ssh_la-cursor.Tpo -c -o libguac_client_ssh_la-cursor.lo `test -f 'cursor.c' || echo '$(srcdir)/'`cursor.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libguac_client_ssh_la-cursor.Tpo $(DEPDIR)/libguac_client_ssh_la-cursor.Plo
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='cursor.c' object='libguac_client_ssh_la-cursor.lo' libtool=yes @AMDEPBACKSLASH@
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -MT libguac_client_ssh_la-client.lo -MD -MP -MF $(DEPDIR)/libguac_client_ssh_la-client.Tpo -c -o libguac_client_ssh_la-client.lo `test -f 'client.c' || echo '$(srcdir)/'`client.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_ssh_la-client.Tpo $(DEPDIR)/libguac_client_ssh_la-client.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='client.c' object='libguac_client_ssh_la-client.lo' libtool=yes @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -c -o libguac_client_ssh_la-cursor.lo `test -f 'cursor.c' || echo '$(srcdir)/'`cursor.c
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -c -o libguac_client_ssh_la-client.lo `test -f 'client.c' || echo '$(srcdir)/'`client.c
 
-libguac_client_ssh_la-display.lo: display.c
- at am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -MT libguac_client_ssh_la-display.lo -MD -MP -MF $(DEPDIR)/libguac_client_ssh_la-display.Tpo -c -o libguac_client_ssh_la-display.lo `test -f 'display.c' || echo '$(srcdir)/'`display.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libguac_client_ssh_la-display.Tpo $(DEPDIR)/libguac_client_ssh_la-display.Plo
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='display.c' object='libguac_client_ssh_la-display.lo' libtool=yes @AMDEPBACKSLASH@
+libguac_client_ssh_la-clipboard.lo: clipboard.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -MT libguac_client_ssh_la-clipboard.lo -MD -MP -MF $(DEPDIR)/libguac_client_ssh_la-clipboard.Tpo -c -o libguac_client_ssh_la-clipboard.lo `test -f 'clipboard.c' || echo '$(srcdir)/'`clipboard.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_ssh_la-clipboard.Tpo $(DEPDIR)/libguac_client_ssh_la-clipboard.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='clipboard.c' object='libguac_client_ssh_la-clipboard.lo' libtool=yes @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -c -o libguac_client_ssh_la-display.lo `test -f 'display.c' || echo '$(srcdir)/'`display.c
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -c -o libguac_client_ssh_la-clipboard.lo `test -f 'clipboard.c' || echo '$(srcdir)/'`clipboard.c
 
 libguac_client_ssh_la-guac_handlers.lo: guac_handlers.c
- at am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -MT libguac_client_ssh_la-guac_handlers.lo -MD -MP -MF $(DEPDIR)/libguac_client_ssh_la-guac_handlers.Tpo -c -o libguac_client_ssh_la-guac_handlers.lo `test -f 'guac_handlers.c' || echo '$(srcdir)/'`guac_handlers.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libguac_client_ssh_la-guac_handlers.Tpo $(DEPDIR)/libguac_client_ssh_la-guac_handlers.Plo
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='guac_handlers.c' object='libguac_client_ssh_la-guac_handlers.lo' libtool=yes @AMDEPBACKSLASH@
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -MT libguac_client_ssh_la-guac_handlers.lo -MD -MP -MF $(DEPDIR)/libguac_client_ssh_la-guac_handlers.Tpo -c -o libguac_client_ssh_la-guac_handlers.lo `test -f 'guac_handlers.c' || echo '$(srcdir)/'`guac_handlers.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_ssh_la-guac_handlers.Tpo $(DEPDIR)/libguac_client_ssh_la-guac_handlers.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_handlers.c' object='libguac_client_ssh_la-guac_handlers.lo' libtool=yes @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -c -o libguac_client_ssh_la-guac_handlers.lo `test -f 'guac_handlers.c' || echo '$(srcdir)/'`guac_handlers.c
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -c -o libguac_client_ssh_la-guac_handlers.lo `test -f 'guac_handlers.c' || echo '$(srcdir)/'`guac_handlers.c
 
-libguac_client_ssh_la-ibar.lo: ibar.c
- at am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -MT libguac_client_ssh_la-ibar.lo -MD -MP -MF $(DEPDIR)/libguac_client_ssh_la-ibar.Tpo -c -o libguac_client_ssh_la-ibar.lo `test -f 'ibar.c' || echo '$(srcdir)/'`ibar.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libguac_client_ssh_la-ibar.Tpo $(DEPDIR)/libguac_client_ssh_la-ibar.Plo
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='ibar.c' object='libguac_client_ssh_la-ibar.lo' libtool=yes @AMDEPBACKSLASH@
+libguac_client_ssh_la-sftp.lo: sftp.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -MT libguac_client_ssh_la-sftp.lo -MD -MP -MF $(DEPDIR)/libguac_client_ssh_la-sftp.Tpo -c -o libguac_client_ssh_la-sftp.lo `test -f 'sftp.c' || echo '$(srcdir)/'`sftp.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_ssh_la-sftp.Tpo $(DEPDIR)/libguac_client_ssh_la-sftp.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='sftp.c' object='libguac_client_ssh_la-sftp.lo' libtool=yes @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -c -o libguac_client_ssh_la-ibar.lo `test -f 'ibar.c' || echo '$(srcdir)/'`ibar.c
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -c -o libguac_client_ssh_la-sftp.lo `test -f 'sftp.c' || echo '$(srcdir)/'`sftp.c
 
 libguac_client_ssh_la-ssh_client.lo: ssh_client.c
- at am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -MT libguac_client_ssh_la-ssh_client.lo -MD -MP -MF $(DEPDIR)/libguac_client_ssh_la-ssh_client.Tpo -c -o libguac_client_ssh_la-ssh_client.lo `test -f 'ssh_client.c' || echo '$(srcdir)/'`ssh_client.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libguac_client_ssh_la-ssh_client.Tpo $(DEPDIR)/libguac_client_ssh_la-ssh_client.Plo
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='ssh_client.c' object='libguac_client_ssh_la-ssh_client.lo' libtool=yes @AMDEPBACKSLASH@
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -MT libguac_client_ssh_la-ssh_client.lo -MD -MP -MF $(DEPDIR)/libguac_client_ssh_la-ssh_client.Tpo -c -o libguac_client_ssh_la-ssh_client.lo `test -f 'ssh_client.c' || echo '$(srcdir)/'`ssh_client.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_ssh_la-ssh_client.Tpo $(DEPDIR)/libguac_client_ssh_la-ssh_client.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='ssh_client.c' object='libguac_client_ssh_la-ssh_client.lo' libtool=yes @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -c -o libguac_client_ssh_la-ssh_client.lo `test -f 'ssh_client.c' || echo '$(srcdir)/'`ssh_client.c
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -c -o libguac_client_ssh_la-ssh_client.lo `test -f 'ssh_client.c' || echo '$(srcdir)/'`ssh_client.c
 
-libguac_client_ssh_la-terminal.lo: terminal.c
- at am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -MT libguac_client_ssh_la-terminal.lo -MD -MP -MF $(DEPDIR)/libguac_client_ssh_la-terminal.Tpo -c -o libguac_client_ssh_la-terminal.lo `test -f 'terminal.c' || echo '$(srcdir)/'`terminal.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libguac_client_ssh_la-terminal.Tpo $(DEPDIR)/libguac_client_ssh_la-terminal.Plo
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='terminal.c' object='libguac_client_ssh_la-terminal.lo' libtool=yes @AMDEPBACKSLASH@
+libguac_client_ssh_la-ssh_agent.lo: ssh_agent.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -MT libguac_client_ssh_la-ssh_agent.lo -MD -MP -MF $(DEPDIR)/libguac_client_ssh_la-ssh_agent.Tpo -c -o libguac_client_ssh_la-ssh_agent.lo `test -f 'ssh_agent.c' || echo '$(srcdir)/'`ssh_agent.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_ssh_la-ssh_agent.Tpo $(DEPDIR)/libguac_client_ssh_la-ssh_agent.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='ssh_agent.c' object='libguac_client_ssh_la-ssh_agent.lo' libtool=yes @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -c -o libguac_client_ssh_la-terminal.lo `test -f 'terminal.c' || echo '$(srcdir)/'`terminal.c
-
-libguac_client_ssh_la-terminal_handlers.lo: terminal_handlers.c
- at am__fastdepCC_TRUE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -MT libguac_client_ssh_la-terminal_handlers.lo -MD -MP -MF $(DEPDIR)/libguac_client_ssh_la-terminal_handlers.Tpo -c -o libguac_client_ssh_la-terminal_handlers.lo `test -f 'terminal_handlers.c' || echo '$(srcdir)/'`terminal_handlers.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libguac_client_ssh_la-terminal_handlers.Tpo $(DEPDIR)/libguac_client_ssh_la-terminal_handlers.Plo
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='terminal_handlers.c' object='libguac_client_ssh_la-terminal_handlers.lo' libtool=yes @AMDEPBACKSLASH@
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -c -o libguac_client_ssh_la-terminal_handlers.lo `test -f 'terminal_handlers.c' || echo '$(srcdir)/'`terminal_handlers.c
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_ssh_la_CFLAGS) $(CFLAGS) -c -o libguac_client_ssh_la-ssh_agent.lo `test -f 'ssh_agent.c' || echo '$(srcdir)/'`ssh_agent.c
 
 mostlyclean-libtool:
 	-rm -f *.lo
@@ -541,26 +560,15 @@ mostlyclean-libtool:
 clean-libtool:
 	-rm -rf .libs _libs
 
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
-	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
-	unique=`for i in $$list; do \
-	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-	  done | \
-	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
-	      END { if (nonempty) { for (i in files) print i; }; }'`; \
-	mkid -fID $$unique
-tags: TAGS
-
-TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
-		$(TAGS_FILES) $(LISP)
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
 	set x; \
 	here=`pwd`; \
-	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
-	unique=`for i in $$list; do \
-	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-	  done | \
-	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
-	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	$(am__define_uniq_tagged_files); \
 	shift; \
 	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
 	  test -n "$$unique" || unique=$$empty_fix; \
@@ -572,15 +580,11 @@ TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
 	      $$unique; \
 	  fi; \
 	fi
-ctags: CTAGS
-CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
-		$(TAGS_FILES) $(LISP)
-	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
-	unique=`for i in $$list; do \
-	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-	  done | \
-	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
-	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
 	test -z "$(CTAGS_ARGS)$$unique" \
 	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
 	     $$unique
@@ -589,6 +593,21 @@ GTAGS:
 	here=`$(am__cd) $(top_builddir) && pwd` \
 	  && $(am__cd) $(top_srcdir) \
 	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
 
 distclean-tags:
 	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
@@ -733,19 +752,19 @@ uninstall-am: uninstall-libLTLIBRARIES
 
 .MAKE: install-am install-strip
 
-.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
-	clean-libLTLIBRARIES clean-libtool ctags distclean \
-	distclean-compile distclean-generic distclean-libtool \
-	distclean-tags distdir dvi dvi-am html html-am info info-am \
-	install install-am install-data install-data-am install-dvi \
-	install-dvi-am install-exec install-exec-am install-html \
-	install-html-am install-info install-info-am \
-	install-libLTLIBRARIES install-man install-pdf install-pdf-am \
-	install-ps install-ps-am install-strip installcheck \
-	installcheck-am installdirs maintainer-clean \
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
+	clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \
+	ctags-am distclean distclean-compile distclean-generic \
+	distclean-libtool distclean-tags distdir dvi dvi-am html \
+	html-am info info-am install install-am install-data \
+	install-data-am install-dvi install-dvi-am install-exec \
+	install-exec-am install-html install-html-am install-info \
+	install-info-am install-libLTLIBRARIES install-man install-pdf \
+	install-pdf-am install-ps install-ps-am install-strip \
+	installcheck installcheck-am installdirs maintainer-clean \
 	maintainer-clean-generic mostlyclean mostlyclean-compile \
 	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
-	tags uninstall uninstall-am uninstall-libLTLIBRARIES
+	tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES
 
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
diff --git a/src/protocols/ssh/blank.c b/src/protocols/ssh/blank.c
deleted file mode 100644
index 9929e2b..0000000
--- a/src/protocols/ssh/blank.c
+++ /dev/null
@@ -1,64 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-ssh.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include <cairo/cairo.h>
-#include <guacamole/client.h>
-#include <guacamole/protocol.h>
-#include <guacamole/socket.h>
-
-#include "cursor.h"
-
-guac_ssh_cursor* guac_ssh_create_blank(guac_client* client) {
-
-    guac_socket* socket = client->socket;
-    guac_ssh_cursor* cursor = guac_ssh_cursor_alloc(client);
-
-    /* Set buffer to a single 1x1 transparent rectangle */
-    guac_protocol_send_rect(socket, cursor->buffer, 0, 0, 1, 1);
-    guac_protocol_send_cfill(socket, GUAC_COMP_SRC, cursor->buffer,
-            0x00, 0x00, 0x00, 0x00);
-
-    /* Initialize cursor properties */
-    cursor->width  = 1;
-    cursor->height = 1;
-    cursor->hotspot_x = 0;
-    cursor->hotspot_y = 0;
-
-    return cursor;
-
-}
-
diff --git a/src/protocols/ssh/blank.h b/src/protocols/ssh/blank.h
deleted file mode 100644
index 16995a2..0000000
--- a/src/protocols/ssh/blank.h
+++ /dev/null
@@ -1,52 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-ssh.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef _GUAC_SSH_BLANK_H
-#define _GUAC_SSH_BLANK_H
-
-#include <cairo/cairo.h>
-#include <guacamole/client.h>
-
-/**
- * Creates a new blank cursor, returning the corresponding cursor object.
- *
- * @param client The guac_client to send the cursor to.
- * @return A new cursor which must be free'd via guac_ssh_cursor_free()/
- */
-guac_ssh_cursor* guac_ssh_create_blank(guac_client* client);
-
-#endif
diff --git a/src/protocols/ssh/char_mappings.h b/src/protocols/ssh/char_mappings.h
deleted file mode 100644
index dfbd1eb..0000000
--- a/src/protocols/ssh/char_mappings.h
+++ /dev/null
@@ -1,63 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-ssh.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef _GUAC_SSH_CHAR_MAPPINGS_H
-#define _GUAC_SSH_CHAR_MAPPINGS_H
-
-/**
- * VT100 graphics mapping. Each entry is the corresponding Unicode codepoint
- * for the character N+32, where N is the index of the element in the array.
- * All characters less than 32 are universally mapped to themselves.
- */
-extern const int vt100_map[];
-
-/**
- * Null graphics mapping. Each entry is the corresponding Unicode codepoint
- * for the character N+32, where N is the index of the element in the array.
- * All characters less than 32 are universally mapped to themselves.
- */
-extern const int null_map[];
-
-/**
- * User graphics mapping. Each entry is the corresponding Unicode codepoint
- * for the character N+32, where N is the index of the element in the array.
- * All characters less than 32 are universally mapped to themselves.
- */
-extern const int user_map[];
-
-#endif
-
diff --git a/src/protocols/ssh/client.c b/src/protocols/ssh/client.c
index 79f5561..f294363 100644
--- a/src/protocols/ssh/client.c
+++ b/src/protocols/ssh/client.c
@@ -1,61 +1,46 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Original Code is libguac-client-ssh.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Contributor(s):
- * James Muehlner <dagger10k at users.sourceforge.net>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <pthread.h>
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <guacamole/socket.h>
-#include <guacamole/protocol.h>
-#include <guacamole/client.h>
-#include <guacamole/error.h>
+#include "config.h"
 
 #include "client.h"
+#include "clipboard.h"
 #include "guac_handlers.h"
-#include "terminal.h"
-#include "blank.h"
-#include "ibar.h"
 #include "ssh_client.h"
+#include "terminal.h"
+
+#include <langinfo.h>
+#include <locale.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <guacamole/client.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
 
 #define GUAC_SSH_DEFAULT_FONT_NAME "monospace" 
 #define GUAC_SSH_DEFAULT_FONT_SIZE 12
-#define GUAC_SSH_DEFAULT_PORT      22
+#define GUAC_SSH_DEFAULT_PORT      "22"
 
 /* Client plugin arguments */
 const char* GUAC_CLIENT_ARGS[] = {
@@ -65,6 +50,14 @@ const char* GUAC_CLIENT_ARGS[] = {
     "password",
     "font-name",
     "font-size",
+    "enable-sftp",
+    "private-key",
+    "passphrase",
+#ifdef ENABLE_SSH_AGENT
+    "enable-agent",
+#endif
+    "color-scheme",
+    "command",
     NULL
 };
 
@@ -100,6 +93,42 @@ enum __SSH_ARGS_IDX {
      */
     IDX_FONT_SIZE,
 
+    /**
+     * Whether SFTP should be enabled.
+     */
+    IDX_ENABLE_SFTP,
+
+    /**
+     * The private key to use for authentication, if any.
+     */
+    IDX_PRIVATE_KEY,
+
+    /**
+     * The passphrase required to decrypt the private key, if any.
+     */
+    IDX_PASSPHRASE,
+
+#ifdef ENABLE_SSH_AGENT
+    /**
+     * Whether SSH agent forwarding support should be enabled.
+     */
+    IDX_ENABLE_AGENT,
+#endif
+
+    /**
+     * The name of the color scheme to use. Currently valid color schemes are:
+     * "black-white", "white-black", "gray-black", and "green-black", each
+     * following the "foreground-background" pattern. By default, this will be
+     * "gray-black".
+     */
+    IDX_COLOR_SCHEME,
+
+    /**
+     * The command to run instead if the default shell. If omitted, a normal
+     * shell session will be created.
+     */
+    IDX_COMMAND,
+
     SSH_ARGS_COUNT
 };
 
@@ -107,26 +136,30 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
 
     guac_socket* socket = client->socket;
 
-    ssh_guac_client_data* client_data = malloc(sizeof(ssh_guac_client_data));
+    ssh_guac_client_data* client_data = calloc(1, sizeof(ssh_guac_client_data));
 
     /* Init client data */
     client->data = client_data;
-    client_data->mod_alt   =
-    client_data->mod_ctrl  =
-    client_data->mod_shift = 0;
-    client_data->clipboard_data = NULL;
-    client_data->term_channel = NULL;
 
     if (argc != SSH_ARGS_COUNT) {
-        guac_client_log_error(client, "Wrong number of arguments");
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Wrong number of arguments");
         return -1;
     }
 
+    /* Set locale and warn if not UTF-8 */
+    setlocale(LC_CTYPE, "");
+    if (strcmp(nl_langinfo(CODESET), "UTF-8") != 0)
+        guac_client_log(client, GUAC_LOG_INFO, "Current locale does not use UTF-8. Some characters may not render correctly.");
+
     /* Read parameters */
     strcpy(client_data->hostname,  argv[IDX_HOSTNAME]);
     strcpy(client_data->username,  argv[IDX_USERNAME]);
     strcpy(client_data->password,  argv[IDX_PASSWORD]);
 
+    /* Init public key auth information */
+    strcpy(client_data->key_base64,     argv[IDX_PRIVATE_KEY]);
+    strcpy(client_data->key_passphrase, argv[IDX_PASSPHRASE]);
+
     /* Read font name */
     if (argv[IDX_FONT_NAME][0] != 0)
         strcpy(client_data->font_name, argv[IDX_FONT_NAME]);
@@ -139,50 +172,55 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
     else
         client_data->font_size = GUAC_SSH_DEFAULT_FONT_SIZE;
 
+    /* Parse SFTP enable */
+    client_data->enable_sftp = strcmp(argv[IDX_ENABLE_SFTP], "true") == 0;
+
+#ifdef ENABLE_SSH_AGENT
+    client_data->enable_agent = strcmp(argv[IDX_ENABLE_AGENT], "true") == 0;
+#endif
+
     /* Read port */
     if (argv[IDX_PORT][0] != 0)
-        client_data->port = atoi(argv[IDX_PORT]);
+        strcpy(client_data->port, argv[IDX_PORT]);
     else
-        client_data->port = GUAC_SSH_DEFAULT_PORT;
+        strcpy(client_data->port, GUAC_SSH_DEFAULT_PORT);
+
+    /* Read command, if any */
+    if (argv[IDX_COMMAND][0] != 0)
+        client_data->command = strdup(argv[IDX_COMMAND]);
 
     /* Create terminal */
     client_data->term = guac_terminal_create(client,
             client_data->font_name, client_data->font_size,
-            client->info.optimal_width, client->info.optimal_height);
+            client->info.optimal_resolution,
+            client->info.optimal_width, client->info.optimal_height,
+            argv[IDX_COLOR_SCHEME]);
 
     /* Fail if terminal init failed */
     if (client_data->term == NULL) {
-        guac_error = GUAC_STATUS_BAD_STATE;
-        guac_error_message = "Terminal initialization failed";
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Terminal initialization failed");
         return -1;
     }
 
-    /* Set up I-bar pointer */
-    client_data->ibar_cursor = guac_ssh_create_ibar(client);
-
-    /* Set up blank pointer */
-    client_data->blank_cursor = guac_ssh_create_blank(client);
+    /* Ensure main socket is threadsafe */
+    guac_socket_require_threadsafe(socket);
 
     /* Send initial name */
     guac_protocol_send_name(socket, client_data->hostname);
 
-    /* Initialize pointer */
-    client_data->current_cursor = client_data->blank_cursor;
-    guac_ssh_set_cursor(client, client_data->current_cursor);
-
     guac_socket_flush(socket);
 
     /* Set basic handlers */
     client->handle_messages   = ssh_guac_client_handle_messages;
-    client->clipboard_handler = ssh_guac_client_clipboard_handler;
     client->key_handler       = ssh_guac_client_key_handler;
     client->mouse_handler     = ssh_guac_client_mouse_handler;
     client->size_handler      = ssh_guac_client_size_handler;
     client->free_handler      = ssh_guac_client_free_handler;
+    client->clipboard_handler = guac_ssh_clipboard_handler;
 
     /* Start client thread */
     if (pthread_create(&(client_data->client_thread), NULL, ssh_client_thread, (void*) client)) {
-        guac_client_log_error(client, "Unable to SSH client thread");
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Unable to start SSH client thread");
         return -1;
     }
 
diff --git a/src/protocols/ssh/client.h b/src/protocols/ssh/client.h
index 08437d0..32e297c 100644
--- a/src/protocols/ssh/client.h
+++ b/src/protocols/ssh/client.h
@@ -1,49 +1,47 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Original Code is libguac-client-ssh.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Contributor(s):
- * James Muehlner <dagger10k at users.sourceforge.net>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef _SSH_GUAC_CLIENT_H
 #define _SSH_GUAC_CLIENT_H
 
-#include <pthread.h>
-#include <libssh/libssh.h>
+#include "config.h"
 
+#include "guac_ssh.h"
+#include "guac_ssh_user.h"
+#include "sftp.h"
 #include "terminal.h"
-#include "cursor.h"
+
+#include <libssh2.h>
+#include <libssh2_sftp.h>
+
+#include <guacamole/object.h>
+
+#ifdef ENABLE_SSH_AGENT
+#include "ssh_agent.h"
+#endif
+
+#include <pthread.h>
+#include <stdbool.h>
 
 /**
  * SSH-specific client data.
@@ -58,7 +56,7 @@ typedef struct ssh_guac_client_data {
     /**
      * The port of the SSH server to connect to.
      */
-    int port;
+    char port[64];
 
     /**
      * The name of the user to login as.
@@ -71,6 +69,22 @@ typedef struct ssh_guac_client_data {
     char password[1024];
 
     /**
+     * The private key, encoded as base64.
+     */
+    char key_base64[4096];
+
+    /**
+     * The password to use to decrypt the given private key.
+     */
+    char key_passphrase[1024];
+
+    /**
+     * The command to run instead of the default shell. If a normal shell
+     * session is desired, this will be NULL.
+     */
+    char* command;
+
+    /**
      * The name of the font to use for display rendering.
      */
     char font_name[1024];
@@ -81,65 +95,62 @@ typedef struct ssh_guac_client_data {
     int font_size;
 
     /**
-     * The SSH client thread.
+     * Whether SFTP is enabled.
      */
-    pthread_t client_thread;
+    bool enable_sftp;
 
+#ifdef ENABLE_SSH_AGENT
     /**
-     * SSH session, used by the SSH client thread.
+     * Whether the SSH agent is enabled.
      */
-    ssh_session session;
+    bool enable_agent;
 
     /**
-     * SSH terminal channel, used by the SSH client thread.
+     * The current agent, if any.
      */
-    ssh_channel term_channel;
+    ssh_auth_agent* auth_agent;
+#endif
 
     /**
-     * The terminal which will render all output from the SSH client.
-     */
-    guac_terminal* term;
-   
-    /**
-     * The current contents of the clipboard.
+     * The SSH client thread.
      */
-    char* clipboard_data;
+    pthread_t client_thread;
 
     /**
-     * Whether the alt key is currently being held down.
+     * The user and credentials to use for all SSH sessions.
      */
-    int mod_alt;
+    guac_common_ssh_user* user;
 
     /**
-     * Whether the control key is currently being held down.
+     * SSH session, used by the SSH client thread.
      */
-    int mod_ctrl;
+    guac_common_ssh_session* session;
 
     /**
-     * Whether the shift key is currently being held down.
+     * SFTP session, used by the SFTP client/filesystem.
      */
-    int mod_shift;
+    guac_common_ssh_session* sftp_session;
 
     /**
-     * The current mouse button state.
+     * The filesystem object exposed for the SFTP session.
      */
-    int mouse_mask;
+    guac_object* sftp_filesystem;
 
     /**
-     * The cached I-bar cursor.
+     * SSH terminal channel, used by the SSH client thread.
      */
-    guac_ssh_cursor* ibar_cursor;
+    LIBSSH2_CHANNEL* term_channel;
 
     /**
-     * The cached invisible (blank) cursor.
+     * Lock dictating access to the SSH terminal channel.
      */
-    guac_ssh_cursor* blank_cursor;
+    pthread_mutex_t term_channel_lock;
 
     /**
-     * The current cursor, used to avoid re-setting the cursor.
+     * The terminal which will render all output from the SSH client.
      */
-    guac_ssh_cursor* current_cursor;
-
+    guac_terminal* term;
+   
 } ssh_guac_client_data;
 
 #endif
diff --git a/src/protocols/ssh/clipboard.c b/src/protocols/ssh/clipboard.c
new file mode 100644
index 0000000..34a86fe
--- /dev/null
+++ b/src/protocols/ssh/clipboard.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+#include "client.h"
+#include "clipboard.h"
+#include "terminal.h"
+
+#include <guacamole/client.h>
+#include <guacamole/stream.h>
+
+int guac_ssh_clipboard_handler(guac_client* client, guac_stream* stream,
+        char* mimetype) {
+
+    /* Clear clipboard and prepare for new data */
+    ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
+    guac_terminal_clipboard_reset(client_data->term, mimetype);
+
+    /* Set handlers for clipboard stream */
+    stream->blob_handler = guac_ssh_clipboard_blob_handler;
+    stream->end_handler = guac_ssh_clipboard_end_handler;
+
+    return 0;
+}
+
+int guac_ssh_clipboard_blob_handler(guac_client* client, guac_stream* stream,
+        void* data, int length) {
+
+    /* Append new data */
+    ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
+    guac_terminal_clipboard_append(client_data->term, data, length);
+
+    return 0;
+}
+
+int guac_ssh_clipboard_end_handler(guac_client* client, guac_stream* stream) {
+
+    /* Nothing to do - clipboard is implemented within client */
+
+    return 0;
+}
+
diff --git a/src/protocols/ssh/clipboard.h b/src/protocols/ssh/clipboard.h
new file mode 100644
index 0000000..dfa2bd6
--- /dev/null
+++ b/src/protocols/ssh/clipboard.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _GUAC_SSH_CLIPBOARD_H
+#define _GUAC_SSH_CLIPBOARD_H
+
+#include "config.h"
+
+#include <guacamole/client.h>
+#include <guacamole/stream.h>
+
+/**
+ * Handler for inbound clipboard data.
+ */
+int guac_ssh_clipboard_handler(guac_client* client, guac_stream* stream,
+        char* mimetype);
+
+/**
+ * Handler for stream data related to clipboard.
+ */
+int guac_ssh_clipboard_blob_handler(guac_client* client, guac_stream* stream,
+        void* data, int length);
+
+/**
+ * Handler for end-of-stream related to clipboard.
+ */
+int guac_ssh_clipboard_end_handler(guac_client* client, guac_stream* stream);
+
+#endif
+
diff --git a/src/protocols/ssh/common.c b/src/protocols/ssh/common.c
deleted file mode 100644
index cdd069e..0000000
--- a/src/protocols/ssh/common.c
+++ /dev/null
@@ -1,121 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-ssh.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include <stdbool.h>
-#include <unistd.h>
-
-int guac_terminal_fit_to_range(int value, int min, int max) {
-
-    if (value < min) return min;
-    if (value > max) return max;
-
-    return value;
-
-}
-
-int guac_terminal_encode_utf8(int codepoint, char* utf8) {
-
-    int i;
-    int mask, bytes;
-
-    /* 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 {
-        *(utf8++) = '?';
-        return 1;
-    }
-
-    /* Offset buffer by size */
-    utf8 += bytes - 1;
-
-    /* Add trailing bytes, if any */
-    for (i=1; i<bytes; i++) {
-        *(utf8--) = 0x80 | (codepoint & 0x3F);
-        codepoint >>= 6;
-    }
-
-    /* Set initial byte */
-    *utf8 = mask | codepoint;
-
-    /* Done */
-    return bytes;
-
-}
-
-bool guac_terminal_has_glyph(int codepoint) {
-    return
-           codepoint != 0
-        && codepoint != ' ';
-}
-
-int guac_terminal_write_all(int fd, const char* buffer, int size) {
-
-    int remaining = size;
-    while (remaining > 0) {
-
-        /* Attempt to write data */
-        int ret_val = write(fd, buffer, remaining);
-        if (ret_val <= 0)
-            return -1;
-
-        /* If successful, contine with what data remains (if any) */
-        remaining -= ret_val;
-        buffer += ret_val;
-
-    }
-
-    return size;
-
-}
-
diff --git a/src/protocols/ssh/common.h b/src/protocols/ssh/common.h
deleted file mode 100644
index d6b224d..0000000
--- a/src/protocols/ssh/common.h
+++ /dev/null
@@ -1,68 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-ssh.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef _SSH_GUAC_COMMON_H
-#define _SSH_GUAC_COMMON_H
-
-#include <stdbool.h>
-
-/**
- * Returns the closest value to the value given that is also
- * within the given range.
- */
-int guac_terminal_fit_to_range(int value, int min, int max);
-
-/**
- * Encodes the given codepoint as UTF-8, storing the result within the
- * provided buffer, and returning the number of bytes stored.
- */
-int guac_terminal_encode_utf8(int codepoint, char* utf8);
-
-/**
- * Returns whether a codepoint has a corresponding glyph, or is rendered
- * as a blank space.
- */
-bool guac_terminal_has_glyph(int codepoint);
-
-/**
- * Similar to write, but automatically retries the write operation until
- * an error occurs.
- */
-int guac_terminal_write_all(int fd, const char* buffer, int size);
-
-#endif
-
diff --git a/src/protocols/ssh/cursor.c b/src/protocols/ssh/cursor.c
deleted file mode 100644
index ea6ad42..0000000
--- a/src/protocols/ssh/cursor.c
+++ /dev/null
@@ -1,74 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-ssh.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include <stdlib.h>
-
-#include <guacamole/client.h>
-#include <guacamole/protocol.h>
-
-#include "cursor.h"
-
-guac_ssh_cursor* guac_ssh_cursor_alloc(guac_client* client) {
-
-    /* Alloc new cursor, initialize buffer */
-    guac_ssh_cursor* cursor = malloc(sizeof(guac_ssh_cursor));
-    cursor->buffer = guac_client_alloc_buffer(client);
-
-    return cursor;
-
-}
-
-void guac_ssh_cursor_free(guac_client* client, guac_ssh_cursor* cursor) {
-
-    /* Free buffer */
-    guac_client_free_buffer(client, cursor->buffer);
-
-    /* Free cursor */
-    free(cursor);
-
-}
-
-void guac_ssh_set_cursor(guac_client* client, guac_ssh_cursor* cursor) {
-
-    /* Set cursor */
-    guac_protocol_send_cursor(client->socket,
-            cursor->hotspot_x, cursor->hotspot_y,
-            cursor->buffer,
-            0, 0, cursor->width, cursor->height);
-
-}
-
diff --git a/src/protocols/ssh/cursor.h b/src/protocols/ssh/cursor.h
deleted file mode 100644
index 37d09be..0000000
--- a/src/protocols/ssh/cursor.h
+++ /dev/null
@@ -1,89 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-ssh.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef _GUAC_SSH_CURSOR_H
-#define _GUAC_SSH_CURSOR_H
-
-#include <guacamole/client.h>
-
-typedef struct guac_ssh_cursor {
-
-    /**
-     * A buffer allocated with guac_client_alloc_buffer() that contains the
-     * cursor image.
-     */
-    guac_layer* buffer;
-
-    /**
-     * The width of the cursor in pixels.
-     */
-    int width;
-
-    /**
-     * The height of the cursor in pixels.
-     */
-    int height;
-
-    /**
-     * The X coordinate of the cursor hotspot.
-     */
-    int hotspot_x;
-
-    /**
-     * The Y coordinate of the cursor hotspot.
-     */
-    int hotspot_y;
-
-} guac_ssh_cursor;
-
-/**
- * Allocates a new cursor, pre-populating the cursor with a newly-allocated
- * buffer.
- */
-guac_ssh_cursor* guac_ssh_cursor_alloc(guac_client* client);
-
-/**
- * Frees the buffer associated with this cursor as well as the cursor itself.
- */
-void guac_ssh_cursor_free(guac_client* client, guac_ssh_cursor* cursor);
-
-/**
- * Set the remote cursor.
- */
-void guac_ssh_set_cursor(guac_client* client, guac_ssh_cursor* cursor);
-
-#endif
diff --git a/src/protocols/ssh/guac_handlers.c b/src/protocols/ssh/guac_handlers.c
index e3a8762..3b1ce2c 100644
--- a/src/protocols/ssh/guac_handlers.c
+++ b/src/protocols/ssh/guac_handlers.c
@@ -1,224 +1,55 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Original Code is libguac-client-ssh.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Contributor(s):
- * James Muehlner <dagger10k at users.sourceforge.net>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <sys/select.h>
+#include "config.h"
 
-#include <stdlib.h>
-#include <string.h>
+#include "client.h"
+#include "guac_handlers.h"
+#include "guac_sftp.h"
+#include "guac_ssh.h"
+#include "terminal.h"
 
 #include <cairo/cairo.h>
-#include <pango/pangocairo.h>
-
-#include <guacamole/socket.h>
-#include <guacamole/protocol.h>
 #include <guacamole/client.h>
-#include <guacamole/error.h>
+#include <libssh2.h>
+#include <libssh2_sftp.h>
 
-#include <libssh/libssh.h>
-#include "libssh_compat.h"
-
-#include "guac_handlers.h"
-#include "client.h"
-#include "common.h"
-#include "cursor.h"
+#include <pthread.h>
+#include <stdlib.h>
 
 int ssh_guac_client_handle_messages(guac_client* client) {
 
-    guac_socket* socket = client->socket;
     ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
-    char buffer[8192];
-
-    int ret_val;
-    int fd = client_data->term->stdout_pipe_fd[0];
-    struct timeval timeout;
-    fd_set fds;
-
-    /* Build fd_set */
-    FD_ZERO(&fds);
-    FD_SET(fd, &fds);
-
-    /* Time to wait */
-    timeout.tv_sec =  1;
-    timeout.tv_usec = 0;
-
-    /* Wait for data to be available */
-    ret_val = select(fd+1, &fds, NULL, NULL, &timeout);
-    if (ret_val > 0) {
-
-        int bytes_read = 0;
-
-        /* Lock terminal access */
-        pthread_mutex_lock(&(client_data->term->lock));
-
-        /* Read data, write to terminal */
-        if ((bytes_read = read(fd, buffer, sizeof(buffer))) > 0) {
-
-            if (guac_terminal_write(client_data->term, buffer, bytes_read))
-                return 1;
-
-        }
-
-        /* Notify on error */
-        if (bytes_read < 0) {
-            guac_protocol_send_error(socket, "Error reading data.");
-            guac_socket_flush(socket);
-            return 1;
-        }
-
-        /* Update cursor */
-        guac_terminal_commit_cursor(client_data->term);
-
-        /* Flush terminal display */
-        guac_terminal_display_flush(client_data->term->display);
-
-        /* Unlock terminal access */
-        pthread_mutex_unlock(&(client_data->term->lock));
-
-    }
-    else if (ret_val < 0) {
-        guac_error_message = "Error waiting for pipe";
-        guac_error = GUAC_STATUS_SEE_ERRNO;
-        return 1;
-    }
-
-    return 0;
+    return guac_terminal_render_frame(client_data->term);
 
 }
 
-int ssh_guac_client_clipboard_handler(guac_client* client, char* data) {
-
-    ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
-
-    free(client_data->clipboard_data);
-
-    client_data->clipboard_data = strdup(data);
-
-    return 0;
-}
-
-
-
 int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) {
 
     ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
     guac_terminal* term = client_data->term;
 
-    /* Determine which buttons were just released and pressed */
-    int released_mask =  client_data->mouse_mask & ~mask;
-    int pressed_mask  = ~client_data->mouse_mask &  mask;
-
-    client_data->mouse_mask = mask;
-
-    /* Show mouse cursor if not already shown */
-    if (client_data->current_cursor != client_data->ibar_cursor) {
-        pthread_mutex_lock(&(term->lock));
-
-        client_data->current_cursor = client_data->ibar_cursor;
-        guac_ssh_set_cursor(client, client_data->ibar_cursor);
-        guac_socket_flush(client->socket);
-
-        pthread_mutex_unlock(&(term->lock));
-    }
-
-    /* Paste contents of clipboard on right or middle mouse button up */
-    if ((released_mask & GUAC_CLIENT_MOUSE_RIGHT) || (released_mask & GUAC_CLIENT_MOUSE_MIDDLE)) {
-        if (client_data->clipboard_data != NULL)
-            return guac_terminal_send_string(term, client_data->clipboard_data);
-        else
-            return 0;
-    }
-
-    /* If text selected, change state based on left mouse mouse button */
-    if (term->text_selected) {
-        pthread_mutex_lock(&(term->lock));
-
-        /* If mouse button released, stop selection */
-        if (released_mask & GUAC_CLIENT_MOUSE_LEFT) {
-
-            /* End selection and get selected text */
-            char* string = malloc(term->term_width * term->term_height * sizeof(char));
-            guac_terminal_select_end(term, string);
-
-            /* Store new data */
-            free(client_data->clipboard_data);
-            client_data->clipboard_data = string;
-
-            /* Send data */
-            guac_protocol_send_clipboard(client->socket, string);
-            guac_socket_flush(client->socket);
-
-        }
-
-        /* Otherwise, just update */
-        else
-            guac_terminal_select_update(term,
-                    y / term->display->char_height - term->scroll_offset,
-                    x / term->display->char_width);
-
-        pthread_mutex_unlock(&(term->lock));
-    }
-
-    /* Otherwise, if mouse button pressed AND moved, start selection */
-    else if (!(pressed_mask & GUAC_CLIENT_MOUSE_LEFT) &&
-               mask         & GUAC_CLIENT_MOUSE_LEFT) {
-        pthread_mutex_lock(&(term->lock));
-
-        guac_terminal_select_start(term,
-                y / term->display->char_height - term->scroll_offset,
-                x / term->display->char_width);
-
-        pthread_mutex_unlock(&(term->lock));
-    }
-
-    /* Scroll up if wheel moved up */
-    if (released_mask & GUAC_CLIENT_MOUSE_SCROLL_UP) {
-        pthread_mutex_lock(&(term->lock));
-        guac_terminal_scroll_display_up(term, GUAC_SSH_WHEEL_SCROLL_AMOUNT);
-        pthread_mutex_unlock(&(term->lock));
-    }
-
-    /* Scroll down if wheel moved down */
-    if (released_mask & GUAC_CLIENT_MOUSE_SCROLL_DOWN) {
-        pthread_mutex_lock(&(term->lock));
-        guac_terminal_scroll_display_down(term, GUAC_SSH_WHEEL_SCROLL_AMOUNT);
-        pthread_mutex_unlock(&(term->lock));
-    }
-
+    /* Send mouse event */
+    guac_terminal_send_mouse(term, x, y, mask);
     return 0;
 
 }
@@ -228,149 +59,8 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) {
     ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
     guac_terminal* term = client_data->term;
 
-    /* Hide mouse cursor if not already hidden */
-    if (client_data->current_cursor != client_data->blank_cursor) {
-        pthread_mutex_lock(&(term->lock));
-
-        client_data->current_cursor = client_data->blank_cursor;
-        guac_ssh_set_cursor(client, client_data->blank_cursor);
-        guac_socket_flush(client->socket);
-
-        pthread_mutex_unlock(&(term->lock));
-    }
-
-    /* Track modifiers */
-    if (keysym == 0xFFE3)
-        client_data->mod_ctrl = pressed;
-    else if (keysym == 0xFFE9)
-        client_data->mod_alt = pressed;
-    else if (keysym == 0xFFE1)
-        client_data->mod_shift = pressed;
-        
-    /* If key pressed */
-    else if (pressed) {
-
-        /* Ctrl+Shift+V shortcut for paste */
-        if (keysym == 'V' && client_data->mod_ctrl) {
-            if (client_data->clipboard_data != NULL)
-                return guac_terminal_send_string(term, client_data->clipboard_data);
-            else
-                return 0;
-        }
-
-        /* Shift+PgUp / Shift+PgDown shortcuts for scrolling */
-        if (client_data->mod_shift) {
-
-            /* Page up */
-            if (keysym == 0xFF55) {
-                pthread_mutex_lock(&(term->lock));
-                guac_terminal_scroll_display_up(term, term->term_height);
-                pthread_mutex_unlock(&(term->lock));
-                return 0;
-            }
-
-            /* Page down */
-            if (keysym == 0xFF56) {
-                pthread_mutex_lock(&(term->lock));
-                guac_terminal_scroll_display_down(term, term->term_height);
-                pthread_mutex_unlock(&(term->lock));
-                return 0;
-            }
-
-        }
-
-        /* Reset scroll */
-        if (term->scroll_offset != 0) {
-            pthread_mutex_lock(&(term->lock));
-            guac_terminal_scroll_display_down(term, term->scroll_offset);
-            pthread_mutex_unlock(&(term->lock));
-        }
-
-        /* If alt being held, also send escape character */
-        if (client_data->mod_alt)
-            return guac_terminal_send_string(term, "\x1B");
-
-        /* Translate Ctrl+letter to control code */ 
-        if (client_data->mod_ctrl) {
-
-            char data;
-
-            /* If valid control code, send it */
-            if (keysym >= 'A' && keysym <= 'Z')
-                data = (char) (keysym - 'A' + 1);
-            else if (keysym >= 'a' && keysym <= 'z')
-                data = (char) (keysym - 'a' + 1);
-
-            /* Otherwise ignore */
-            else
-                return 0;
-
-            return guac_terminal_send_data(term, &data, 1);
-
-        }
-
-        /* Translate Unicode to UTF-8 */
-        else if ((keysym >= 0x00 && keysym <= 0xFF) || ((keysym & 0xFFFF0000) == 0x01000000)) {
-
-            int length;
-            char data[5];
-
-            length = guac_terminal_encode_utf8(keysym & 0xFFFF, data);
-            return guac_terminal_send_data(term, data, length);
-
-        }
-
-        /* Non-printable keys */
-        else {
-
-            if (keysym == 0xFF08) return guac_terminal_send_string(term, "\x7F"); /* Backspace */
-            if (keysym == 0xFF09) return guac_terminal_send_string(term, "\x09"); /* Tab */
-            if (keysym == 0xFF0D) return guac_terminal_send_string(term, "\x0D"); /* Enter */
-            if (keysym == 0xFF1B) return guac_terminal_send_string(term, "\x1B"); /* Esc */
-
-            if (keysym == 0xFF50) return guac_terminal_send_string(term, "\x1B[1~"); /* Home */
-
-            /* Arrow keys w/ application cursor */
-            if (term->application_cursor_keys) {
-                if (keysym == 0xFF51) return guac_terminal_send_string(term, "\x1BOD"); /* Left */
-                if (keysym == 0xFF52) return guac_terminal_send_string(term, "\x1BOA"); /* Up */
-                if (keysym == 0xFF53) return guac_terminal_send_string(term, "\x1BOC"); /* Right */
-                if (keysym == 0xFF54) return guac_terminal_send_string(term, "\x1BOB"); /* Down */
-            }
-            else {
-                if (keysym == 0xFF51) return guac_terminal_send_string(term, "\x1B[D"); /* Left */
-                if (keysym == 0xFF52) return guac_terminal_send_string(term, "\x1B[A"); /* Up */
-                if (keysym == 0xFF53) return guac_terminal_send_string(term, "\x1B[C"); /* Right */
-                if (keysym == 0xFF54) return guac_terminal_send_string(term, "\x1B[B"); /* Down */
-            }
-
-            if (keysym == 0xFF55) return guac_terminal_send_string(term, "\x1B[5~"); /* Page up */
-            if (keysym == 0xFF56) return guac_terminal_send_string(term, "\x1B[6~"); /* Page down */
-            if (keysym == 0xFF57) return guac_terminal_send_string(term, "\x1B[4~"); /* End */
-
-            if (keysym == 0xFF63) return guac_terminal_send_string(term, "\x1B[2~"); /* Insert */
-
-            if (keysym == 0xFFBE) return guac_terminal_send_string(term, "\x1B[[A"); /* F1  */
-            if (keysym == 0xFFBF) return guac_terminal_send_string(term, "\x1B[[B"); /* F2  */
-            if (keysym == 0xFFC0) return guac_terminal_send_string(term, "\x1B[[C"); /* F3  */
-            if (keysym == 0xFFC1) return guac_terminal_send_string(term, "\x1B[[D"); /* F4  */
-            if (keysym == 0xFFC2) return guac_terminal_send_string(term, "\x1B[[E"); /* F5  */
-
-            if (keysym == 0xFFC3) return guac_terminal_send_string(term, "\x1B[17~"); /* F6  */
-            if (keysym == 0xFFC4) return guac_terminal_send_string(term, "\x1B[18~"); /* F7  */
-            if (keysym == 0xFFC5) return guac_terminal_send_string(term, "\x1B[19~"); /* F8  */
-            if (keysym == 0xFFC6) return guac_terminal_send_string(term, "\x1B[20~"); /* F9  */
-            if (keysym == 0xFFC7) return guac_terminal_send_string(term, "\x1B[21~"); /* F10 */
-            if (keysym == 0xFFC8) return guac_terminal_send_string(term, "\x1B[22~"); /* F11 */
-            if (keysym == 0xFFC9) return guac_terminal_send_string(term, "\x1B[23~"); /* F12 */
-
-            if (keysym == 0xFFFF) return guac_terminal_send_string(term, "\x1B[3~"); /* Delete */
-
-            /* Ignore unknown keys */
-        }
-
-    }
-
+    /* Send key */
+    guac_terminal_send_key(term, keysym, pressed);
     return 0;
 
 }
@@ -381,34 +71,17 @@ int ssh_guac_client_size_handler(guac_client* client, int width, int height) {
     ssh_guac_client_data* guac_client_data = (ssh_guac_client_data*) client->data;
     guac_terminal* terminal = guac_client_data->term;
 
-    /* Calculate dimensions */
-    int rows    = height / terminal->display->char_height;
-    int columns = width  / terminal->display->char_width;
-
-    pthread_mutex_lock(&(terminal->lock));
-
-    /* If size has changed */
-    if (columns != terminal->term_width || rows != terminal->term_height) {
-
-        /* Resize terminal */
-        guac_terminal_resize(terminal, columns, rows);
-
-        /* Update SSH pty size if connected */
-        if (guac_client_data->term_channel != NULL)
-            channel_change_pty_size(guac_client_data->term_channel,
-                    terminal->term_width, terminal->term_height);
+    /* Resize terminal */
+    guac_terminal_resize(terminal, width, height);
 
-        /* Reset scroll region */
-        terminal->scroll_end = rows - 1;
-
-        guac_terminal_display_flush(terminal->display);
-        guac_protocol_send_sync(terminal->client->socket,
-                client->last_sent_timestamp);
-        guac_socket_flush(terminal->client->socket);
+    /* Update SSH pty size if connected */
+    if (guac_client_data->term_channel != NULL) {
+        pthread_mutex_lock(&(guac_client_data->term_channel_lock));
+        libssh2_channel_request_pty_size(guac_client_data->term_channel,
+                terminal->term_width, terminal->term_height);
+        pthread_mutex_unlock(&(guac_client_data->term_channel_lock));
     }
 
-    pthread_mutex_unlock(&(terminal->lock));
-
     return 0;
 }
 
@@ -418,8 +91,8 @@ int ssh_guac_client_free_handler(guac_client* client) {
 
     /* Close SSH channel */
     if (guac_client_data->term_channel != NULL) {
-        ssh_channel_close(guac_client_data->term_channel);
-        ssh_channel_send_eof(guac_client_data->term_channel);
+        libssh2_channel_send_eof(guac_client_data->term_channel);
+        libssh2_channel_close(guac_client_data->term_channel);
     }
 
     /* Free terminal */
@@ -427,21 +100,29 @@ int ssh_guac_client_free_handler(guac_client* client) {
     pthread_join(guac_client_data->client_thread, NULL);
 
     /* Free channels */
-    ssh_channel_free(guac_client_data->term_channel);
+    libssh2_channel_free(guac_client_data->term_channel);
+
+    /* Clean up the SFTP filesystem object and session */
+    if (guac_client_data->sftp_filesystem) {
+        guac_common_ssh_destroy_sftp_filesystem(guac_client_data->sftp_filesystem);
+        guac_common_ssh_destroy_session(guac_client_data->sftp_session);
+    }
 
     /* Free session */
-    ssh_free(guac_client_data->session);
+    if (guac_client_data->session != NULL)
+        guac_common_ssh_destroy_session(guac_client_data->session);
 
-    /* Free clipboard data */
-    free(guac_client_data->clipboard_data);
+    /* Free user */
+    if (guac_client_data->user != NULL)
+        guac_common_ssh_destroy_user(guac_client_data->user);
 
-    /* Free cursors */
-    guac_ssh_cursor_free(client, guac_client_data->ibar_cursor);
-    guac_ssh_cursor_free(client, guac_client_data->blank_cursor);
+    /* Free copied settings */
+    free(guac_client_data->command);
 
     /* Free generic data struct */
     free(client->data);
 
+    guac_common_ssh_uninit();
     return 0;
 }
 
diff --git a/src/protocols/ssh/guac_handlers.h b/src/protocols/ssh/guac_handlers.h
index d9f7ef1..917b59a 100644
--- a/src/protocols/ssh/guac_handlers.h
+++ b/src/protocols/ssh/guac_handlers.h
@@ -1,50 +1,36 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-ssh.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
- * James Muehlner <dagger10k at users.sourceforge.net>
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef _SSH_GUAC_HANDLERS_H
 #define _SSH_GUAC_HANDLERS_H
 
+#include "config.h"
+
 #include <guacamole/client.h>
 
 int ssh_guac_client_handle_messages(guac_client* client);
 int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed);
 int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask);
-int ssh_guac_client_clipboard_handler(guac_client* client, char* data);
 int ssh_guac_client_size_handler(guac_client* client, int width, int height);
 int ssh_guac_client_free_handler(guac_client* client);
 
diff --git a/src/protocols/ssh/ibar.c b/src/protocols/ssh/ibar.c
deleted file mode 100644
index 70b969c..0000000
--- a/src/protocols/ssh/ibar.c
+++ /dev/null
@@ -1,108 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-ssh.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include <cairo/cairo.h>
-#include <guacamole/client.h>
-#include <guacamole/protocol.h>
-#include <guacamole/socket.h>
-
-#include "cursor.h"
-
-/* Macros for prettying up the embedded image. */
-#define X 0x00,0x00,0x00,0xFF
-#define U 0x80,0x80,0x80,0xFF
-#define O 0xFF,0xFF,0xFF,0xFF
-#define _ 0x00,0x00,0x00,0x00
-
-/* Dimensions */
-const int guac_ssh_ibar_width  = 7;
-const int guac_ssh_ibar_height = 16;
-
-/* Format */
-const cairo_format_t guac_ssh_ibar_format = CAIRO_FORMAT_ARGB32;
-const int guac_ssh_ibar_stride = 28;
-
-/* Embedded pointer graphic */
-unsigned char guac_ssh_ibar[] = {
-
-        X,X,X,X,X,X,X,
-        X,O,O,U,O,O,X,
-        X,X,X,O,X,X,X,
-        _,_,X,O,X,_,_,
-        _,_,X,O,X,_,_,
-        _,_,X,O,X,_,_,
-        _,_,X,O,X,_,_,
-        _,_,X,O,X,_,_,
-        _,_,X,O,X,_,_,
-        _,_,X,O,X,_,_,
-        _,_,X,O,X,_,_,
-        _,_,X,O,X,_,_,
-        _,_,X,O,X,_,_,
-        X,X,X,O,X,X,X,
-        X,O,O,U,O,O,X,
-        X,X,X,X,X,X,X
-
-};
-
-
-guac_ssh_cursor* guac_ssh_create_ibar(guac_client* client) {
-
-    guac_socket* socket = client->socket;
-    guac_ssh_cursor* cursor = guac_ssh_cursor_alloc(client);
-
-    /* Draw to buffer */
-    cairo_surface_t* graphic = cairo_image_surface_create_for_data(
-            guac_ssh_ibar,
-            guac_ssh_ibar_format,
-            guac_ssh_ibar_width,
-            guac_ssh_ibar_height,
-            guac_ssh_ibar_stride);
-
-    guac_protocol_send_png(socket, GUAC_COMP_SRC, cursor->buffer,
-            0, 0, graphic);
-    cairo_surface_destroy(graphic);
-
-    /* Initialize cursor properties */
-    cursor->width = guac_ssh_ibar_width;
-    cursor->height = guac_ssh_ibar_height;
-    cursor->hotspot_x = guac_ssh_ibar_width / 2;
-    cursor->hotspot_y = guac_ssh_ibar_height / 2;
-
-    return cursor;
-
-}
-
diff --git a/src/protocols/ssh/ibar.h b/src/protocols/ssh/ibar.h
deleted file mode 100644
index 66e4ff4..0000000
--- a/src/protocols/ssh/ibar.h
+++ /dev/null
@@ -1,77 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-ssh.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef _GUAC_SSH_IBAR_H
-#define _GUAC_SSH_IBAR_H
-
-#include <cairo/cairo.h>
-#include <guacamole/client.h>
-
-/**
- * Width of the embedded mouse cursor graphic.
- */
-extern const int guac_ssh_ibar_width;
-
-/**
- * Height of the embedded mouse cursor graphic.
- */
-extern const int guac_ssh_ibar_height;
-
-/**
- * Number of bytes in each row of the embedded mouse cursor graphic.
- */
-extern const int guac_ssh_ibar_stride;
-
-/**
- * The Cairo grapic format of the mouse cursor graphic.
- */
-extern const cairo_format_t guac_ssh_ibar_format;
-
-/**
- * Embedded mouse cursor graphic.
- */
-extern unsigned char guac_ssh_ibar[];
-
-/**
- * Creates a new I-bar cursor, returning the corresponding cursor object.
- *
- * @param client The guac_client to send the cursor to.
- * @return A new cursor which must be free'd via guac_ssh_cursor_free()/
- */
-guac_ssh_cursor* guac_ssh_create_ibar(guac_client* client);
-
-#endif
diff --git a/src/protocols/ssh/libssh_compat.h b/src/protocols/ssh/libssh_compat.h
deleted file mode 100644
index 82205d7..0000000
--- a/src/protocols/ssh/libssh_compat.h
+++ /dev/null
@@ -1,57 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-ssh.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef _SSH_GUAC_LIBSSH_COMPAT_H
-#define _SSH_GUAC_LIBSSH_COMPAT_H
-
-/* Define ssh_channel_close() if undefined */
-#ifndef HAVE_SSH_CHANNEL_CLOSE
-#define ssh_channel_close channel_close
-#endif
-
-/* Define ssh_channel_send_eof() if undefined */
-#ifndef HAVE_SSH_CHANNEL_SEND_EOF
-#define ssh_channel_send_eof channel_send_eof
-#endif
-
-/* Define ssh_channel_free() if undefined */
-#ifndef HAVE_SSH_CHANNEL_FREE
-#define ssh_channel_free channel_free
-#endif
-
-#endif
-
diff --git a/src/protocols/ssh/sftp.c b/src/protocols/ssh/sftp.c
new file mode 100644
index 0000000..af6be7b
--- /dev/null
+++ b/src/protocols/ssh/sftp.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "client.h"
+#include "guac_sftp.h"
+#include "sftp.h"
+
+#include <guacamole/client.h>
+#include <guacamole/stream.h>
+
+int guac_sftp_file_handler(guac_client* client, guac_stream* stream,
+        char* mimetype, char* filename) {
+
+    ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
+    guac_object* filesystem = client_data->sftp_filesystem;
+
+    /* Handle file upload */
+    return guac_common_ssh_sftp_handle_file_stream(filesystem, stream,
+            mimetype, filename);
+
+}
+
+guac_stream* guac_sftp_download_file(guac_client* client,
+        char* filename) {
+
+    ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
+    guac_object* filesystem = client_data->sftp_filesystem;
+
+    /* Initiate download of requested file */
+    return guac_common_ssh_sftp_download_file(filesystem, filename);
+
+}
+
+void guac_sftp_set_upload_path(guac_client* client, char* path) {
+
+    ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
+    guac_object* filesystem = client_data->sftp_filesystem;
+
+    /* Set upload path as specified */
+    guac_common_ssh_sftp_set_upload_path(filesystem, path);
+
+}
+
diff --git a/src/protocols/ssh/sftp.h b/src/protocols/ssh/sftp.h
new file mode 100644
index 0000000..af97c98
--- /dev/null
+++ b/src/protocols/ssh/sftp.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef _SSH_GUAC_SFTP_H
+#define _SSH_GUAC_SFTP_H
+
+#include "config.h"
+
+#include <guacamole/client.h>
+#include <guacamole/stream.h>
+
+/**
+ * Handles an incoming stream from a Guacamole "file" instruction, saving the
+ * contents of that stream to the file having the given name within the
+ * upload directory set by guac_sftp_set_upload_path().
+ *
+ * @param client
+ *     The client receiving the uploaded file.
+ *
+ * @param stream
+ *     The stream through which the uploaded file data will be received.
+ *
+ * @param mimetype
+ *     The mimetype of the data being received.
+ *
+ * @param filename
+ *     The filename of the file to write to. This filename will always be taken
+ *     relative to the upload path set by
+ *     guac_common_ssh_sftp_set_upload_path().
+ *
+ * @return
+ *     Zero if the incoming stream has been handled successfully, non-zero on
+ *     failure.
+ */
+int guac_sftp_file_handler(guac_client* client, guac_stream* stream,
+        char* mimetype, char* filename);
+
+/**
+ * Initiates an SFTP file download to the user via the Guacamole "file"
+ * instruction. The download will be automatically monitored and continued
+ * after this function terminates in response to "ack" instructions received by
+ * the client.
+ *
+ * @param client
+ *     The client receiving the file.
+ *
+ * @param filename
+ *     The filename of the file to download, relative to the given filesystem.
+ *
+ * @return
+ *     The file stream created for the file download, already configured to
+ *     properly handle "ack" responses, etc. from the client.
+ */
+guac_stream* guac_sftp_download_file(guac_client* client, char* filename);
+
+/**
+ * Sets the destination directory for future uploads submitted via Guacamole
+ * "file" instruction. This function has no bearing on the destination
+ * directories of files uploaded with "put" instructions.
+ *
+ * @param client
+ *     The client setting the upload path.
+ *
+ * @param path
+ *     The path to use for future uploads submitted via "file" instruction.
+ */
+void guac_sftp_set_upload_path(guac_client* client, char* path);
+
+#endif
+
diff --git a/src/protocols/ssh/ssh_agent.c b/src/protocols/ssh/ssh_agent.c
new file mode 100644
index 0000000..8c54f57
--- /dev/null
+++ b/src/protocols/ssh/ssh_agent.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "client.h"
+#include "ssh_agent.h"
+#include "ssh_buffer.h"
+
+#include <guacamole/client.h>
+#include <libssh2.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+void ssh_auth_agent_sign(ssh_auth_agent* agent, char* data, int data_length) {
+
+    LIBSSH2_CHANNEL* channel = agent->channel;
+    ssh_key* key = agent->identity;
+
+    char sig[4096];
+    int sig_len;
+
+    char buffer[4096];
+    char* pos = buffer;
+
+    /* Sign with key */
+    sig_len = ssh_key_sign(key, data, data_length, (u_char*) sig);
+    if (sig_len < 0)
+        return;
+
+    buffer_write_uint32(&pos, 1+4 + 4+7 + 4+sig_len);
+
+    buffer_write_byte(&pos, SSH2_AGENT_SIGN_RESPONSE);
+    buffer_write_uint32(&pos, 4+7 + 4+sig_len);
+
+    /* Write key type */
+    if (key->type == SSH_KEY_RSA)
+        buffer_write_string(&pos, "ssh-rsa", 7);
+    else if (key->type == SSH_KEY_DSA)
+        buffer_write_string(&pos, "ssh-dss", 7);
+    else
+        return;
+
+    /* Write signature */
+    buffer_write_string(&pos, sig, sig_len);
+
+    libssh2_channel_write(channel, buffer, pos-buffer);
+    libssh2_channel_flush(channel);
+
+}
+
+void ssh_auth_agent_list_identities(ssh_auth_agent* auth_agent) {
+
+    LIBSSH2_CHANNEL* channel = auth_agent->channel;
+    ssh_key* key = auth_agent->identity;
+
+    char buffer[4096];
+    char* pos = buffer;
+
+    buffer_write_uint32(&pos, 1+4
+                              + key->public_key_length+4
+                              + sizeof(SSH_AGENT_COMMENT)+3);
+
+    buffer_write_byte(&pos, SSH2_AGENT_IDENTITIES_ANSWER);
+    buffer_write_uint32(&pos, 1);
+
+    buffer_write_string(&pos, key->public_key, key->public_key_length);
+    buffer_write_string(&pos, SSH_AGENT_COMMENT, sizeof(SSH_AGENT_COMMENT)-1);
+
+    libssh2_channel_write(channel, buffer, pos-buffer);
+    libssh2_channel_flush(channel);
+
+}
+
+void ssh_auth_agent_handle_packet(ssh_auth_agent* auth_agent, uint8_t type,
+        char* data, int data_length) {
+
+    switch (type) {
+
+        /* List identities */
+        case SSH2_AGENT_REQUEST_IDENTITIES:
+            ssh_auth_agent_list_identities(auth_agent);
+            break;
+
+        /* Sign request */
+        case SSH2_AGENT_SIGN_REQUEST: {
+
+            char* pos = data;
+
+            int key_blob_length;
+            int sign_data_length;
+            char* sign_data;
+
+            /* Skip past key, read data, ignore flags */
+            buffer_read_string(&pos, &key_blob_length);
+            sign_data = buffer_read_string(&pos, &sign_data_length);
+
+            /* Sign given data */
+            ssh_auth_agent_sign(auth_agent, sign_data, sign_data_length);
+            break;
+        }
+
+        /* Otherwise, return failure */
+        default:
+            libssh2_channel_write(auth_agent->channel,
+                    UNSUPPORTED, sizeof(UNSUPPORTED)-1);
+
+    }
+
+}
+
+int ssh_auth_agent_read(ssh_auth_agent* auth_agent) {
+
+    LIBSSH2_CHANNEL* channel = auth_agent->channel;
+
+    int bytes_read;
+
+    if (libssh2_channel_eof(channel))
+        return -1;
+
+    /* Read header if available */
+    if (auth_agent->buffer_length >= 5) {
+
+        uint32_t length =
+              (((unsigned char*) auth_agent->buffer)[0] << 24)
+            | (((unsigned char*) auth_agent->buffer)[1] << 16)
+            | (((unsigned char*) auth_agent->buffer)[2] <<  8)
+            |  ((unsigned char*) auth_agent->buffer)[3];
+
+        uint8_t type = ((unsigned char*) auth_agent->buffer)[4];
+
+        /* If enough data read, call handler, shift data */
+        if (auth_agent->buffer_length >= length+4) {
+
+            ssh_auth_agent_handle_packet(auth_agent, type,
+                    auth_agent->buffer+5, length-1);
+
+            auth_agent->buffer_length -= length+4;
+            memmove(auth_agent->buffer,
+                    auth_agent->buffer+length+4, auth_agent->buffer_length);
+            return length+4;
+        }
+
+    }
+
+    /* Read data into buffer */
+    bytes_read = libssh2_channel_read(channel,
+            auth_agent->buffer+auth_agent->buffer_length,
+            sizeof(auth_agent->buffer)-auth_agent->buffer_length);
+
+    /* If unsuccessful, return error */
+    if (bytes_read < 0)
+        return bytes_read;
+
+    /* Update buffer length */
+    auth_agent->buffer_length += bytes_read;
+    return bytes_read;
+
+}
+
+void ssh_auth_agent_callback(LIBSSH2_SESSION *session,
+        LIBSSH2_CHANNEL *channel, void **abstract) {
+
+    /* Get client data */
+    guac_client* client = (guac_client*) *abstract;
+    ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
+
+    /* Init auth agent */
+    ssh_auth_agent* auth_agent = malloc(sizeof(ssh_auth_agent));
+    auth_agent->channel = channel;
+    auth_agent->identity = client_data->key;
+    auth_agent->buffer_length = 0;
+
+    /* Store auth agent */
+    client_data->auth_agent = auth_agent;
+
+}
+
diff --git a/src/protocols/ssh/ssh_agent.h b/src/protocols/ssh/ssh_agent.h
new file mode 100644
index 0000000..7fc89dd
--- /dev/null
+++ b/src/protocols/ssh/ssh_agent.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef _GUAC_SSH_AGENT_H
+#define _GUAC_SSH_AGENT_H
+
+#include "config.h"
+
+#include "ssh_key.h"
+
+/**
+ * Packet type of an agent identity request.
+ */
+#define SSH2_AGENT_REQUEST_IDENTITIES 0x0B
+
+/**
+ * Packet type of an agent identity response.
+ */
+#define SSH2_AGENT_IDENTITIES_ANSWER 0x0C
+
+/**
+ * Packet type of an agent sign request.
+ */
+#define SSH2_AGENT_SIGN_REQUEST 0x0D
+
+/**
+ * Packet type of an agent sign response.
+ */
+#define SSH2_AGENT_SIGN_RESPONSE 0x0E
+
+/**
+ * The comment to associate with public keys when listed.
+ */
+#define SSH_AGENT_COMMENT "Guacamole SSH Agent"
+
+/**
+ * The packet sent by the SSH agent when an operation is not supported.
+ */
+#define UNSUPPORTED "\x00\x00\x00\x0C\x05Unsupported"
+
+/**
+ * Data representing an SSH auth agent.
+ */
+typedef struct ssh_auth_agent {
+
+    /**
+     * The SSH channel being used for SSH agent protocol.
+     */
+    LIBSSH2_CHANNEL* channel;
+
+    /**
+     * The single private key to use for authentication.
+     */
+    ssh_key* identity;
+
+    /**
+     * Data read from the agent channel.
+     */
+    char buffer[4096];
+
+    /**
+     * The number of bytes of data currently stored in the buffer.
+     */
+    int buffer_length;
+
+} ssh_auth_agent;
+
+/**
+ * Handler for an agent sign request.
+ */
+void ssh_auth_agent_sign(ssh_auth_agent* auth_agent,
+        char* data, int data_length);
+
+/**
+ * Handler for an agent identity request.
+ */
+void ssh_auth_agent_list_identities(ssh_auth_agent* auth_agent);
+
+/**
+ * Generic handler for all packets received over the auth agent channel.
+ */
+void ssh_auth_agent_handle_packet(ssh_auth_agent* auth_agent,
+        uint8_t type, char* data, int data_length);
+
+/**
+ * Reads and handles a single packet from the SSH agent channel associated
+ * with the given ssh_auth_agent, returning the size of that packet, the size
+ * of the partial packet read, or a negative value if an error occurs.
+ */
+int ssh_auth_agent_read(ssh_auth_agent* auth_agent);
+
+/**
+ * Libssh2 callback, invoked when the auth agent channel is opened.
+ */
+void ssh_auth_agent_callback(LIBSSH2_SESSION *session,
+        LIBSSH2_CHANNEL *channel, void **abstract);
+
+#endif
+
diff --git a/src/protocols/ssh/ssh_client.c b/src/protocols/ssh/ssh_client.c
index 3d352d2..ffab21a 100644
--- a/src/protocols/ssh/ssh_client.c
+++ b/src/protocols/ssh/ssh_client.c
@@ -1,110 +1,153 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-ssh.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
- * James Muehlner <dagger10k at users.sourceforge.net>
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <stdio.h>
-#include <string.h>
-#include <pthread.h>
+#include "config.h"
+
+#include "client.h"
+#include "guac_sftp.h"
+#include "guac_ssh.h"
+#include "sftp.h"
+#include "terminal.h"
+
+#ifdef ENABLE_SSH_AGENT
+#include "ssh_agent.h"
+#endif
 
+#include <libssh2.h>
+#include <libssh2_sftp.h>
 #include <guacamole/client.h>
 #include <guacamole/protocol.h>
 #include <guacamole/socket.h>
+#include <openssl/err.h>
+#include <openssl/ssl.h>
 
-#include <libssh/libssh.h>
+#ifdef LIBSSH2_USES_GCRYPT
+#include <gcrypt.h>
+#endif
 
-#include "client.h"
-#include "common.h"
+#include <errno.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/time.h>
 
 /**
- * Reads a single line from STDIN.
+ * Produces a new user object containing a username and password or private
+ * key, prompting the user as necessary to obtain that information.
+ *
+ * @param client
+ *     The Guacamole client containing any existing user data, as well as the
+ *     terminal to use when prompting the user.
+ *
+ * @return
+ *     A new user object containing the user's username and other credentials.
  */
-static char* prompt(guac_client* client, const char* title, char* str, int size, bool echo) {
+static guac_common_ssh_user* guac_ssh_get_user(guac_client* client) {
 
     ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
 
-    int pos;
-    char in_byte;
-
-    /* Get STDIN and STDOUT */
-    int stdin_fd  = client_data->term->stdin_pipe_fd[0];
-    int stdout_fd = client_data->term->stdout_pipe_fd[1];
-
-    /* Print title */
-    guac_terminal_write_all(stdout_fd, title, strlen(title));
+    guac_common_ssh_user* user;
 
-    /* Make room for null terminator */
-    size--;
-
-    /* Read bytes until newline */
-    pos = 0;
-    while (pos < size && read(stdin_fd, &in_byte, 1) == 1) {
-
-        /* Backspace */
-        if (in_byte == 0x7F) {
+    /* Get username */
+    if (client_data->username[0] == 0)
+        guac_terminal_prompt(client_data->term, "Login as: ",
+                client_data->username, sizeof(client_data->username), true);
+
+    /* Create user object from username */
+    user = guac_common_ssh_create_user(client_data->username);
+
+    /* If key specified, import */
+    if (client_data->key_base64[0] != 0) {
+
+        guac_client_log(client, GUAC_LOG_DEBUG,
+                "Attempting private key import (WITHOUT passphrase)");
+
+        /* Attempt to read key without passphrase */
+        if (guac_common_ssh_user_import_key(user,
+                    client_data->key_base64, NULL)) {
+
+            /* Log failure of initial attempt */
+            guac_client_log(client, GUAC_LOG_DEBUG,
+                    "Initial import failed: %s",
+                    guac_common_ssh_key_error());
+  
+            guac_client_log(client, GUAC_LOG_DEBUG,
+                    "Re-attempting private key import (WITH passphrase)");
+
+            /* Prompt for passphrase if missing */
+            if (client_data->key_passphrase[0] == 0)
+                guac_terminal_prompt(client_data->term, "Key passphrase: ",
+                        client_data->key_passphrase,
+                        sizeof(client_data->key_passphrase), false);
+
+            /* Reattempt import with passphrase */
+            if (guac_common_ssh_user_import_key(user,
+                        client_data->key_base64,
+                        client_data->key_passphrase)) {
+
+                /* If still failing, give up */
+                guac_client_abort(client,
+                        GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
+                        "Auth key import failed: %s",
+                        guac_common_ssh_key_error());
+
+                guac_common_ssh_destroy_user(user);
+                return NULL;
 
-            if (pos > 0) {
-                guac_terminal_write_all(stdout_fd, "\b \b", 3);
-                pos--;
             }
-        }
 
-        /* CR (end of input */
-        else if (in_byte == 0x0D) {
-            guac_terminal_write_all(stdout_fd, "\r\n", 2);
-            break;
-        }
+        } /* end decrypt key with passphrase */
 
-        else {
+        /* Success */
+        guac_client_log(client, GUAC_LOG_INFO,
+                "Auth key successfully imported.");
 
-            /* Store character, update buffers */
-            str[pos++] = in_byte;
+    } /* end if key given */
 
-            /* Print character if echoing */
-            if (echo)
-                guac_terminal_write_all(stdout_fd, &in_byte, 1);
-            else
-                guac_terminal_write_all(stdout_fd, "*", 1);
+    /* Otherwise, use password */
+    else {
 
-        }
+        /* Get password if not provided */
+        if (client_data->password[0] == 0)
+            guac_terminal_prompt(client_data->term, "Password: ",
+                    client_data->password, sizeof(client_data->password),
+                    false);
+
+        /* Set provided password */
+        guac_common_ssh_user_set_password(user, client_data->password);
 
     }
 
-    str[pos] = 0;
-    return str;
+    /* Clear screen of any prompts */
+    guac_terminal_printf(client_data->term, "\x1B[H\x1B[J");
+
+    return user;
 
 }
 
@@ -116,11 +159,12 @@ void* ssh_input_thread(void* data) {
     char buffer[8192];
     int bytes_read;
 
-    int stdin_fd = client_data->term->stdin_pipe_fd[0];
-
     /* Write all data read */
-    while ((bytes_read = read(stdin_fd, buffer, sizeof(buffer))) > 0)
-        channel_write(client_data->term_channel, buffer, bytes_read);
+    while ((bytes_read = guac_terminal_read_stdin(client_data->term, buffer, sizeof(buffer))) > 0) {
+        pthread_mutex_lock(&(client_data->term_channel_lock));
+        libssh2_channel_write(client_data->term_channel, buffer, bytes_read);
+        pthread_mutex_unlock(&(client_data->term_channel_lock));
+    }
 
     return NULL;
 
@@ -131,121 +175,193 @@ void* ssh_client_thread(void* data) {
     guac_client* client = (guac_client*) data;
     ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
 
-    char name[1024];
-
     guac_socket* socket = client->socket;
     char buffer[8192];
-    int bytes_read = -1234;
-
-    int stdout_fd = client_data->term->stdout_pipe_fd[1];
 
     pthread_t input_thread;
 
-    /* Get username */
-    if (client_data->username[0] == 0 &&
-            prompt(client, "Login as: ", client_data->username, sizeof(client_data->username), true) == NULL)
+    /* Init SSH base libraries */
+    if (guac_common_ssh_init(client))
         return NULL;
 
+    /* Get user and credentials */
+    client_data->user = guac_ssh_get_user(client);
+
     /* Send new name */
-    snprintf(name, sizeof(name)-1, "%s@%s", client_data->username, client_data->hostname);
+    char name[1024];
+    snprintf(name, sizeof(name)-1, "%s@%s",
+            client_data->username, client_data->hostname);
     guac_protocol_send_name(socket, name);
 
-    /* Get password */
-    if (client_data->password[0] == 0 &&
-            prompt(client, "Password: ", client_data->password, sizeof(client_data->password), false) == NULL)
-        return NULL;
-
-
-    /* Clear screen */
-    guac_terminal_write_all(stdout_fd, "\x1B[H\x1B[J", 6);
-
     /* Open SSH session */
-    client_data->session = ssh_new();
+    client_data->session = guac_common_ssh_create_session(client,
+            client_data->hostname, client_data->port, client_data->user);
     if (client_data->session == NULL) {
-        guac_protocol_send_error(socket, "Unable to create SSH session.");
-        guac_socket_flush(socket);
+        /* Already aborted within guac_common_ssh_create_session() */
         return NULL;
     }
 
-    /* Set session options */
-    ssh_options_set(client_data->session, SSH_OPTIONS_HOST, client_data->hostname);
-    ssh_options_set(client_data->session, SSH_OPTIONS_PORT, &(client_data->port));
-    ssh_options_set(client_data->session, SSH_OPTIONS_USER, client_data->username);
+    pthread_mutex_init(&client_data->term_channel_lock, NULL);
 
-    /* Connect */
-    if (ssh_connect(client_data->session) != SSH_OK) {
-        guac_protocol_send_error(socket, "Unable to connect via SSH.");
-        guac_socket_flush(socket);
+    /* Open channel for terminal */
+    client_data->term_channel =
+        libssh2_channel_open_session(client_data->session->session);
+    if (client_data->term_channel == NULL) {
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
+                "Unable to open terminal channel.");
         return NULL;
     }
 
-    /* Authenticate */
-    if (ssh_userauth_password(client_data->session, NULL, client_data->password) != SSH_AUTH_SUCCESS) {
-        guac_protocol_send_error(socket, "SSH auth failed.");
-        guac_socket_flush(socket);
-        return NULL;
+#ifdef ENABLE_SSH_AGENT
+    /* Start SSH agent forwarding, if enabled */
+    if (client_data->enable_agent) {
+        libssh2_session_callback_set(client_data->session,
+                LIBSSH2_CALLBACK_AUTH_AGENT, (void*) ssh_auth_agent_callback);
+
+        /* Request agent forwarding */
+        if (libssh2_channel_request_auth_agent(client_data->term_channel))
+            guac_client_log(client, GUAC_LOG_ERROR, "Agent forwarding request failed");
+        else
+            guac_client_log(client, GUAC_LOG_INFO, "Agent forwarding enabled.");
     }
 
-    /* Open channel for terminal */
-    client_data->term_channel = channel_new(client_data->session);
-    if (client_data->term_channel == NULL) {
-        guac_protocol_send_error(socket, "Unable to open channel.");
-        guac_socket_flush(socket);
-        return NULL;
-    }
+    client_data->auth_agent = NULL;
+#endif
+
+    /* Start SFTP session as well, if enabled */
+    if (client_data->enable_sftp) {
+
+        /* Create SSH session specific for SFTP */
+        guac_client_log(client, GUAC_LOG_DEBUG, "Reconnecting for SFTP...");
+        client_data->sftp_session =
+            guac_common_ssh_create_session(client, client_data->hostname,
+                    client_data->port, client_data->user);
+        if (client_data->sftp_session == NULL) {
+            /* Already aborted within guac_common_ssh_create_session() */
+            return NULL;
+        }
+
+        /* Request SFTP */
+        client_data->sftp_filesystem =
+            guac_common_ssh_create_sftp_filesystem(
+                    client_data->sftp_session, "/");
+
+        /* Set generic (non-filesystem) file upload handler */
+        client->file_handler = guac_sftp_file_handler;
+
+        /* Init handlers for Guacamole-specific console codes */
+        client_data->term->upload_path_handler = guac_sftp_set_upload_path;
+        client_data->term->file_download_handler = guac_sftp_download_file;
+
+        guac_client_log(client, GUAC_LOG_DEBUG, "SFTP session initialized");
 
-    /* Open session for channel */
-    if (channel_open_session(client_data->term_channel) != SSH_OK) {
-        guac_protocol_send_error(socket, "Unable to open channel session.");
-        guac_socket_flush(socket);
-        return NULL;
     }
 
     /* Request PTY */
-    if (channel_request_pty_size(client_data->term_channel, "linux",
-            client_data->term->term_width, client_data->term->term_height) != SSH_OK) {
-        guac_protocol_send_error(socket, "Unable to allocate PTY for channel.");
-        guac_socket_flush(socket);
+    if (libssh2_channel_request_pty_ex(client_data->term_channel, "linux", sizeof("linux")-1, NULL, 0,
+            client_data->term->term_width, client_data->term->term_height, 0, 0)) {
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Unable to allocate PTY.");
         return NULL;
     }
 
-    /* Request shell */
-    if (channel_request_shell(client_data->term_channel) != SSH_OK) {
-        guac_protocol_send_error(socket, "Unable to associate shell with PTY.");
-        guac_socket_flush(socket);
+    /* If a command is specified, run that instead of a shell */
+    if (client_data->command != NULL) {
+        if (libssh2_channel_exec(client_data->term_channel, client_data->command)) {
+            guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
+                    "Unable to execute command.");
+            return NULL;
+        }
+    }
+
+    /* Otherwise, request a shell */
+    else if (libssh2_channel_shell(client_data->term_channel)) {
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
+                "Unable to associate shell with PTY.");
         return NULL;
     }
 
     /* Logged in */
-    guac_client_log_info(client, "SSH connection successful.");
+    guac_client_log(client, GUAC_LOG_INFO, "SSH connection successful.");
 
     /* Start input thread */
     if (pthread_create(&(input_thread), NULL, ssh_input_thread, (void*) client)) {
-        guac_client_log_error(client, "Unable to start SSH input thread");
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Unable to start input thread");
         return NULL;
     }
 
+    /* Set non-blocking */
+    libssh2_session_set_blocking(client_data->session->session, 0);
+
     /* While data available, write to terminal */
-    while (channel_is_open(client_data->term_channel)
-            && !channel_is_eof(client_data->term_channel)) {
+    int bytes_read = 0;
+    for (;;) {
+
+        /* Track total amount of data read */
+        int total_read = 0;
+
+        pthread_mutex_lock(&(client_data->term_channel_lock));
+
+        /* Stop reading at EOF */
+        if (libssh2_channel_eof(client_data->term_channel)) {
+            pthread_mutex_unlock(&(client_data->term_channel_lock));
+            break;
+        }
+
+        /* Read terminal data */
+        bytes_read = libssh2_channel_read(client_data->term_channel,
+                buffer, sizeof(buffer));
 
-        /* Repeat read if necessary */
-        if ((bytes_read = channel_read(client_data->term_channel, buffer, sizeof(buffer), 0)) == SSH_AGAIN)
-            continue;
+        pthread_mutex_unlock(&(client_data->term_channel_lock));
 
         /* Attempt to write data received. Exit on failure. */
         if (bytes_read > 0) {
-            int written = guac_terminal_write_all(stdout_fd, buffer, bytes_read);
+            int written = guac_terminal_write_stdout(client_data->term, buffer, bytes_read);
             if (written < 0)
                 break;
+
+            total_read += bytes_read;
+        }
+
+        else if (bytes_read < 0 && bytes_read != LIBSSH2_ERROR_EAGAIN)
+            break;
+
+#ifdef ENABLE_SSH_AGENT
+        /* If agent open, handle any agent packets */
+        if (client_data->auth_agent != NULL) {
+            bytes_read = ssh_auth_agent_read(client_data->auth_agent);
+            if (bytes_read > 0)
+                total_read += bytes_read;
+            else if (bytes_read < 0 && bytes_read != LIBSSH2_ERROR_EAGAIN)
+                client_data->auth_agent = NULL;
+        }
+#endif
+
+        /* Wait for more data if reads turn up empty */
+        if (total_read == 0) {
+            fd_set fds;
+            struct timeval timeout;
+
+            FD_ZERO(&fds);
+            FD_SET(client_data->session->fd, &fds);
+
+            /* Wait for one second */
+            timeout.tv_sec = 1;
+            timeout.tv_usec = 0;
+
+            if (select(client_data->session->fd + 1, &fds,
+                        NULL, NULL, &timeout) < 0)
+                break;
         }
 
     }
 
-    /* Wait for input thread to die */
+    /* Kill client and Wait for input thread to die */
+    guac_client_stop(client);
     pthread_join(input_thread, NULL);
 
-    guac_client_log_info(client, "SSH connection ended.");
+    pthread_mutex_destroy(&client_data->term_channel_lock);
+
+    guac_client_log(client, GUAC_LOG_INFO, "SSH connection ended.");
     return NULL;
 
 }
diff --git a/src/protocols/ssh/ssh_client.h b/src/protocols/ssh/ssh_client.h
index e964ba3..2c8859c 100644
--- a/src/protocols/ssh/ssh_client.h
+++ b/src/protocols/ssh/ssh_client.h
@@ -1,43 +1,31 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-ssh.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef __SSH_CLIENT_H
 #define __SSH_CLIENT_H
 
+#include "config.h"
+
 #include <guacamole/client.h>
 
 /**
diff --git a/src/protocols/ssh/terminal.c b/src/protocols/ssh/terminal.c
deleted file mode 100644
index b983931..0000000
--- a/src/protocols/ssh/terminal.c
+++ /dev/null
@@ -1,868 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-ssh.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * James Muehlner <dagger10k at users.sourceforge.net>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <pthread.h>
-
-#include <cairo/cairo.h>
-#include <pango/pangocairo.h>
-
-#include <guacamole/socket.h>
-#include <guacamole/protocol.h>
-#include <guacamole/client.h>
-#include <guacamole/error.h>
-
-#include "types.h"
-#include "buffer.h"
-#include "common.h"
-#include "display.h"
-#include "terminal.h"
-#include "terminal_handlers.h"
-
-void guac_terminal_reset(guac_terminal* term) {
-
-    int row;
-
-    /* Set current state */
-    term->char_handler = guac_terminal_echo; 
-    term->active_char_set = 0;
-    term->char_mapping[0] =
-    term->char_mapping[1] = NULL;
-
-    /* Reset cursor location */
-    term->cursor_row = term->visible_cursor_row = term->saved_cursor_row = 0;
-    term->cursor_col = term->visible_cursor_col = term->saved_cursor_col = 0;
-
-    /* Clear scrollback, buffer, and scoll region */
-    term->buffer->top = 0;
-    term->buffer->length = 0;
-    term->scroll_start = 0;
-    term->scroll_end = term->term_height - 1;
-    term->scroll_offset = 0;
-
-    /* Reset flags */
-    term->text_selected = false;
-    term->application_cursor_keys = false;
-    term->automatic_carriage_return = false;
-    term->insert_mode = false;
-
-    /* Reset tabs */
-    term->tab_interval = 8;
-    memset(term->custom_tabs, 0, sizeof(term->custom_tabs));
-
-    /* Clear terminal */
-    for (row=0; row<term->term_height; row++)
-        guac_terminal_set_columns(term, row, 0, term->term_width, &(term->default_char));
-
-}
-
-guac_terminal* guac_terminal_create(guac_client* client,
-        const char* font_name, int font_size,
-        int width, int height) {
-
-    guac_terminal_char default_char = {
-        .value = 0,
-        .attributes = {
-            .foreground = 7,
-            .background = 0,
-            .bold       = false,
-            .reverse    = false,
-            .underscore = false
-        }};
-
-    guac_terminal* term = malloc(sizeof(guac_terminal));
-    term->client = client;
-
-    /* Init buffer */
-    term->buffer = guac_terminal_buffer_alloc(1000, &default_char);
-
-    /* Init display */
-    term->display = guac_terminal_display_alloc(client,
-            font_name, font_size,
-            default_char.attributes.foreground,
-            default_char.attributes.background);
-
-    /* Fail if display init failed */
-    if (term->display == NULL) {
-        guac_error = GUAC_STATUS_BAD_STATE;
-        guac_error_message = "Display initialization failed";
-        free(term);
-        return NULL;
-    }
-
-    /* Init terminal state */
-    term->current_attributes = default_char.attributes;
-    term->default_char = default_char;
-
-    term->term_width   = width  / term->display->char_width;
-    term->term_height  = height / term->display->char_height;
-
-    /* Open STDOUT pipe */
-    if (pipe(term->stdout_pipe_fd)) {
-        guac_error = GUAC_STATUS_SEE_ERRNO;
-        guac_error_message = "Unable to open pipe for STDOUT";
-        free(term);
-        return NULL;
-    }
-
-    /* Open STDIN pipe */
-    if (pipe(term->stdin_pipe_fd)) {
-        guac_error = GUAC_STATUS_SEE_ERRNO;
-        guac_error_message = "Unable to open pipe for STDIN";
-        free(term);
-        return NULL;
-    }
-
-    /* Init terminal lock */
-    pthread_mutex_init(&(term->lock), NULL);
-
-    /* Size display */
-    guac_terminal_display_resize(term->display,
-            term->term_width, term->term_height);
-
-    /* Init terminal */
-    guac_terminal_reset(term);
-
-    return term;
-
-}
-
-void guac_terminal_free(guac_terminal* term) {
-    
-    /* Close terminal output pipe */
-    close(term->stdout_pipe_fd[1]);
-    close(term->stdout_pipe_fd[0]);
-
-    /* Close user input pipe */
-    close(term->stdin_pipe_fd[1]);
-    close(term->stdin_pipe_fd[0]);
-
-    /* Free display */
-    guac_terminal_display_free(term->display);
-
-    /* Free buffer */
-    guac_terminal_buffer_free(term->buffer);
-
-}
-
-int guac_terminal_set(guac_terminal* term, int row, int col, int codepoint) {
-
-    /* Build character with current attributes */
-    guac_terminal_char guac_char;
-    guac_char.value = codepoint;
-    guac_char.attributes = term->current_attributes;
-
-    guac_terminal_set_columns(term, row, col, col, &guac_char);
-
-    return 0;
-
-}
-
-void guac_terminal_commit_cursor(guac_terminal* term) {
-
-    guac_terminal_char* guac_char;
-
-    guac_terminal_buffer_row* old_row;
-    guac_terminal_buffer_row* new_row;
-
-    /* If no change, done */
-    if (term->visible_cursor_row == term->cursor_row && term->visible_cursor_col == term->cursor_col)
-        return;
-
-    /* Get old and new rows with cursor */
-    new_row = guac_terminal_buffer_get_row(term->buffer, term->cursor_row, term->cursor_col+1);
-    old_row = guac_terminal_buffer_get_row(term->buffer, term->visible_cursor_row, term->visible_cursor_col+1);
-
-    /* Clear cursor */
-    guac_char = &(old_row->characters[term->visible_cursor_col]);
-    guac_char->attributes.cursor = false;
-    guac_terminal_display_set_columns(term->display, term->visible_cursor_row + term->scroll_offset,
-            term->visible_cursor_col, term->visible_cursor_col, guac_char);
-
-    /* Set cursor */
-    guac_char = &(new_row->characters[term->cursor_col]);
-    guac_char->attributes.cursor = true;
-    guac_terminal_display_set_columns(term->display, term->cursor_row + term->scroll_offset,
-            term->cursor_col, term->cursor_col, guac_char);
-
-    term->visible_cursor_row = term->cursor_row;
-    term->visible_cursor_col = term->cursor_col;
-
-    return;
-
-}
-
-int guac_terminal_write(guac_terminal* term, const char* c, int size) {
-
-    while (size > 0) {
-        term->char_handler(term, *(c++));
-        size--;
-    }
-
-    return 0;
-
-}
-
-int guac_terminal_scroll_up(guac_terminal* term,
-        int start_row, int end_row, int amount) {
-
-    /* If scrolling entire display, update scroll offset */
-    if (start_row == 0 && end_row == term->term_height - 1) {
-
-        /* Scroll up visibly */
-        guac_terminal_display_copy_rows(term->display, start_row + amount, end_row, -amount);
-
-        /* Advance by scroll amount */
-        term->buffer->top += amount;
-        if (term->buffer->top >= term->buffer->available)
-            term->buffer->top -= term->buffer->available;
-
-        term->buffer->length += amount;
-        if (term->buffer->length > term->buffer->available)
-            term->buffer->length = term->buffer->available;
-
-        /* Update cursor location if within region */
-        if (term->visible_cursor_row >= start_row &&
-            term->visible_cursor_row <= end_row)
-            term->visible_cursor_row -= amount;
-
-    }
-
-    /* Otherwise, just copy row data upwards */
-    else
-        guac_terminal_copy_rows(term, start_row + amount, end_row, -amount);
-
-    /* Clear new area */
-    guac_terminal_clear_range(term,
-            end_row - amount + 1, 0,
-            end_row, term->term_width - 1);
-
-    return 0;
-}
-
-int guac_terminal_scroll_down(guac_terminal* term,
-        int start_row, int end_row, int amount) {
-
-    guac_terminal_copy_rows(term, start_row, end_row - amount, amount);
-
-    /* Clear new area */
-    guac_terminal_clear_range(term,
-            start_row, 0,
-            start_row + amount - 1, term->term_width - 1);
-
-    return 0;
-}
-
-int guac_terminal_clear_columns(guac_terminal* term,
-        int row, int start_col, int end_col) {
-
-    /* Build space */
-    guac_terminal_char blank;
-    blank.value = 0;
-    blank.attributes = term->current_attributes;
-
-    /* Clear */
-    guac_terminal_set_columns(term,
-        row, start_col, end_col, &blank);
-
-    return 0;
-
-}
-
-int guac_terminal_clear_range(guac_terminal* term,
-        int start_row, int start_col,
-        int end_row, int end_col) {
-
-    /* If not at far left, must clear sub-region to far right */
-    if (start_col > 0) {
-
-        /* Clear from start_col to far right */
-        guac_terminal_clear_columns(term,
-            start_row, start_col, term->term_width - 1);
-
-        /* One less row to clear */
-        start_row++;
-    }
-
-    /* If not at far right, must clear sub-region to far left */
-    if (end_col < term->term_width - 1) {
-
-        /* Clear from far left to end_col */
-        guac_terminal_clear_columns(term, end_row, 0, end_col);
-
-        /* One less row to clear */
-        end_row--;
-
-    }
-
-    /* Remaining region now guaranteed rectangular. Clear, if possible */
-    if (start_row <= end_row) {
-
-        int row;
-        for (row=start_row; row<=end_row; row++) {
-
-            /* Clear entire row */
-            guac_terminal_clear_columns(term, row, 0, term->term_width - 1);
-
-        }
-
-    }
-
-    return 0;
-
-}
-
-void guac_terminal_scroll_display_down(guac_terminal* terminal,
-        int scroll_amount) {
-
-    int start_row, end_row;
-    int dest_row;
-    int row, column;
-
-    /* Limit scroll amount by size of scrollback buffer */
-    if (scroll_amount > terminal->scroll_offset)
-        scroll_amount = terminal->scroll_offset;
-
-    /* If not scrolling at all, don't bother trying */
-    if (scroll_amount <= 0)
-        return;
-
-    /* Shift screen up */
-    if (terminal->term_height > scroll_amount)
-        guac_terminal_display_copy_rows(terminal->display,
-                scroll_amount, terminal->term_height - 1,
-                -scroll_amount);
-
-    /* Advance by scroll amount */
-    terminal->scroll_offset -= scroll_amount;
-
-    /* Get row range */
-    end_row   = terminal->term_height - terminal->scroll_offset - 1;
-    start_row = end_row - scroll_amount + 1;
-    dest_row  = terminal->term_height - scroll_amount;
-
-    /* Draw new rows from scrollback */
-    for (row=start_row; row<=end_row; row++) {
-
-        /* Get row from scrollback */
-        guac_terminal_buffer_row* buffer_row =
-            guac_terminal_buffer_get_row(terminal->buffer, row, 0);
-
-        /* Clear row */
-        guac_terminal_display_set_columns(terminal->display,
-                dest_row, 0, terminal->display->width, &(terminal->default_char));
-
-        /* Draw row */
-        guac_terminal_char* current = buffer_row->characters;
-        for (column=0; column<buffer_row->length; column++)
-            guac_terminal_display_set_columns(terminal->display,
-                    dest_row, column, column, current++);
-
-        /* Next row */
-        dest_row++;
-
-    }
-
-    guac_terminal_display_flush(terminal->display);
-    guac_protocol_send_sync(terminal->client->socket,
-            terminal->client->last_sent_timestamp);
-    guac_socket_flush(terminal->client->socket);
-
-}
-
-void guac_terminal_scroll_display_up(guac_terminal* terminal,
-        int scroll_amount) {
-
-    int start_row, end_row;
-    int dest_row;
-    int row, column;
-
-
-    /* Limit scroll amount by size of scrollback buffer */
-    if (terminal->scroll_offset + scroll_amount > terminal->buffer->length - terminal->term_height)
-        scroll_amount = terminal->buffer->length - terminal->scroll_offset - terminal->term_height;
-
-    /* If not scrolling at all, don't bother trying */
-    if (scroll_amount <= 0)
-        return;
-
-    /* Shift screen down */
-    if (terminal->term_height > scroll_amount)
-        guac_terminal_display_copy_rows(terminal->display,
-                0, terminal->term_height - scroll_amount - 1,
-                scroll_amount);
-
-    /* Advance by scroll amount */
-    terminal->scroll_offset += scroll_amount;
-
-    /* Get row range */
-    start_row = -terminal->scroll_offset;
-    end_row   = start_row + scroll_amount - 1;
-    dest_row  = 0;
-
-    /* Draw new rows from scrollback */
-    for (row=start_row; row<=end_row; row++) {
-
-        /* Get row from scrollback */
-        guac_terminal_buffer_row* buffer_row = 
-            guac_terminal_buffer_get_row(terminal->buffer, row, 0);
-
-        /* Clear row */
-        guac_terminal_display_set_columns(terminal->display,
-                dest_row, 0, terminal->display->width, &(terminal->default_char));
-
-        /* Draw row */
-        guac_terminal_char* current = buffer_row->characters;
-        for (column=0; column<buffer_row->length; column++)
-            guac_terminal_display_set_columns(terminal->display,
-                    dest_row, column, column, current++);
-
-        /* Next row */
-        dest_row++;
-
-    }
-
-    guac_terminal_display_flush(terminal->display);
-    guac_protocol_send_sync(terminal->client->socket,
-            terminal->client->last_sent_timestamp);
-    guac_socket_flush(terminal->client->socket);
-
-}
-
-void guac_terminal_select_redraw(guac_terminal* terminal) {
-
-    guac_terminal_display_select(terminal->display,
-            terminal->selection_start_row + terminal->scroll_offset,
-            terminal->selection_start_column,
-            terminal->selection_end_row + terminal->scroll_offset,
-            terminal->selection_end_column);
-
-}
-
-void guac_terminal_select_start(guac_terminal* terminal, int row, int column) {
-
-    terminal->selection_start_row = 
-    terminal->selection_end_row   = row;
-
-    terminal->selection_start_column = 
-    terminal->selection_end_column   = column;
-
-    terminal->text_selected = true;
-
-    guac_terminal_select_redraw(terminal);
-
-}
-
-void guac_terminal_select_update(guac_terminal* terminal, int row, int column) {
-
-    if (row != terminal->selection_end_row || column != terminal->selection_end_column) {
-        terminal->selection_end_row   = row;
-        terminal->selection_end_column   = column;
-
-        guac_terminal_select_redraw(terminal);
-    }
-
-}
-
-int __guac_terminal_buffer_string(guac_terminal_buffer_row* row, int start, int end, char* string) {
-
-    int length = 0;
-    int i;
-    for (i=start; i<=end; i++) {
-
-        int codepoint = row->characters[i].value;
-
-        /* If not null (blank), add to string */
-        if (codepoint != 0) {
-            int bytes = guac_terminal_encode_utf8(codepoint, string);
-            string += bytes;
-            length += bytes;
-        }
-
-    }
-
-    return length;
-
-}
-
-void guac_terminal_select_end(guac_terminal* terminal, char* string) {
-
-    /* Deselect */
-    terminal->text_selected = false;
-    guac_terminal_display_commit_select(terminal->display);
-
-    guac_terminal_buffer_row* buffer_row;
-
-    int row;
-
-    int start_row, start_col;
-    int end_row, end_col;
-
-    /* Ensure proper ordering of start and end coords */
-    if (terminal->selection_start_row <= terminal->selection_end_row) {
-        start_row = terminal->selection_start_row;
-        start_col = terminal->selection_start_column;
-        end_row   = terminal->selection_end_row;
-        end_col   = terminal->selection_end_column;
-    }
-    else {
-        end_row   = terminal->selection_start_row;
-        end_col   = terminal->selection_start_column;
-        start_row = terminal->selection_end_row;
-        start_col = terminal->selection_end_column;
-    }
-
-    /* If only one row, simply copy */
-    buffer_row = guac_terminal_buffer_get_row(terminal->buffer, start_row, 0);
-    if (end_row == start_row) {
-        if (buffer_row->length - 1 < end_col)
-            end_col = buffer_row->length - 1;
-        string += __guac_terminal_buffer_string(buffer_row, start_col, end_col, string);
-    }
-
-    /* Otherwise, copy multiple rows */
-    else {
-
-        /* Store first row */
-        string += __guac_terminal_buffer_string(buffer_row, start_col, buffer_row->length - 1, string);
-
-        /* Store all middle rows */
-        for (row=start_row+1; row<end_row; row++) {
-
-            buffer_row = guac_terminal_buffer_get_row(terminal->buffer, row, 0);
-
-            *(string++) = '\n';
-            string += __guac_terminal_buffer_string(buffer_row, 0, buffer_row->length - 1, string);
-
-        }
-
-        /* Store last row */
-        buffer_row = guac_terminal_buffer_get_row(terminal->buffer, end_row, 0);
-        if (buffer_row->length - 1 < end_col)
-            end_col = buffer_row->length - 1;
-
-        *(string++) = '\n';
-        string += __guac_terminal_buffer_string(buffer_row, 0, end_col, string);
-
-    }
-
-    /* Null terminator */
-    *string = 0;
-
-}
-
-void guac_terminal_copy_columns(guac_terminal* terminal, int row,
-        int start_column, int end_column, int offset) {
-
-    guac_terminal_display_copy_columns(terminal->display, row + terminal->scroll_offset,
-            start_column, end_column, offset);
-
-    guac_terminal_buffer_copy_columns(terminal->buffer, row,
-            start_column, end_column, offset);
-
-    /* Update cursor location if within region */
-    if (row == terminal->visible_cursor_row &&
-            terminal->visible_cursor_col >= start_column &&
-            terminal->visible_cursor_col <= end_column)
-        terminal->visible_cursor_col += offset;
-
-}
-
-void guac_terminal_copy_rows(guac_terminal* terminal,
-        int start_row, int end_row, int offset) {
-
-    guac_terminal_display_copy_rows(terminal->display,
-            start_row + terminal->scroll_offset, end_row + terminal->scroll_offset, offset);
-
-    guac_terminal_buffer_copy_rows(terminal->buffer,
-            start_row, end_row, offset);
-
-    /* Update cursor location if within region */
-    if (terminal->visible_cursor_row >= start_row &&
-        terminal->visible_cursor_row <= end_row)
-        terminal->visible_cursor_row += offset;
-
-}
-
-void guac_terminal_set_columns(guac_terminal* terminal, int row,
-        int start_column, int end_column, guac_terminal_char* character) {
-
-    guac_terminal_display_set_columns(terminal->display, row + terminal->scroll_offset,
-            start_column, end_column, character);
-
-    guac_terminal_buffer_set_columns(terminal->buffer, row,
-            start_column, end_column, character);
-
-    /* If visible cursor in current row, preserve state */
-    if (row == terminal->visible_cursor_row
-            && terminal->visible_cursor_col >= start_column
-            && terminal->visible_cursor_col <= end_column) {
-
-        /* Create copy of character with cursor attribute set */
-        guac_terminal_char cursor_character = *character;
-        cursor_character.attributes.cursor = true;
-
-        guac_terminal_display_set_columns(terminal->display, row + terminal->scroll_offset,
-                terminal->visible_cursor_col, terminal->visible_cursor_col, &cursor_character);
-
-        guac_terminal_buffer_set_columns(terminal->buffer, row,
-                terminal->visible_cursor_col, terminal->visible_cursor_col, &cursor_character);
-
-    }
-
-}
-
-static void __guac_terminal_redraw_rect(guac_terminal* term, int start_row, int start_col, int end_row, int end_col) {
-
-    int row, col;
-
-    /* Redraw region */
-    for (row=start_row; row<=end_row; row++) {
-
-        guac_terminal_buffer_row* buffer_row =
-            guac_terminal_buffer_get_row(term->buffer, row - term->scroll_offset, 0);
-
-        /* Clear row */
-        guac_terminal_display_set_columns(term->display,
-                row, start_col, end_col, &(term->default_char));
-
-        /* Copy characters */
-        for (col=start_col; col <= end_col && col < buffer_row->length; col++)
-            guac_terminal_display_set_columns(term->display, row, col, col,
-                    &(buffer_row->characters[col]));
-
-    }
-
-}
-
-void guac_terminal_resize(guac_terminal* term, int width, int height) {
-
-    /* If height is decreasing, shift display up */
-    if (height < term->term_height) {
-
-        int shift_amount;
-
-        /* Get number of rows actually occupying terminal space */
-        int used_height = term->buffer->length;
-        if (used_height > term->term_height)
-            used_height = term->term_height;
-
-        shift_amount = used_height - height;
-
-        /* If the new terminal bottom covers N rows, shift up N rows */
-        if (shift_amount > 0) {
-
-            guac_terminal_display_copy_rows(term->display,
-                    shift_amount, term->display->height - 1, -shift_amount);
-
-            /* Update buffer top and cursor row based on shift */
-            term->buffer->top += shift_amount;
-            term->cursor_row  -= shift_amount;
-            term->visible_cursor_row  -= shift_amount;
-
-            /* Redraw characters within old region */
-            __guac_terminal_redraw_rect(term, height - shift_amount, 0, height-1, width-1);
-
-        }
-
-    }
-
-    /* Resize display */
-    guac_terminal_display_flush(term->display);
-    guac_terminal_display_resize(term->display, width, height);
-
-    /* Reraw any characters on right if widening */
-    if (width > term->term_width)
-        __guac_terminal_redraw_rect(term, 0, term->term_width-1, height-1, width-1);
-
-    /* If height is increasing, shift display down */
-    if (height > term->term_height) {
-
-        /* If undisplayed rows exist in the buffer, shift them into view */
-        if (term->term_height < term->buffer->length) {
-
-            /* If the new terminal bottom reveals N rows, shift down N rows */
-            int shift_amount = height - term->term_height;
-
-            /* The maximum amount we can shift is the number of undisplayed rows */
-            int max_shift = term->buffer->length - term->term_height;
-
-            if (shift_amount > max_shift)
-                shift_amount = max_shift;
-
-            /* Update buffer top and cursor row based on shift */
-            term->buffer->top -= shift_amount;
-            term->cursor_row  += shift_amount;
-            term->visible_cursor_row  += shift_amount;
-
-            /* If scrolled enough, use scroll to fulfill entire resize */
-            if (term->scroll_offset >= shift_amount) {
-
-                term->scroll_offset -= shift_amount;
-
-                /* Draw characters from scroll at bottom */
-                __guac_terminal_redraw_rect(term, term->term_height, 0, term->term_height + shift_amount - 1, width-1);
-
-            }
-
-            /* Otherwise, fulfill with as much scroll as possible */
-            else {
-
-                /* Draw characters from scroll at bottom */
-                __guac_terminal_redraw_rect(term, term->term_height, 0, term->term_height + term->scroll_offset - 1, width-1);
-
-                /* Update shift_amount and scroll based on new rows */
-                shift_amount -= term->scroll_offset;
-                term->scroll_offset = 0;
-
-                /* If anything remains, move screen as necessary */
-                if (shift_amount > 0) {
-
-                    guac_terminal_display_copy_rows(term->display,
-                            0, term->display->height - shift_amount - 1, shift_amount);
-
-                    /* Draw characters at top from scroll */
-                    __guac_terminal_redraw_rect(term, 0, 0, shift_amount - 1, width-1);
-
-                }
-
-            }
-
-        } /* end if undisplayed rows exist */
-
-    }
-
-    /* Commit new dimensions */
-    term->term_width = width;
-    term->term_height = height;
-
-}
-
-int guac_terminal_send_data(guac_terminal* term, const char* data, int length) {
-    return guac_terminal_write_all(term->stdin_pipe_fd[1], data, length);
-}
-
-int guac_terminal_send_string(guac_terminal* term, const char* data) {
-    return guac_terminal_write_all(term->stdin_pipe_fd[1], data, strlen(data));
-}
-
-int guac_terminal_sendf(guac_terminal* term, const char* format, ...) {
-
-    int written;
-
-    va_list ap;
-    char buffer[1024];
-
-    /* Print to buffer */
-    va_start(ap, format);
-    written = vsnprintf(buffer, sizeof(buffer)-1, format, ap);
-    va_end(ap);
-
-    if (written < 0)
-        return written;
-
-    /* Write to STDIN */
-    return guac_terminal_write_all(term->stdin_pipe_fd[1], buffer, written);
-
-}
-
-void guac_terminal_set_tab(guac_terminal* term, int column) {
-
-    int i;
-
-    /* Search for available space, set if available */
-    for (i=0; i<GUAC_TERMINAL_MAX_TABS; i++) {
-
-        /* Set tab if space free */
-        if (term->custom_tabs[i] == 0) {
-            term->custom_tabs[i] = column+1;
-            break;
-        }
-
-    }
-
-}
-
-void guac_terminal_unset_tab(guac_terminal* term, int column) {
-
-    int i;
-
-    /* Search for given tab, unset if found */
-    for (i=0; i<GUAC_TERMINAL_MAX_TABS; i++) {
-
-        /* Unset tab if found */
-        if (term->custom_tabs[i] == column+1) {
-            term->custom_tabs[i] = 0;
-            break;
-        }
-
-    }
-
-}
-
-void guac_terminal_clear_tabs(guac_terminal* term) {
-    term->tab_interval = 0;
-    memset(term->custom_tabs, 0, sizeof(term->custom_tabs));
-}
-
-int guac_terminal_next_tab(guac_terminal* term, int column) {
-
-    int i;
-
-    /* Determine tab stop from interval */
-    int tabstop;
-    if (term->tab_interval != 0)
-        tabstop = (column / term->tab_interval + 1) * term->tab_interval;
-    else
-        tabstop = term->term_width - 1;
-
-    /* Walk custom tabs, trying to find an earlier occurrence */
-    for (i=0; i<GUAC_TERMINAL_MAX_TABS; i++) {
-
-        int custom_tabstop = term->custom_tabs[i] - 1;
-        if (custom_tabstop != -1 && custom_tabstop > column && custom_tabstop < tabstop)
-            tabstop = custom_tabstop;
-
-    }
-
-    return tabstop;
-}
-
-
diff --git a/src/protocols/ssh/terminal_handlers.h b/src/protocols/ssh/terminal_handlers.h
deleted file mode 100644
index 3b1faf6..0000000
--- a/src/protocols/ssh/terminal_handlers.h
+++ /dev/null
@@ -1,54 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-ssh.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef _SSH_GUAC_TERMINAL_HANDLERS
-#define _SSH_GUAC_TERMINAL_HANDLERS
-
-#include "terminal.h"
-
-int guac_terminal_echo(guac_terminal* term, char c);
-int guac_terminal_escape(guac_terminal* term, char c);
-int guac_terminal_g0_charset(guac_terminal* term, char c);
-int guac_terminal_g1_charset(guac_terminal* term, char c);
-int guac_terminal_g2_charset(guac_terminal* term, char c);
-int guac_terminal_g3_charset(guac_terminal* term, char c);
-int guac_terminal_csi(guac_terminal* term, char c);
-int guac_terminal_osc(guac_terminal* term, char c);
-int guac_terminal_ctrl_func(guac_terminal* term, char c);
-
-#endif
-
diff --git a/src/protocols/ssh/types.h b/src/protocols/ssh/types.h
deleted file mode 100644
index aba59bc..0000000
--- a/src/protocols/ssh/types.h
+++ /dev/null
@@ -1,122 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-ssh.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef _SSH_GUAC_TYPES_H
-#define _SSH_GUAC_TYPES_H
-
-#include <stdbool.h>
-
-/**
- * An RGB color, where each component ranges from 0 to 255.
- */
-typedef struct guac_terminal_color {
-
-    /**
-     * The red component of this color.
-     */
-    int red;
-
-    /**
-     * The green component of this color.
-     */
-    int green;
-
-    /**
-     * The blue component of this color.
-     */
-    int blue;
-
-} guac_terminal_color;
-
-/**
- * Terminal attributes, as can be applied to a single character.
- */
-typedef struct guac_terminal_attributes {
-
-    /**
-     * Whether the character should be rendered bold.
-     */
-    bool bold;
-
-    /**
-     * Whether the character should be rendered with reversed colors
-     * (background becomes foreground and vice-versa).
-     */
-    bool reverse;
-
-    /**
-     * Whether the associated character is highlighted by the cursor.
-     */
-    bool cursor;
-
-    /**
-     * Whether to render the character with underscore.
-     */
-    bool underscore;
-
-    /**
-     * The foreground color of this character, as a palette index.
-     */
-    int foreground;
-
-    /**
-     * The background color of this character, as a palette index.
-     */
-    int background;
-
-} guac_terminal_attributes;
-
-/**
- * Represents a single character for display in a terminal, including actual
- * character value, foreground color, and background color.
- */
-typedef struct guac_terminal_char {
-
-    /**
-     * The Unicode codepoint of the character to display.
-     */
-    int value;
-
-    /**
-     * The attributes of the character to display.
-     */
-    guac_terminal_attributes attributes;
-
-} guac_terminal_char;
-
-#endif
-
diff --git a/src/protocols/telnet/Makefile.am b/src/protocols/telnet/Makefile.am
new file mode 100644
index 0000000..fdaf00f
--- /dev/null
+++ b/src/protocols/telnet/Makefile.am
@@ -0,0 +1,54 @@
+#
+# Copyright (C) 2015 Glyptodon LLC
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+AUTOMAKE_OPTIONS = foreign
+ACLOCAL_AMFLAGS = -I m4
+
+lib_LTLIBRARIES = libguac-client-telnet.la
+
+libguac_client_telnet_la_SOURCES = \
+    client.c                       \
+    clipboard.c                    \
+    guac_handlers.c                \
+    telnet_client.c
+
+noinst_HEADERS =                \
+    client.h                    \
+    clipboard.h                 \
+    guac_handlers.h             \
+    telnet_client.h
+
+libguac_client_telnet_la_CFLAGS = \
+    -Werror -Wall -Iinclude       \
+    @LIBGUAC_INCLUDE@             \
+    @TERMINAL_INCLUDE@
+
+libguac_client_telnet_la_LIBADD = \
+    @COMMON_LTLIB@                \
+    @LIBGUAC_LTLIB@               \
+    @TERMINAL_LTLIB@
+
+libguac_client_telnet_la_LDFLAGS = \
+    -version-info 0:0:0            \
+    @PTHREAD_LIBS@                 \
+    @TELNET_LIBS@
+
diff --git a/src/protocols/telnet/Makefile.in b/src/protocols/telnet/Makefile.in
new file mode 100644
index 0000000..55525c5
--- /dev/null
+++ b/src/protocols/telnet/Makefile.in
@@ -0,0 +1,752 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+ at SET_MAKE@
+
+#
+# Copyright (C) 2015 Glyptodon LLC
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/protocols/telnet
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(top_srcdir)/depcomp $(noinst_HEADERS)
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
+	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+	$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__installdirs = "$(DESTDIR)$(libdir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+libguac_client_telnet_la_DEPENDENCIES =
+am_libguac_client_telnet_la_OBJECTS =  \
+	libguac_client_telnet_la-client.lo \
+	libguac_client_telnet_la-clipboard.lo \
+	libguac_client_telnet_la-guac_handlers.lo \
+	libguac_client_telnet_la-telnet_client.lo
+libguac_client_telnet_la_OBJECTS =  \
+	$(am_libguac_client_telnet_la_OBJECTS)
+AM_V_lt = $(am__v_lt_ at AM_V@)
+am__v_lt_ = $(am__v_lt_ at AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+libguac_client_telnet_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(libguac_client_telnet_la_CFLAGS) $(CFLAGS) \
+	$(libguac_client_telnet_la_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_ at AM_V@)
+am__v_P_ = $(am__v_P_ at AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_ at AM_V@)
+am__v_GEN_ = $(am__v_GEN_ at AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_ at AM_V@)
+am__v_at_ = $(am__v_at_ at AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I. at am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_ at AM_V@)
+am__v_CC_ = $(am__v_CC_ at AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_ at AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_ at AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
+SOURCES = $(libguac_client_telnet_la_SOURCES)
+DIST_SOURCES = $(libguac_client_telnet_la_SOURCES)
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CAIRO_LIBS = @CAIRO_LIBS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+COMMON_INCLUDE = @COMMON_INCLUDE@
+COMMON_LTLIB = @COMMON_LTLIB@
+COMMON_SSH_INCLUDE = @COMMON_SSH_INCLUDE@
+COMMON_SSH_LTLIB = @COMMON_SSH_LTLIB@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CUNIT_LIBS = @CUNIT_LIBS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+JPEG_LIBS = @JPEG_LIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBADD_DLOPEN = @LIBADD_DLOPEN@
+LIBGUAC_INCLUDE = @LIBGUAC_INCLUDE@
+LIBGUAC_LTLIB = @LIBGUAC_LTLIB@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MATH_LIBS = @MATH_LIBS@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGO_CFLAGS = @PANGO_CFLAGS@
+PANGO_LIBS = @PANGO_LIBS@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_LIBS = @PNG_LIBS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+PULSE_LIBS = @PULSE_LIBS@
+RANLIB = @RANLIB@
+RDP_LIBS = @RDP_LIBS@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SSH_LIBS = @SSH_LIBS@
+SSL_LIBS = @SSL_LIBS@
+STRIP = @STRIP@
+TELNET_LIBS = @TELNET_LIBS@
+TERMINAL_INCLUDE = @TERMINAL_INCLUDE@
+TERMINAL_LTLIB = @TERMINAL_LTLIB@
+UUID_LIBS = @UUID_LIBS@
+VERSION = @VERSION@
+VNC_LIBS = @VNC_LIBS@
+VORBIS_LIBS = @VORBIS_LIBS@
+WEBP_LIBS = @WEBP_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+init_dir = @init_dir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AUTOMAKE_OPTIONS = foreign
+ACLOCAL_AMFLAGS = -I m4
+lib_LTLIBRARIES = libguac-client-telnet.la
+libguac_client_telnet_la_SOURCES = \
+    client.c                       \
+    clipboard.c                    \
+    guac_handlers.c                \
+    telnet_client.c
+
+noinst_HEADERS = \
+    client.h                    \
+    clipboard.h                 \
+    guac_handlers.h             \
+    telnet_client.h
+
+libguac_client_telnet_la_CFLAGS = \
+    -Werror -Wall -Iinclude       \
+    @LIBGUAC_INCLUDE@             \
+    @TERMINAL_INCLUDE@
+
+libguac_client_telnet_la_LIBADD = \
+    @COMMON_LTLIB@                \
+    @LIBGUAC_LTLIB@               \
+    @TERMINAL_LTLIB@
+
+libguac_client_telnet_la_LDFLAGS = \
+    -version-info 0:0:0            \
+    @PTHREAD_LIBS@                 \
+    @TELNET_LIBS@
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/protocols/telnet/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --foreign src/protocols/telnet/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure:  $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+	@$(NORMAL_INSTALL)
+	@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+	list2=; for p in $$list; do \
+	  if test -f $$p; then \
+	    list2="$$list2 $$p"; \
+	  else :; fi; \
+	done; \
+	test -z "$$list2" || { \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+	}
+
+uninstall-libLTLIBRARIES:
+	@$(NORMAL_UNINSTALL)
+	@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+	for p in $$list; do \
+	  $(am__strip_dir) \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+	done
+
+clean-libLTLIBRARIES:
+	-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+	@list='$(lib_LTLIBRARIES)'; \
+	locs=`for p in $$list; do echo $$p; done | \
+	      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+	      sort -u`; \
+	test -z "$$locs" || { \
+	  echo rm -f $${locs}; \
+	  rm -f $${locs}; \
+	}
+
+libguac-client-telnet.la: $(libguac_client_telnet_la_OBJECTS) $(libguac_client_telnet_la_DEPENDENCIES) $(EXTRA_libguac_client_telnet_la_DEPENDENCIES) 
+	$(AM_V_CCLD)$(libguac_client_telnet_la_LINK) -rpath $(libdir) $(libguac_client_telnet_la_OBJECTS) $(libguac_client_telnet_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_telnet_la-client.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_telnet_la-clipboard.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_telnet_la-guac_handlers.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_telnet_la-telnet_client.Plo at am__quote@
+
+.c.o:
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+ at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+ at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+ at am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+libguac_client_telnet_la-client.lo: client.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_telnet_la_CFLAGS) $(CFLAGS) -MT libguac_client_telnet_la-client.lo -MD -MP -MF $(DEPDIR)/libguac_client_telnet_la-client.Tpo -c -o libguac_client_telnet_la-client.lo `test -f 'client.c' || echo '$(srcdir)/'`client.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_telnet_la-client.Tpo $(DEPDIR)/libguac_client_telnet_la-client.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='client.c' object='libguac_client_telnet_la-client.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_telnet_la_CFLAGS) $(CFLAGS) -c -o libguac_client_telnet_la-client.lo `test -f 'client.c' || echo '$(srcdir)/'`client.c
+
+libguac_client_telnet_la-clipboard.lo: clipboard.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_telnet_la_CFLAGS) $(CFLAGS) -MT libguac_client_telnet_la-clipboard.lo -MD -MP -MF $(DEPDIR)/libguac_client_telnet_la-clipboard.Tpo -c -o libguac_client_telnet_la-clipboard.lo `test -f 'clipboard.c' || echo '$(srcdir)/'`clipboard.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_telnet_la-clipboard.Tpo $(DEPDIR)/libguac_client_telnet_la-clipboard.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='clipboard.c' object='libguac_client_telnet_la-clipboard.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_telnet_la_CFLAGS) $(CFLAGS) -c -o libguac_client_telnet_la-clipboard.lo `test -f 'clipboard.c' || echo '$(srcdir)/'`clipboard.c
+
+libguac_client_telnet_la-guac_handlers.lo: guac_handlers.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_telnet_la_CFLAGS) $(CFLAGS) -MT libguac_client_telnet_la-guac_handlers.lo -MD -MP -MF $(DEPDIR)/libguac_client_telnet_la-guac_handlers.Tpo -c -o libguac_client_telnet_la-guac_handlers.lo `test -f 'guac_handlers.c' || echo '$(srcdir)/'`guac_handlers.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_telnet_la-guac_handlers.Tpo $(DEPDIR)/libguac_client_telnet_la-guac_handlers.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_handlers.c' object='libguac_client_telnet_la-guac_handlers.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_telnet_la_CFLAGS) $(CFLAGS) -c -o libguac_client_telnet_la-guac_handlers.lo `test -f 'guac_handlers.c' || echo '$(srcdir)/'`guac_handlers.c
+
+libguac_client_telnet_la-telnet_client.lo: telnet_client.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_telnet_la_CFLAGS) $(CFLAGS) -MT libguac_client_telnet_la-telnet_client.lo -MD -MP -MF $(DEPDIR)/libguac_client_telnet_la-telnet_client.Tpo -c -o libguac_client_telnet_la-telnet_client.lo `test -f 'telnet_client.c' || echo '$(srcdir)/'`telnet_client.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_telnet_la-telnet_client.Tpo $(DEPDIR)/libguac_client_telnet_la-telnet_client.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='telnet_client.c' object='libguac_client_telnet_la-telnet_client.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_telnet_la_CFLAGS) $(CFLAGS) -c -o libguac_client_telnet_la-telnet_client.lo `test -f 'telnet_client.c' || echo '$(srcdir)/'`telnet_client.c
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+	for dir in "$(DESTDIR)$(libdir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+	mostlyclean-am
+
+distclean: distclean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
+	clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \
+	ctags-am distclean distclean-compile distclean-generic \
+	distclean-libtool distclean-tags distdir dvi dvi-am html \
+	html-am info info-am install install-am install-data \
+	install-data-am install-dvi install-dvi-am install-exec \
+	install-exec-am install-html install-html-am install-info \
+	install-info-am install-libLTLIBRARIES install-man install-pdf \
+	install-pdf-am install-ps install-ps-am install-strip \
+	installcheck installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+	tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/protocols/telnet/client.c b/src/protocols/telnet/client.c
new file mode 100644
index 0000000..dab2bda
--- /dev/null
+++ b/src/protocols/telnet/client.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+#include "client.h"
+#include "clipboard.h"
+#include "guac_handlers.h"
+#include "telnet_client.h"
+#include "terminal.h"
+
+#include <langinfo.h>
+#include <locale.h>
+#include <pthread.h>
+#include <regex.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <guacamole/client.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+
+#define GUAC_TELNET_DEFAULT_FONT_NAME "monospace" 
+#define GUAC_TELNET_DEFAULT_FONT_SIZE 12
+#define GUAC_TELNET_DEFAULT_PORT      "23"
+
+/* Client plugin arguments */
+const char* GUAC_CLIENT_ARGS[] = {
+    "hostname",
+    "port",
+    "username",
+    "username-regex",
+    "password",
+    "password-regex",
+    "font-name",
+    "font-size",
+    "color-scheme",
+    NULL
+};
+
+enum __TELNET_ARGS_IDX {
+
+    /**
+     * The hostname to connect to. Required.
+     */
+    IDX_HOSTNAME,
+
+    /**
+     * The port to connect to. Optional.
+     */
+    IDX_PORT,
+
+    /**
+     * The name of the user to login as. Optional.
+     */
+    IDX_USERNAME,
+
+    /**
+     * The regular expression to use when searching for the username/login prompt.
+     * Optional.
+     */
+    IDX_USERNAME_REGEX,
+
+    /**
+     * The password to use when logging in. Optional.
+     */
+    IDX_PASSWORD,
+
+    /**
+     * The regular expression to use when searching for the password prompt.
+     * Optional.
+     */
+    IDX_PASSWORD_REGEX,
+
+    /**
+     * The name of the font to use within the terminal.
+     */
+    IDX_FONT_NAME,
+
+    /**
+     * The size of the font to use within the terminal, in points.
+     */
+    IDX_FONT_SIZE,
+
+    /**
+     * The name of the color scheme to use. Currently valid color schemes are:
+     * "black-white", "white-black", "gray-black", and "green-black", each
+     * following the "foreground-background" pattern. By default, this will be
+     * "gray-black".
+     */
+    IDX_COLOR_SCHEME,
+
+    TELNET_ARGS_COUNT
+};
+
+/**
+ * Compiles the given regular expression, returning NULL if compilation fails.
+ */
+static regex_t* __guac_telnet_compile_regex(guac_client* client, char* pattern) {
+
+    int compile_result;
+    regex_t* regex = malloc(sizeof(regex_t));
+
+    /* Compile regular expression */
+    compile_result = regcomp(regex, pattern,REG_EXTENDED | REG_NOSUB | REG_ICASE | REG_NEWLINE);
+
+    /* Notify of failure to parse/compile */
+    if (compile_result != 0) {
+        guac_client_log(client, GUAC_LOG_ERROR, "Regular expression '%s' could not be compiled.", pattern);
+        free(regex);
+        return NULL;
+    }
+
+    return regex;
+}
+
+int guac_client_init(guac_client* client, int argc, char** argv) {
+
+    guac_socket* socket = client->socket;
+
+    guac_telnet_client_data* client_data = malloc(sizeof(guac_telnet_client_data));
+
+    /* Init client data */
+    client->data = client_data;
+    client_data->telnet = NULL;
+    client_data->socket_fd = -1;
+    client_data->naws_enabled = 0;
+    client_data->echo_enabled = 1;
+
+    if (argc != TELNET_ARGS_COUNT) {
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Wrong number of arguments");
+        return -1;
+    }
+
+    /* Set locale and warn if not UTF-8 */
+    setlocale(LC_CTYPE, "");
+    if (strcmp(nl_langinfo(CODESET), "UTF-8") != 0)
+        guac_client_log(client, GUAC_LOG_INFO, "Current locale does not use UTF-8. Some characters may not render correctly.");
+
+    /* Read parameters */
+    strcpy(client_data->hostname,  argv[IDX_HOSTNAME]);
+    strcpy(client_data->username,  argv[IDX_USERNAME]);
+    strcpy(client_data->password,  argv[IDX_PASSWORD]);
+
+    /* Set username regex, if needed */
+    if (client_data->username[0] != 0) {
+
+        /* Compile regular expression */
+        if (argv[IDX_USERNAME_REGEX][0] != 0)
+            client_data->username_regex = __guac_telnet_compile_regex(client, argv[IDX_USERNAME_REGEX]);
+        else
+            client_data->username_regex = __guac_telnet_compile_regex(client, GUAC_TELNET_DEFAULT_USERNAME_REGEX);
+
+    }
+    else
+        client_data->username_regex = NULL;
+
+    /* Set password regex, if needed */
+    if (client_data->password[0] != 0) {
+
+        /* Compile regular expression */
+        if (argv[IDX_PASSWORD_REGEX][0] != 0)
+            client_data->password_regex = __guac_telnet_compile_regex(client, argv[IDX_PASSWORD_REGEX]);
+        else
+            client_data->password_regex = __guac_telnet_compile_regex(client, GUAC_TELNET_DEFAULT_PASSWORD_REGEX);
+
+    }
+    else
+        client_data->password_regex = NULL;
+
+    /* Read port */
+    if (argv[IDX_PORT][0] != 0)
+        strcpy(client_data->port, argv[IDX_PORT]);
+    else
+        strcpy(client_data->port, GUAC_TELNET_DEFAULT_PORT);
+
+    /* Read font name */
+    if (argv[IDX_FONT_NAME][0] != 0)
+        strcpy(client_data->font_name, argv[IDX_FONT_NAME]);
+    else
+        strcpy(client_data->font_name, GUAC_TELNET_DEFAULT_FONT_NAME );
+
+    /* Read font size */
+    if (argv[IDX_FONT_SIZE][0] != 0)
+        client_data->font_size = atoi(argv[IDX_FONT_SIZE]);
+    else
+        client_data->font_size = GUAC_TELNET_DEFAULT_FONT_SIZE;
+
+    /* Create terminal */
+    client_data->term = guac_terminal_create(client,
+            client_data->font_name, client_data->font_size,
+            client->info.optimal_resolution,
+            client->info.optimal_width, client->info.optimal_height,
+            argv[IDX_COLOR_SCHEME]);
+
+    /* Fail if terminal init failed */
+    if (client_data->term == NULL) {
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Terminal initialization failed");
+        return -1;
+    }
+
+    /* Send initial name */
+    guac_protocol_send_name(socket, client_data->hostname);
+
+    guac_socket_flush(socket);
+
+    /* Set basic handlers */
+    client->handle_messages   = guac_telnet_client_handle_messages;
+    client->key_handler       = guac_telnet_client_key_handler;
+    client->mouse_handler     = guac_telnet_client_mouse_handler;
+    client->size_handler      = guac_telnet_client_size_handler;
+    client->free_handler      = guac_telnet_client_free_handler;
+    client->clipboard_handler = guac_telnet_clipboard_handler;
+
+    /* Start client thread */
+    if (pthread_create(&(client_data->client_thread), NULL, guac_telnet_client_thread, (void*) client)) {
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Unable to start telnet client thread");
+        return -1;
+    }
+
+    /* Success */
+    return 0;
+
+}
+
diff --git a/src/protocols/telnet/client.h b/src/protocols/telnet/client.h
new file mode 100644
index 0000000..e72a1a6
--- /dev/null
+++ b/src/protocols/telnet/client.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GUAC_TELNET__CLIENT_H
+#define GUAC_TELNET__CLIENT_H
+
+#include "config.h"
+#include "terminal.h"
+
+#include <pthread.h>
+#include <regex.h>
+#include <sys/types.h>
+
+#include <libtelnet.h>
+
+#define GUAC_TELNET_DEFAULT_USERNAME_REGEX "[Ll]ogin:"
+#define GUAC_TELNET_DEFAULT_PASSWORD_REGEX "[Pp]assword:"
+
+/**
+ * Telnet-specific client data.
+ */
+typedef struct guac_telnet_client_data {
+
+    /**
+     * The hostname of the telnet server to connect to.
+     */
+    char hostname[1024];
+
+    /**
+     * The port of the telnet server to connect to.
+     */
+    char port[64];
+
+    /**
+     * The name of the user to login as.
+     */
+    char username[1024];
+
+    /**
+     * The regular expression to use when searching for the username
+     * prompt. This will be NULL unless the telnet client is currently
+     * searching for the username prompt.
+     */
+    regex_t* username_regex;
+
+    /**
+     * The password to give when authenticating.
+     */
+    char password[1024];
+
+    /**
+     * The regular expression to use when searching for the password
+     * prompt. This will be NULL unless the telnet client is currently
+     * searching for the password prompt.
+     */
+    regex_t* password_regex;
+
+    /**
+     * The name of the font to use for display rendering.
+     */
+    char font_name[1024];
+
+    /**
+     * The size of the font to use, in points.
+     */
+    int font_size;
+
+    /**
+     * The telnet client thread.
+     */
+    pthread_t client_thread;
+
+    /**
+     * The file descriptor of the socket connected to the telnet server,
+     * or -1 if no connection has been established.
+     */
+    int socket_fd;
+
+    /**
+     * Telnet connection, used by the telnet client thread.
+     */
+    telnet_t* telnet;
+
+    /**
+     * Whether window size should be sent when the window is resized.
+     */
+    int naws_enabled;
+
+    /**
+     * Whether all user input should be automatically echoed to the
+     * terminal.
+     */
+    int echo_enabled;
+
+    /**
+     * The terminal which will render all output from the telnet client.
+     */
+    guac_terminal* term;
+   
+} guac_telnet_client_data;
+
+#endif
+
diff --git a/src/protocols/telnet/clipboard.c b/src/protocols/telnet/clipboard.c
new file mode 100644
index 0000000..f8ff54c
--- /dev/null
+++ b/src/protocols/telnet/clipboard.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+#include "client.h"
+#include "clipboard.h"
+#include "terminal.h"
+
+#include <guacamole/client.h>
+#include <guacamole/stream.h>
+
+int guac_telnet_clipboard_handler(guac_client* client, guac_stream* stream,
+        char* mimetype) {
+
+    /* Clear clipboard and prepare for new data */
+    guac_telnet_client_data* client_data = (guac_telnet_client_data*) client->data;
+    guac_terminal_clipboard_reset(client_data->term, mimetype);
+
+    /* Set handlers for clipboard stream */
+    stream->blob_handler = guac_telnet_clipboard_blob_handler;
+    stream->end_handler = guac_telnet_clipboard_end_handler;
+
+    return 0;
+}
+
+int guac_telnet_clipboard_blob_handler(guac_client* client, guac_stream* stream,
+        void* data, int length) {
+
+    /* Append new data */
+    guac_telnet_client_data* client_data = (guac_telnet_client_data*) client->data;
+    guac_terminal_clipboard_append(client_data->term, data, length);
+
+    return 0;
+}
+
+int guac_telnet_clipboard_end_handler(guac_client* client, guac_stream* stream) {
+
+    /* Nothing to do - clipboard is implemented within client */
+
+    return 0;
+}
+
diff --git a/src/protocols/telnet/clipboard.h b/src/protocols/telnet/clipboard.h
new file mode 100644
index 0000000..8823bb4
--- /dev/null
+++ b/src/protocols/telnet/clipboard.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GUAC_TELNET__CLIPBOARD_H
+#define GUAC_TELNET__CLIPBOARD_H
+
+#include "config.h"
+
+#include <guacamole/client.h>
+#include <guacamole/stream.h>
+
+/**
+ * Handler for inbound clipboard data.
+ */
+int guac_telnet_clipboard_handler(guac_client* client, guac_stream* stream,
+        char* mimetype);
+
+/**
+ * Handler for stream data related to clipboard.
+ */
+int guac_telnet_clipboard_blob_handler(guac_client* client, guac_stream* stream,
+        void* data, int length);
+
+/**
+ * Handler for end-of-stream related to clipboard.
+ */
+int guac_telnet_clipboard_end_handler(guac_client* client, guac_stream* stream);
+
+#endif
+
diff --git a/src/protocols/telnet/guac_handlers.c b/src/protocols/telnet/guac_handlers.c
new file mode 100644
index 0000000..9832eb5
--- /dev/null
+++ b/src/protocols/telnet/guac_handlers.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+#include "client.h"
+#include "guac_handlers.h"
+#include "terminal.h"
+#include "telnet_client.h"
+
+#include <guacamole/client.h>
+#include <libtelnet.h>
+
+#include <pthread.h>
+#include <regex.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+int guac_telnet_client_handle_messages(guac_client* client) {
+
+    guac_telnet_client_data* client_data = (guac_telnet_client_data*) client->data;
+    return guac_terminal_render_frame(client_data->term);
+
+}
+
+int guac_telnet_client_mouse_handler(guac_client* client, int x, int y, int mask) {
+
+    guac_telnet_client_data* client_data = (guac_telnet_client_data*) client->data;
+    guac_terminal* term = client_data->term;
+
+    /* Send mouse if not searching for password or username */
+    if (client_data->password_regex == NULL && client_data->username_regex == NULL)
+        guac_terminal_send_mouse(term, x, y, mask);
+
+    return 0;
+
+}
+
+int guac_telnet_client_key_handler(guac_client* client, int keysym, int pressed) {
+
+    guac_telnet_client_data* client_data = (guac_telnet_client_data*) client->data;
+    guac_terminal* term = client_data->term;
+
+    /* Stop searching for password */
+    if (client_data->password_regex != NULL) {
+
+        guac_client_log(client, GUAC_LOG_DEBUG,
+                "Stopping password prompt search due to user input.");
+
+        regfree(client_data->password_regex);
+        free(client_data->password_regex);
+        client_data->password_regex = NULL;
+
+    }
+
+    /* Stop searching for username */
+    if (client_data->username_regex != NULL) {
+
+        guac_client_log(client, GUAC_LOG_DEBUG,
+                "Stopping username prompt search due to user input.");
+
+        regfree(client_data->username_regex);
+        free(client_data->username_regex);
+        client_data->username_regex = NULL;
+
+    }
+
+    /* Intercept and handle Pause / Break / Ctrl+0 as "IAC BRK" */
+    if (pressed && (
+                keysym == 0xFF13                  /* Pause */
+             || keysym == 0xFF6B                  /* Break */
+             || (term->mod_ctrl && keysym == '0') /* Ctrl + 0 */
+       )) {
+
+        /* Send IAC BRK */
+        telnet_iac(client_data->telnet, TELNET_BREAK);
+
+        return 0;
+    }
+
+    /* Send key */
+    guac_terminal_send_key(term, keysym, pressed);
+
+    return 0;
+
+}
+
+int guac_telnet_client_size_handler(guac_client* client, int width, int height) {
+
+    /* Get terminal */
+    guac_telnet_client_data* guac_client_data = (guac_telnet_client_data*) client->data;
+    guac_terminal* terminal = guac_client_data->term;
+
+    /* Resize terminal */
+    guac_terminal_resize(terminal, width, height);
+
+    /* Update terminal window size if connected */
+    if (guac_client_data->telnet != NULL && guac_client_data->naws_enabled)
+        guac_telnet_send_naws(guac_client_data->telnet, terminal->term_width, terminal->term_height);
+
+    return 0;
+}
+
+int guac_telnet_client_free_handler(guac_client* client) {
+
+    guac_telnet_client_data* guac_client_data = (guac_telnet_client_data*) client->data;
+
+    /* Close telnet connection */
+    if (guac_client_data->socket_fd != -1)
+        close(guac_client_data->socket_fd);
+
+    /* Kill terminal */
+    guac_terminal_free(guac_client_data->term);
+
+    /* Wait for and free telnet session, if connected */
+    if (guac_client_data->telnet != NULL) {
+        pthread_join(guac_client_data->client_thread, NULL);
+        telnet_free(guac_client_data->telnet);
+    }
+
+    /* Free password regex */
+    if (guac_client_data->password_regex != NULL) {
+        regfree(guac_client_data->password_regex);
+        free(guac_client_data->password_regex);
+    }
+
+    free(client->data);
+    return 0;
+
+}
+
diff --git a/src/protocols/telnet/guac_handlers.h b/src/protocols/telnet/guac_handlers.h
new file mode 100644
index 0000000..d2b67b7
--- /dev/null
+++ b/src/protocols/telnet/guac_handlers.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GUAC_TELNET__GUAC_HANDLERS_H
+#define GUAC_TELNET__GUAC_HANDLERS_H
+
+#include "config.h"
+
+#include <guacamole/client.h>
+
+/**
+ * Generic handler for sending outbound messages. Required by libguac and
+ * called periodically by guacd when the client is ready for more graphical
+ * updates.
+ */
+int guac_telnet_client_handle_messages(guac_client* client);
+
+/**
+ * Handler for key events. Required by libguac and called whenever key events
+ * are received.
+ */
+int guac_telnet_client_key_handler(guac_client* client, int keysym, int pressed);
+
+/**
+ * Handler for mouse events. Required by libguac and called whenever mouse
+ * events are received.
+ */
+int guac_telnet_client_mouse_handler(guac_client* client, int x, int y, int mask);
+
+/**
+ * Handler for size events. Required by libguac and called whenever the remote
+ * display (window) is resized.
+ */
+int guac_telnet_client_size_handler(guac_client* client, int width, int height);
+
+/**
+ * Free handler. Required by libguac and called when the guac_client is
+ * disconnected and must be cleaned up.
+ */
+int guac_telnet_client_free_handler(guac_client* client);
+
+#endif
+
diff --git a/src/protocols/telnet/telnet_client.c b/src/protocols/telnet/telnet_client.c
new file mode 100644
index 0000000..6adf145
--- /dev/null
+++ b/src/protocols/telnet/telnet_client.c
@@ -0,0 +1,501 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+#include "client.h"
+#include "telnet_client.h"
+#include "terminal.h"
+
+#include <guacamole/client.h>
+#include <guacamole/protocol.h>
+#include <libtelnet.h>
+
+#include <errno.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+/**
+ * Support levels for various telnet options, required for connection
+ * negotiation by telnet_init(), part of libtelnet.
+ */
+static const telnet_telopt_t __telnet_options[] = {
+    { TELNET_TELOPT_ECHO,        TELNET_WONT, TELNET_DO   },
+    { TELNET_TELOPT_TTYPE,       TELNET_WILL, TELNET_DONT },
+    { TELNET_TELOPT_COMPRESS2,   TELNET_WONT, TELNET_DO   },
+    { TELNET_TELOPT_MSSP,        TELNET_WONT, TELNET_DO   },
+    { TELNET_TELOPT_NAWS,        TELNET_WILL, TELNET_DONT },
+    { TELNET_TELOPT_NEW_ENVIRON, TELNET_WILL, TELNET_DONT },
+    { -1, 0, 0 }
+};
+
+/**
+ * Write the entire buffer given to the specified file descriptor, retrying
+ * the write automatically if necessary. This function will return a value
+ * not equal to the buffer's size iff an error occurs which prevents all
+ * future writes.
+ *
+ * @param fd The file descriptor to write to.
+ * @param buffer The buffer to write.
+ * @param size The number of bytes from the buffer to write.
+ */
+static int __guac_telnet_write_all(int fd, const char* buffer, int size) {
+
+    int remaining = size;
+    while (remaining > 0) {
+
+        /* Attempt to write data */
+        int ret_val = write(fd, buffer, remaining);
+        if (ret_val <= 0)
+            return -1;
+
+        /* If successful, contine with what data remains (if any) */
+        remaining -= ret_val;
+        buffer += ret_val;
+
+    }
+
+    return size;
+
+}
+
+/**
+ * Searches for a line matching the stored password regex, appending the given
+ * buffer to the internal pattern matching buffer. The internal pattern match
+ * buffer is cleared whenever a newline is read. Returns TRUE if a match is found and the
+ * value is sent.
+ */
+static bool __guac_telnet_regex_search(guac_client* client, regex_t* regex, char* value, const char* buffer, int size) {
+
+    static char line_buffer[1024] = {0};
+    static int length = 0;
+
+    guac_telnet_client_data* client_data = (guac_telnet_client_data*) client->data;
+
+    int i;
+    const char* current;
+
+    /* Ensure line buffer contains only the most recent line */
+    current = buffer;
+    for (i = 0; i < size; i++) {
+
+        /* Reset line buffer and shift input buffer for each newline */
+        if (*(current++) == '\n') {
+            length = 0;
+            buffer += i;
+            size -= i;
+            i = 0;
+        }
+    }
+
+    /* Truncate if necessary */
+    if (size + length + 1 > sizeof(line_buffer))
+        size = sizeof(line_buffer) - length - 1;
+
+    /* Append to line */
+    memcpy(&(line_buffer[length]), buffer, size);
+    length += size;
+    line_buffer[length] = '\0';
+
+    /* Send value upon match */
+    if (regexec(regex, line_buffer, 0, NULL, 0) == 0) {
+
+        /* Send value */
+        guac_terminal_send_string(client_data->term, value);
+        guac_terminal_send_key(client_data->term, 0xFF0D, 1);
+        guac_terminal_send_key(client_data->term, 0xFF0D, 0);
+
+        /* Stop searching for prompt */
+        return TRUE;
+
+    }
+
+    return FALSE;
+}
+
+/**
+ * Event handler, as defined by libtelnet. This function is passed to
+ * telnet_init() and will be called for every event fired by libtelnet,
+ * including feature enable/disable and receipt/transmission of data.
+ */
+static void __guac_telnet_event_handler(telnet_t* telnet, telnet_event_t* event, void* data) {
+
+    guac_client* client = (guac_client*) data;
+    guac_telnet_client_data* client_data = (guac_telnet_client_data*) client->data;
+
+    switch (event->type) {
+
+        /* Terminal output received */
+        case TELNET_EV_DATA:
+            guac_terminal_write_stdout(client_data->term, event->data.buffer, event->data.size);
+
+            /* Continue search for username prompt */
+            if (client_data->username_regex != NULL) {
+                if (__guac_telnet_regex_search(client, client_data->username_regex, client_data->username,
+                                           event->data.buffer, event->data.size)) {
+                    guac_client_log(client, GUAC_LOG_DEBUG, "Username sent");
+                    regfree(client_data->username_regex);
+                    free(client_data->username_regex);
+                    client_data->username_regex = NULL;
+                }
+            }
+
+            /* Continue search for password prompt */
+            if (client_data->password_regex != NULL) {
+                if (__guac_telnet_regex_search(client, client_data->password_regex, client_data->password,
+                                           event->data.buffer, event->data.size)) {
+
+                    guac_client_log(client, GUAC_LOG_DEBUG, "Password sent");
+
+                    /* Do not continue searching for username once password is sent */
+                    if (client_data->username_regex != NULL) {
+                        regfree(client_data->username_regex);
+                        free(client_data->username_regex);
+                        client_data->username_regex = NULL;
+                    }
+
+                    regfree(client_data->password_regex);
+                    free(client_data->password_regex);
+                    client_data->password_regex = NULL;
+                }
+            }
+            break;
+
+        /* Data destined for remote end */
+        case TELNET_EV_SEND:
+            if (__guac_telnet_write_all(client_data->socket_fd, event->data.buffer, event->data.size)
+                    != event->data.size)
+                guac_client_stop(client);
+            break;
+
+        /* Remote feature enabled */
+        case TELNET_EV_WILL:
+            if (event->neg.telopt == TELNET_TELOPT_ECHO)
+                client_data->echo_enabled = 0; /* Disable local echo, as remote will echo */
+            break;
+
+        /* Remote feature disabled */
+        case TELNET_EV_WONT:
+            if (event->neg.telopt == TELNET_TELOPT_ECHO)
+                client_data->echo_enabled = 1; /* Enable local echo, as remote won't echo */
+            break;
+
+        /* Local feature enable */
+        case TELNET_EV_DO:
+            if (event->neg.telopt == TELNET_TELOPT_NAWS) {
+                client_data->naws_enabled = 1;
+                guac_telnet_send_naws(telnet, client_data->term->term_width, client_data->term->term_height);
+            }
+            break;
+
+        /* Terminal type request */
+        case TELNET_EV_TTYPE:
+            if (event->ttype.cmd == TELNET_TTYPE_SEND)
+                telnet_ttype_is(client_data->telnet, "linux");
+            break;
+
+        /* Environment request */
+        case TELNET_EV_ENVIRON:
+
+            /* Only send USER if entire environment was requested */
+            if (event->environ.size == 0)
+                guac_telnet_send_user(telnet, client_data->username);
+
+            break;
+
+        /* Connection warnings */
+        case TELNET_EV_WARNING:
+            guac_client_log(client, GUAC_LOG_WARNING, "%s", event->error.msg);
+            break;
+
+        /* Connection errors */
+        case TELNET_EV_ERROR:
+            guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
+                    "Telnet connection closing with error: %s", event->error.msg);
+            break;
+
+        /* Ignore other events */
+        default:
+            break;
+
+    }
+
+}
+
+/**
+ * Input thread, started by the main telnet client thread. This thread
+ * continuously reads from the terminal's STDIN and transfers all read
+ * data to the telnet connection.
+ *
+ * @param data The current guac_client instance.
+ * @return Always NULL.
+ */
+static void* __guac_telnet_input_thread(void* data) {
+
+    guac_client* client = (guac_client*) data;
+    guac_telnet_client_data* client_data = (guac_telnet_client_data*) client->data;
+
+    char buffer[8192];
+    int bytes_read;
+
+    /* Write all data read */
+    while ((bytes_read = guac_terminal_read_stdin(client_data->term, buffer, sizeof(buffer))) > 0) {
+        telnet_send(client_data->telnet, buffer, bytes_read);
+        if (client_data->echo_enabled)
+            guac_terminal_write_stdout(client_data->term, buffer, bytes_read);
+    }
+
+    return NULL;
+
+}
+
+/**
+ * Connects to the telnet server specified within the data associated
+ * with the given guac_client, which will have been populated by
+ * guac_client_init.
+ *
+ * @return The connected telnet instance, if successful, or NULL if the
+ *         connection fails for any reason.
+ */
+static telnet_t* __guac_telnet_create_session(guac_client* client) {
+
+    int retval;
+
+    int fd;
+    struct addrinfo* addresses;
+    struct addrinfo* current_address;
+
+    char connected_address[1024];
+    char connected_port[64];
+
+    guac_telnet_client_data* client_data = (guac_telnet_client_data*) client->data;
+
+    struct addrinfo hints = {
+        .ai_family   = AF_UNSPEC,
+        .ai_socktype = SOCK_STREAM,
+        .ai_protocol = IPPROTO_TCP
+    };
+
+    /* Get socket */
+    fd = socket(AF_INET, SOCK_STREAM, 0);
+
+    /* Get addresses connection */
+    if ((retval = getaddrinfo(client_data->hostname, client_data->port,
+                    &hints, &addresses))) {
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Error parsing given address or port: %s",
+                gai_strerror(retval));
+        return NULL;
+
+    }
+
+    /* Attempt connection to each address until success */
+    current_address = addresses;
+    while (current_address != NULL) {
+
+        int retval;
+
+        /* Resolve hostname */
+        if ((retval = getnameinfo(current_address->ai_addr,
+                current_address->ai_addrlen,
+                connected_address, sizeof(connected_address),
+                connected_port, sizeof(connected_port),
+                NI_NUMERICHOST | NI_NUMERICSERV)))
+            guac_client_log(client, GUAC_LOG_DEBUG, "Unable to resolve host: %s", gai_strerror(retval));
+
+        /* Connect */
+        if (connect(fd, current_address->ai_addr,
+                        current_address->ai_addrlen) == 0) {
+
+            guac_client_log(client, GUAC_LOG_DEBUG, "Successfully connected to "
+                    "host %s, port %s", connected_address, connected_port);
+
+            /* Done if successful connect */
+            break;
+
+        }
+
+        /* Otherwise log information regarding bind failure */
+        else
+            guac_client_log(client, GUAC_LOG_DEBUG, "Unable to connect to "
+                    "host %s, port %s: %s",
+                    connected_address, connected_port, strerror(errno));
+
+        current_address = current_address->ai_next;
+
+    }
+
+    /* If unable to connect to anything, fail */
+    if (current_address == NULL) {
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Unable to connect to any addresses.");
+        return NULL;
+    }
+
+    /* Free addrinfo */
+    freeaddrinfo(addresses);
+
+    /* Open telnet session */
+    telnet_t* telnet = telnet_init(__telnet_options, __guac_telnet_event_handler, 0, client);
+    if (telnet == NULL) {
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Telnet client allocation failed.");
+        return NULL;
+    }
+
+    /* Save file descriptor */
+    client_data->socket_fd = fd;
+
+    return telnet;
+
+}
+
+/**
+ * Sends a 16-bit value over the given telnet connection with the byte order
+ * required by the telnet protocol.
+ *
+ * @param telnet The telnet connection to use.
+ * @param value The value to send.
+ */
+static void __guac_telnet_send_uint16(telnet_t* telnet, uint16_t value) {
+
+    unsigned char buffer[2];
+    buffer[0] = (value >> 8) & 0xFF;
+    buffer[1] =  value       & 0xFF;
+
+    telnet_send(telnet, (char*) buffer, 2);
+
+}
+
+/**
+ * Sends an 8-bit value over the given telnet connection.
+ *
+ * @param telnet The telnet connection to use.
+ * @param value The value to send.
+ */
+static void __guac_telnet_send_uint8(telnet_t* telnet, uint8_t value) {
+    telnet_send(telnet, (char*) (&value), 1);
+}
+
+void guac_telnet_send_naws(telnet_t* telnet, uint16_t width, uint16_t height) {
+    telnet_begin_sb(telnet, TELNET_TELOPT_NAWS);
+    __guac_telnet_send_uint16(telnet, width);
+    __guac_telnet_send_uint16(telnet, height);
+    telnet_finish_sb(telnet);
+}
+
+void guac_telnet_send_user(telnet_t* telnet, const char* username) {
+
+    /* IAC SB NEW-ENVIRON IS */
+    telnet_begin_sb(telnet, TELNET_TELOPT_NEW_ENVIRON);
+    __guac_telnet_send_uint8(telnet, TELNET_ENVIRON_IS);
+
+    /* VAR "USER" */
+    __guac_telnet_send_uint8(telnet, TELNET_ENVIRON_VAR);
+    telnet_send(telnet, "USER", 4);
+
+    /* VALUE username */
+    __guac_telnet_send_uint8(telnet, TELNET_ENVIRON_VALUE);
+    telnet_send(telnet, username, strlen(username));
+
+    /* IAC SE */
+    telnet_finish_sb(telnet);
+
+}
+
+/**
+ * Waits for data on the given file descriptor for up to one second. The
+ * return value is identical to that of select(): 0 on timeout, < 0 on
+ * error, and > 0 on success.
+ *
+ * @param socket_fd The file descriptor to wait for.
+ * @return A value greater than zero on success, zero on timeout, and
+ *         less than zero on error.
+ */
+static int __guac_telnet_wait(int socket_fd) {
+
+    fd_set fds;
+    struct timeval timeout;
+
+    FD_ZERO(&fds);
+    FD_SET(socket_fd, &fds);
+
+    /* Wait for one second */
+    timeout.tv_sec = 1;
+    timeout.tv_usec = 0;
+
+    return select(socket_fd+1, &fds, NULL, NULL, &timeout);
+
+}
+
+void* guac_telnet_client_thread(void* data) {
+
+    guac_client* client = (guac_client*) data;
+    guac_telnet_client_data* client_data = (guac_telnet_client_data*) client->data;
+
+    pthread_t input_thread;
+    char buffer[8192];
+    int wait_result;
+
+    /* Open telnet session */
+    client_data->telnet = __guac_telnet_create_session(client);
+    if (client_data->telnet == NULL) {
+        /* Already aborted within __guac_telnet_create_session() */
+        return NULL;
+    }
+
+    /* Logged in */
+    guac_client_log(client, GUAC_LOG_INFO, "Telnet connection successful.");
+
+    /* Start input thread */
+    if (pthread_create(&(input_thread), NULL, __guac_telnet_input_thread, (void*) client)) {
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Unable to start input thread");
+        return NULL;
+    }
+
+    /* While data available, write to terminal */
+    while ((wait_result = __guac_telnet_wait(client_data->socket_fd)) >= 0) {
+
+        /* Resume waiting of no data available */
+        if (wait_result == 0)
+            continue;
+
+        int bytes_read = read(client_data->socket_fd, buffer, sizeof(buffer));
+        if (bytes_read <= 0)
+            break;
+
+        telnet_recv(client_data->telnet, buffer, bytes_read);
+
+    }
+
+    /* Kill client and Wait for input thread to die */
+    guac_client_stop(client);
+    pthread_join(input_thread, NULL);
+
+    guac_client_log(client, GUAC_LOG_INFO, "Telnet connection ended.");
+    return NULL;
+
+}
+
diff --git a/src/protocols/telnet/telnet_client.h b/src/protocols/telnet/telnet_client.h
new file mode 100644
index 0000000..6c72bde
--- /dev/null
+++ b/src/protocols/telnet/telnet_client.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GUAC_TELNET__TELNET_CLIENT_H
+#define GUAC_TELNET__TELNET_CLIENT_H
+
+#include "config.h"
+
+#include <libtelnet.h>
+
+#include <stdint.h>
+
+/**
+ * Main telnet client thread, handling transfer of telnet output to STDOUT.
+ */
+void* guac_telnet_client_thread(void* data);
+
+/**
+ * Send a telnet NAWS message indicating the given terminal window dimensions
+ * in characters.
+ */
+void guac_telnet_send_naws(telnet_t* telnet, uint16_t width, uint16_t height);
+
+/**
+ * Sends the given username by setting the remote USER environment variable
+ * using the telnet NEW-ENVIRON option.
+ */
+void guac_telnet_send_user(telnet_t* telnet, const char* username);
+
+#endif
+
diff --git a/src/protocols/vnc/Makefile.am b/src/protocols/vnc/Makefile.am
index ef792c7..86db0a7 100644
--- a/src/protocols/vnc/Makefile.am
+++ b/src/protocols/vnc/Makefile.am
@@ -1,57 +1,41 @@
-# ***** BEGIN LICENSE BLOCK *****
-# Version: MPL 1.1/GPL 2.0/LGPL 2.1
 #
-# The contents of this file are subject to the Mozilla Public License Version
-# 1.1 (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.mozilla.org/MPL/
+# Copyright (C) 2015 Glyptodon LLC
 #
-# Software distributed under the License is distributed on an "AS IS" basis,
-# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-# for the specific language governing rights and limitations under the
-# License.
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
 #
-# The Original Code is libguac-client-vnc.
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
 #
-# The Initial Developer of the Original Code is
-# Michael Jumper.
-# Portions created by the Initial Developer are Copyright (C) 2010
-# the Initial Developer. All Rights Reserved.
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
 #
-# Contributor(s):
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either the GNU General Public License Version 2 or later (the "GPL"), or
-# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-# in which case the provisions of the GPL or the LGPL are applicable instead
-# of those above. If you wish to allow use of your version of this file only
-# under the terms of either the GPL or the LGPL, and not to allow others to
-# use your version of this file under the terms of the MPL, indicate your
-# decision by deleting the provisions above and replace them with the notice
-# and other provisions required by the GPL or the LGPL. If you do not delete
-# the provisions above, a recipient may use your version of this file under
-# the terms of any one of the MPL, the GPL or the LGPL.
-#
-# ***** END LICENSE BLOCK *****
 
 AUTOMAKE_OPTIONS = foreign
-
 ACLOCAL_AMFLAGS = -I m4
-AM_CFLAGS = -Werror -Wall -pedantic -Iinclude @LIBGUAC_INCLUDE@
 
 lib_LTLIBRARIES = libguac-client-vnc.la
 
 libguac_client_vnc_la_SOURCES = \
     client.c                    \
-    convert.c                   \
+    clipboard.c                 \
     guac_handlers.c             \
-    vnc_handlers.c
+    vnc_handlers.c              
     
-noinst_HEADERS =    \
-    client.h        \
-    convert.h       \
-    guac_handlers.h \
-    vnc_handlers.h
+noinst_HEADERS =      \
+    client.h          \
+    clipboard.h       \
+    guac_handlers.h   \
+    vnc_handlers.h    
 
 # Optional PulseAudio support
 if ENABLE_PULSE
@@ -59,6 +43,26 @@ libguac_client_vnc_la_SOURCES += pulse.c
 noinst_HEADERS += pulse.h
 endif
 
-libguac_client_vnc_la_LDFLAGS = -version-info 0:0:0 @VNC_LIBS@ @CAIRO_LIBS@ @PULSE_LIBS@
-libguac_client_vnc_la_LIBADD  = @LIBGUAC_LTLIB@
+libguac_client_vnc_la_CFLAGS =        \
+    -Werror -Wall -pedantic -Iinclude \
+    @COMMON_INCLUDE@                  \
+    @COMMON_SSH_INCLUDE@              \
+    @LIBGUAC_INCLUDE@
+
+libguac_client_vnc_la_LDFLAGS = \
+    -version-info 0:0:0         \
+    @CAIRO_LIBS@                \
+    @PULSE_LIBS@                \
+    @VNC_LIBS@ 
+
+libguac_client_vnc_la_LIBADD = \
+    @COMMON_LTLIB@             \
+    @LIBGUAC_LTLIB@
+
+# Optional SFTP support
+if ENABLE_COMMON_SSH
+libguac_client_vnc_la_SOURCES += sftp.c
+noinst_HEADERS                += sftp.h
+libguac_client_vnc_la_LIBADD  += @COMMON_SSH_LTLIB@
+endif
 
diff --git a/src/protocols/vnc/Makefile.in b/src/protocols/vnc/Makefile.in
index c1f2ee3..c9b434f 100644
--- a/src/protocols/vnc/Makefile.in
+++ b/src/protocols/vnc/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.6 from Makefile.am.
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software
-# Foundation, Inc.
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
@@ -15,61 +14,75 @@
 
 @SET_MAKE@
 
-# ***** BEGIN LICENSE BLOCK *****
-# Version: MPL 1.1/GPL 2.0/LGPL 2.1
-#
-# The contents of this file are subject to the Mozilla Public License Version
-# 1.1 (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.mozilla.org/MPL/
-#
-# Software distributed under the License is distributed on an "AS IS" basis,
-# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-# for the specific language governing rights and limitations under the
-# License.
 #
-# The Original Code is libguac-client-vnc.
+# Copyright (C) 2015 Glyptodon LLC
 #
-# The Initial Developer of the Original Code is
-# Michael Jumper.
-# Portions created by the Initial Developer are Copyright (C) 2010
-# the Initial Developer. All Rights Reserved.
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
 #
-# Contributor(s):
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
 #
-# Alternatively, the contents of this file may be used under the terms of
-# either the GNU General Public License Version 2 or later (the "GPL"), or
-# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-# in which case the provisions of the GPL or the LGPL are applicable instead
-# of those above. If you wish to allow use of your version of this file only
-# under the terms of either the GPL or the LGPL, and not to allow others to
-# use your version of this file under the terms of the MPL, indicate your
-# decision by deleting the provisions above and replace them with the notice
-# and other provisions required by the GPL or the LGPL. If you do not delete
-# the provisions above, a recipient may use your version of this file under
-# the terms of any one of the MPL, the GPL or the LGPL.
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
 #
-# ***** END LICENSE BLOCK *****
 
 
 VPATH = @srcdir@
-am__make_dryrun = \
-  { \
-    am__dry=no; \
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
     case $$MAKEFLAGS in \
       *\\[\ \	]*) \
-        echo 'am--echo: ; @echo "AM"  OK' | $(MAKE) -f - 2>/dev/null \
-          | grep '^AM OK$$' >/dev/null || am__dry=yes;; \
-      *) \
-        for am__flg in $$MAKEFLAGS; do \
-          case $$am__flg in \
-            *=*|--*) ;; \
-            *n*) am__dry=yes; break;; \
-          esac; \
-        done;; \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
     esac; \
-    test $$am__dry = yes; \
-  }
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
 pkgdatadir = $(datadir)/@PACKAGE@
 pkgincludedir = $(includedir)/@PACKAGE@
 pkglibdir = $(libdir)/@PACKAGE@
@@ -92,9 +105,14 @@ host_triplet = @host@
 # Optional PulseAudio support
 @ENABLE_PULSE_TRUE at am__append_1 = pulse.c
 @ENABLE_PULSE_TRUE at am__append_2 = pulse.h
+
+# Optional SFTP support
+ at ENABLE_COMMON_SSH_TRUE@am__append_3 = sftp.c
+ at ENABLE_COMMON_SSH_TRUE@am__append_4 = sftp.h
+ at ENABLE_COMMON_SSH_TRUE@am__append_5 = @COMMON_SSH_LTLIB@
 subdir = src/protocols/vnc
-DIST_COMMON = $(am__noinst_HEADERS_DIST) $(srcdir)/Makefile.am \
-	$(srcdir)/Makefile.in
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(top_srcdir)/depcomp $(am__noinst_HEADERS_DIST)
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
 	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
@@ -103,6 +121,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
 CONFIG_CLEAN_FILES =
 CONFIG_CLEAN_VPATH_FILES =
 am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
@@ -134,29 +153,60 @@ am__uninstall_files_from_dir = { \
   }
 am__installdirs = "$(DESTDIR)$(libdir)"
 LTLIBRARIES = $(lib_LTLIBRARIES)
-libguac_client_vnc_la_DEPENDENCIES =
-am__libguac_client_vnc_la_SOURCES_DIST = client.c convert.c \
-	guac_handlers.c vnc_handlers.c pulse.c
- at ENABLE_PULSE_TRUE@am__objects_1 = pulse.lo
-am_libguac_client_vnc_la_OBJECTS = client.lo convert.lo \
-	guac_handlers.lo vnc_handlers.lo $(am__objects_1)
+am__DEPENDENCIES_1 =
+libguac_client_vnc_la_DEPENDENCIES = $(am__DEPENDENCIES_1)
+am__libguac_client_vnc_la_SOURCES_DIST = client.c clipboard.c \
+	guac_handlers.c vnc_handlers.c pulse.c sftp.c
+ at ENABLE_PULSE_TRUE@am__objects_1 = libguac_client_vnc_la-pulse.lo
+ at ENABLE_COMMON_SSH_TRUE@am__objects_2 = libguac_client_vnc_la-sftp.lo
+am_libguac_client_vnc_la_OBJECTS = libguac_client_vnc_la-client.lo \
+	libguac_client_vnc_la-clipboard.lo \
+	libguac_client_vnc_la-guac_handlers.lo \
+	libguac_client_vnc_la-vnc_handlers.lo $(am__objects_1) \
+	$(am__objects_2)
 libguac_client_vnc_la_OBJECTS = $(am_libguac_client_vnc_la_OBJECTS)
-libguac_client_vnc_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
-	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+AM_V_lt = $(am__v_lt_ at AM_V@)
+am__v_lt_ = $(am__v_lt_ at AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+libguac_client_vnc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(libguac_client_vnc_la_CFLAGS) $(CFLAGS) \
 	$(libguac_client_vnc_la_LDFLAGS) $(LDFLAGS) -o $@
-DEFAULT_INCLUDES = -I. at am__isrc@
+AM_V_P = $(am__v_P_ at AM_V@)
+am__v_P_ = $(am__v_P_ at AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_ at AM_V@)
+am__v_GEN_ = $(am__v_GEN_ at AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_ at AM_V@)
+am__v_at_ = $(am__v_at_ at AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I. at am__isrc@ -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/depcomp
 am__depfiles_maybe = depfiles
 am__mv = mv -f
 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
 	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
-	--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
-	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_ at AM_V@)
+am__v_CC_ = $(am__v_CC_ at AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
 CCLD = $(CC)
-LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
-	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
-	$(LDFLAGS) -o $@
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_ at AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_ at AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
 SOURCES = $(libguac_client_vnc_la_SOURCES)
 DIST_SOURCES = $(am__libguac_client_vnc_la_SOURCES_DIST)
 am__can_run_installinfo = \
@@ -164,14 +214,32 @@ am__can_run_installinfo = \
     n|no|NO) false;; \
     *) (install-info --version) >/dev/null 2>&1;; \
   esac
-am__noinst_HEADERS_DIST = client.h convert.h guac_handlers.h \
-	vnc_handlers.h pulse.h
+am__noinst_HEADERS_DIST = client.h clipboard.h guac_handlers.h \
+	vnc_handlers.h pulse.h sftp.h
 HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
 ETAGS = etags
 CTAGS = ctags
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 ACLOCAL = @ACLOCAL@
 AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -181,6 +249,10 @@ CAIRO_LIBS = @CAIRO_LIBS@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+COMMON_INCLUDE = @COMMON_INCLUDE@
+COMMON_LTLIB = @COMMON_LTLIB@
+COMMON_SSH_INCLUDE = @COMMON_SSH_INCLUDE@
+COMMON_SSH_LTLIB = @COMMON_SSH_LTLIB@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
 CUNIT_LIBS = @CUNIT_LIBS@
@@ -188,7 +260,6 @@ CYGPATH_W = @CYGPATH_W@
 DEFS = @DEFS@
 DEPDIR = @DEPDIR@
 DLLTOOL = @DLLTOOL@
-DL_LIBS = @DL_LIBS@
 DSYMUTIL = @DSYMUTIL@
 DUMPBIN = @DUMPBIN@
 ECHO_C = @ECHO_C@
@@ -203,8 +274,10 @@ INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
 INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+JPEG_LIBS = @JPEG_LIBS@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
+LIBADD_DLOPEN = @LIBADD_DLOPEN@
 LIBGUAC_INCLUDE = @LIBGUAC_INCLUDE@
 LIBGUAC_LTLIB = @LIBGUAC_LTLIB@
 LIBOBJS = @LIBOBJS@
@@ -215,6 +288,7 @@ LN_S = @LN_S@
 LTLIBOBJS = @LTLIBOBJS@
 MAKEINFO = @MAKEINFO@
 MANIFEST_TOOL = @MANIFEST_TOOL@
+MATH_LIBS = @MATH_LIBS@
 MKDIR_P = @MKDIR_P@
 NM = @NM@
 NMEDIT = @NMEDIT@
@@ -248,9 +322,14 @@ SHELL = @SHELL@
 SSH_LIBS = @SSH_LIBS@
 SSL_LIBS = @SSL_LIBS@
 STRIP = @STRIP@
+TELNET_LIBS = @TELNET_LIBS@
+TERMINAL_INCLUDE = @TERMINAL_INCLUDE@
+TERMINAL_LTLIB = @TERMINAL_LTLIB@
+UUID_LIBS = @UUID_LIBS@
 VERSION = @VERSION@
 VNC_LIBS = @VNC_LIBS@
 VORBIS_LIBS = @VORBIS_LIBS@
+WEBP_LIBS = @WEBP_LIBS@
 abs_builddir = @abs_builddir@
 abs_srcdir = @abs_srcdir@
 abs_top_builddir = @abs_top_builddir@
@@ -306,14 +385,25 @@ top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
 AUTOMAKE_OPTIONS = foreign
 ACLOCAL_AMFLAGS = -I m4
-AM_CFLAGS = -Werror -Wall -pedantic -Iinclude @LIBGUAC_INCLUDE@
 lib_LTLIBRARIES = libguac-client-vnc.la
-libguac_client_vnc_la_SOURCES = client.c convert.c guac_handlers.c \
-	vnc_handlers.c $(am__append_1)
-noinst_HEADERS = client.h convert.h guac_handlers.h vnc_handlers.h \
-	$(am__append_2)
-libguac_client_vnc_la_LDFLAGS = -version-info 0:0:0 @VNC_LIBS@ @CAIRO_LIBS@ @PULSE_LIBS@
-libguac_client_vnc_la_LIBADD = @LIBGUAC_LTLIB@
+libguac_client_vnc_la_SOURCES = client.c clipboard.c guac_handlers.c \
+	vnc_handlers.c $(am__append_1) $(am__append_3)
+noinst_HEADERS = client.h clipboard.h guac_handlers.h vnc_handlers.h \
+	$(am__append_2) $(am__append_4)
+libguac_client_vnc_la_CFLAGS = \
+    -Werror -Wall -pedantic -Iinclude \
+    @COMMON_INCLUDE@                  \
+    @COMMON_SSH_INCLUDE@              \
+    @LIBGUAC_INCLUDE@
+
+libguac_client_vnc_la_LDFLAGS = \
+    -version-info 0:0:0         \
+    @CAIRO_LIBS@                \
+    @PULSE_LIBS@                \
+    @VNC_LIBS@ 
+
+libguac_client_vnc_la_LIBADD = @COMMON_LTLIB@ @LIBGUAC_LTLIB@ \
+	$(am__append_5)
 all: all-am
 
 .SUFFIXES:
@@ -348,6 +438,7 @@ $(top_srcdir)/configure:  $(am__configure_deps)
 $(ACLOCAL_M4):  $(am__aclocal_m4_deps)
 	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
 $(am__aclocal_m4_deps):
+
 install-libLTLIBRARIES: $(lib_LTLIBRARIES)
 	@$(NORMAL_INSTALL)
 	@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
@@ -374,14 +465,17 @@ uninstall-libLTLIBRARIES:
 
 clean-libLTLIBRARIES:
 	-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
-	@list='$(lib_LTLIBRARIES)'; for p in $$list; do \
-	  dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
-	  test "$$dir" != "$$p" || dir=.; \
-	  echo "rm -f \"$${dir}/so_locations\""; \
-	  rm -f "$${dir}/so_locations"; \
-	done
+	@list='$(lib_LTLIBRARIES)'; \
+	locs=`for p in $$list; do echo $$p; done | \
+	      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+	      sort -u`; \
+	test -z "$$locs" || { \
+	  echo rm -f $${locs}; \
+	  rm -f $${locs}; \
+	}
+
 libguac-client-vnc.la: $(libguac_client_vnc_la_OBJECTS) $(libguac_client_vnc_la_DEPENDENCIES) $(EXTRA_libguac_client_vnc_la_DEPENDENCIES) 
-	$(libguac_client_vnc_la_LINK) -rpath $(libdir) $(libguac_client_vnc_la_OBJECTS) $(libguac_client_vnc_la_LIBADD) $(LIBS)
+	$(AM_V_CCLD)$(libguac_client_vnc_la_LINK) -rpath $(libdir) $(libguac_client_vnc_la_OBJECTS) $(libguac_client_vnc_la_LIBADD) $(LIBS)
 
 mostlyclean-compile:
 	-rm -f *.$(OBJEXT)
@@ -389,32 +483,78 @@ mostlyclean-compile:
 distclean-compile:
 	-rm -f *.tab.c
 
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/client.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/convert.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/guac_handlers.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/pulse.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/vnc_handlers.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_vnc_la-client.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_vnc_la-clipboard.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_vnc_la-guac_handlers.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_vnc_la-pulse.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_vnc_la-sftp.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_client_vnc_la-vnc_handlers.Plo at am__quote@
 
 .c.o:
- at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+ at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(COMPILE) -c $<
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(COMPILE) -c -o $@ $<
 
 .c.obj:
- at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+ at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(COMPILE) -c `$(CYGPATH_W) '$<'`
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
 
 .c.lo:
- at am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+ at am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+libguac_client_vnc_la-client.lo: client.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_vnc_la_CFLAGS) $(CFLAGS) -MT libguac_client_vnc_la-client.lo -MD -MP -MF $(DEPDIR)/libguac_client_vnc_la-client.Tpo -c -o libguac_client_vnc_la-client.lo `test -f 'client.c' || echo '$(srcdir)/'`client.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_vnc_la-client.Tpo $(DEPDIR)/libguac_client_vnc_la-client.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='client.c' object='libguac_client_vnc_la-client.lo' libtool=yes @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_vnc_la_CFLAGS) $(CFLAGS) -c -o libguac_client_vnc_la-client.lo `test -f 'client.c' || echo '$(srcdir)/'`client.c
+
+libguac_client_vnc_la-clipboard.lo: clipboard.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_vnc_la_CFLAGS) $(CFLAGS) -MT libguac_client_vnc_la-clipboard.lo -MD -MP -MF $(DEPDIR)/libguac_client_vnc_la-clipboard.Tpo -c -o libguac_client_vnc_la-clipboard.lo `test -f 'clipboard.c' || echo '$(srcdir)/'`clipboard.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_vnc_la-clipboard.Tpo $(DEPDIR)/libguac_client_vnc_la-clipboard.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='clipboard.c' object='libguac_client_vnc_la-clipboard.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_vnc_la_CFLAGS) $(CFLAGS) -c -o libguac_client_vnc_la-clipboard.lo `test -f 'clipboard.c' || echo '$(srcdir)/'`clipboard.c
+
+libguac_client_vnc_la-guac_handlers.lo: guac_handlers.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_vnc_la_CFLAGS) $(CFLAGS) -MT libguac_client_vnc_la-guac_handlers.lo -MD -MP -MF $(DEPDIR)/libguac_client_vnc_la-guac_handlers.Tpo -c -o libguac_client_vnc_la-guac_handlers.lo `test -f 'guac_handlers.c' || echo '$(srcdir)/'`guac_handlers.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_vnc_la-guac_handlers.Tpo $(DEPDIR)/libguac_client_vnc_la-guac_handlers.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='guac_handlers.c' object='libguac_client_vnc_la-guac_handlers.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_vnc_la_CFLAGS) $(CFLAGS) -c -o libguac_client_vnc_la-guac_handlers.lo `test -f 'guac_handlers.c' || echo '$(srcdir)/'`guac_handlers.c
+
+libguac_client_vnc_la-vnc_handlers.lo: vnc_handlers.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_vnc_la_CFLAGS) $(CFLAGS) -MT libguac_client_vnc_la-vnc_handlers.lo -MD -MP -MF $(DEPDIR)/libguac_client_vnc_la-vnc_handlers.Tpo -c -o libguac_client_vnc_la-vnc_handlers.lo `test -f 'vnc_handlers.c' || echo '$(srcdir)/'`vnc_handlers.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_vnc_la-vnc_handlers.Tpo $(DEPDIR)/libguac_client_vnc_la-vnc_handlers.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='vnc_handlers.c' object='libguac_client_vnc_la-vnc_handlers.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_vnc_la_CFLAGS) $(CFLAGS) -c -o libguac_client_vnc_la-vnc_handlers.lo `test -f 'vnc_handlers.c' || echo '$(srcdir)/'`vnc_handlers.c
+
+libguac_client_vnc_la-pulse.lo: pulse.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_vnc_la_CFLAGS) $(CFLAGS) -MT libguac_client_vnc_la-pulse.lo -MD -MP -MF $(DEPDIR)/libguac_client_vnc_la-pulse.Tpo -c -o libguac_client_vnc_la-pulse.lo `test -f 'pulse.c' || echo '$(srcdir)/'`pulse.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_vnc_la-pulse.Tpo $(DEPDIR)/libguac_client_vnc_la-pulse.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='pulse.c' object='libguac_client_vnc_la-pulse.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_vnc_la_CFLAGS) $(CFLAGS) -c -o libguac_client_vnc_la-pulse.lo `test -f 'pulse.c' || echo '$(srcdir)/'`pulse.c
+
+libguac_client_vnc_la-sftp.lo: sftp.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_vnc_la_CFLAGS) $(CFLAGS) -MT libguac_client_vnc_la-sftp.lo -MD -MP -MF $(DEPDIR)/libguac_client_vnc_la-sftp.Tpo -c -o libguac_client_vnc_la-sftp.lo `test -f 'sftp.c' || echo '$(srcdir)/'`sftp.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_client_vnc_la-sftp.Tpo $(DEPDIR)/libguac_client_vnc_la-sftp.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='sftp.c' object='libguac_client_vnc_la-sftp.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_client_vnc_la_CFLAGS) $(CFLAGS) -c -o libguac_client_vnc_la-sftp.lo `test -f 'sftp.c' || echo '$(srcdir)/'`sftp.c
 
 mostlyclean-libtool:
 	-rm -f *.lo
@@ -422,26 +562,15 @@ mostlyclean-libtool:
 clean-libtool:
 	-rm -rf .libs _libs
 
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
-	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
-	unique=`for i in $$list; do \
-	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-	  done | \
-	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
-	      END { if (nonempty) { for (i in files) print i; }; }'`; \
-	mkid -fID $$unique
-tags: TAGS
-
-TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
-		$(TAGS_FILES) $(LISP)
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
 	set x; \
 	here=`pwd`; \
-	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
-	unique=`for i in $$list; do \
-	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-	  done | \
-	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
-	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	$(am__define_uniq_tagged_files); \
 	shift; \
 	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
 	  test -n "$$unique" || unique=$$empty_fix; \
@@ -453,15 +582,11 @@ TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
 	      $$unique; \
 	  fi; \
 	fi
-ctags: CTAGS
-CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
-		$(TAGS_FILES) $(LISP)
-	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
-	unique=`for i in $$list; do \
-	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-	  done | \
-	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
-	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
 	test -z "$(CTAGS_ARGS)$$unique" \
 	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
 	     $$unique
@@ -470,6 +595,21 @@ GTAGS:
 	here=`$(am__cd) $(top_builddir) && pwd` \
 	  && $(am__cd) $(top_srcdir) \
 	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
 
 distclean-tags:
 	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
@@ -614,19 +754,19 @@ uninstall-am: uninstall-libLTLIBRARIES
 
 .MAKE: install-am install-strip
 
-.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
-	clean-libLTLIBRARIES clean-libtool ctags distclean \
-	distclean-compile distclean-generic distclean-libtool \
-	distclean-tags distdir dvi dvi-am html html-am info info-am \
-	install install-am install-data install-data-am install-dvi \
-	install-dvi-am install-exec install-exec-am install-html \
-	install-html-am install-info install-info-am \
-	install-libLTLIBRARIES install-man install-pdf install-pdf-am \
-	install-ps install-ps-am install-strip installcheck \
-	installcheck-am installdirs maintainer-clean \
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
+	clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \
+	ctags-am distclean distclean-compile distclean-generic \
+	distclean-libtool distclean-tags distdir dvi dvi-am html \
+	html-am info info-am install install-am install-data \
+	install-data-am install-dvi install-dvi-am install-exec \
+	install-exec-am install-html install-html-am install-info \
+	install-info-am install-libLTLIBRARIES install-man install-pdf \
+	install-pdf-am install-ps install-ps-am install-strip \
+	installcheck installcheck-am installdirs maintainer-clean \
 	maintainer-clean-generic mostlyclean mostlyclean-compile \
 	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
-	tags uninstall uninstall-am uninstall-libLTLIBRARIES
+	tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES
 
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
diff --git a/src/protocols/vnc/client.c b/src/protocols/vnc/client.c
index 7c6331e..7d999d4 100644
--- a/src/protocols/vnc/client.c
+++ b/src/protocols/vnc/client.c
@@ -1,58 +1,55 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Original Code is libguac-client-vnc.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <stdlib.h>
-#include <string.h>
-
-#include <rfb/rfbclient.h>
-
-#include <guacamole/socket.h>
-#include <guacamole/protocol.h>
-#include <guacamole/client.h>
-#include <guacamole/audio.h>
+#include "config.h"
 
 #include "client.h"
-#include "vnc_handlers.h"
+#include "clipboard.h"
+#include "guac_clipboard.h"
+#include "guac_dot_cursor.h"
 #include "guac_handlers.h"
+#include "guac_pointer_cursor.h"
+#include "vnc_handlers.h"
 
 #ifdef ENABLE_PULSE
 #include "pulse.h"
 #endif
 
+#ifdef ENABLE_COMMON_SSH
+#include "guac_sftp.h"
+#include "guac_ssh.h"
+#include "sftp.h"
+#endif
+
+#include <rfb/rfbclient.h>
+#include <rfb/rfbproto.h>
+#include <guacamole/client.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
 /* Client plugin arguments */
 const char* GUAC_CLIENT_ARGS[] = {
     "hostname",
@@ -62,6 +59,9 @@ const char* GUAC_CLIENT_ARGS[] = {
     "password",
     "swap-red-blue",
     "color-depth",
+    "cursor",
+    "autoretry",
+    "clipboard-encoding",
 
 #ifdef ENABLE_VNC_REPEATER
     "dest-host",
@@ -73,6 +73,22 @@ const char* GUAC_CLIENT_ARGS[] = {
     "audio-servername",
 #endif
 
+#ifdef ENABLE_VNC_LISTEN
+    "reverse-connect",
+    "listen-timeout",
+#endif
+
+#ifdef ENABLE_COMMON_SSH
+    "enable-sftp",
+    "sftp-hostname",
+    "sftp-port",
+    "sftp-username",
+    "sftp-password",
+    "sftp-private-key",
+    "sftp-passphrase",
+    "sftp-directory",
+#endif
+
     NULL
 };
 
@@ -85,6 +101,9 @@ enum VNC_ARGS_IDX {
     IDX_PASSWORD,
     IDX_SWAP_RED_BLUE,
     IDX_COLOR_DEPTH,
+    IDX_CURSOR,
+    IDX_AUTORETRY,
+    IDX_CLIPBOARD_ENCODING,
 
 #ifdef ENABLE_VNC_REPEATER
     IDX_DEST_HOST,
@@ -96,19 +115,181 @@ enum VNC_ARGS_IDX {
     IDX_AUDIO_SERVERNAME,
 #endif
 
+#ifdef ENABLE_VNC_LISTEN
+    IDX_REVERSE_CONNECT,
+    IDX_LISTEN_TIMEOUT,
+#endif
+
+#ifdef ENABLE_COMMON_SSH
+    IDX_ENABLE_SFTP,
+    IDX_SFTP_HOSTNAME,
+    IDX_SFTP_PORT,
+    IDX_SFTP_USERNAME,
+    IDX_SFTP_PASSWORD,
+    IDX_SFTP_PRIVATE_KEY,
+    IDX_SFTP_PASSPHRASE,
+    IDX_SFTP_DIRECTORY,
+#endif
+
     VNC_ARGS_COUNT
 };
 
-
 char* __GUAC_CLIENT = "GUAC_CLIENT";
 
+/**
+ * Allocates a new rfbClient instance given the parameters stored within the
+ * client, returning NULL on failure.
+ */
+static rfbClient* __guac_vnc_get_client(guac_client* client) {
+
+    rfbClient* rfb_client = rfbGetClient(8, 3, 4); /* 32-bpp client */
+    vnc_guac_client_data* guac_client_data =
+        (vnc_guac_client_data*) client->data;
+
+    /* Store Guac client in rfb client */
+    rfbClientSetClientData(rfb_client, __GUAC_CLIENT, client);
+
+    /* Framebuffer update handler */
+    rfb_client->GotFrameBufferUpdate = guac_vnc_update;
+    rfb_client->GotCopyRect = guac_vnc_copyrect;
+
+    /* Do not handle clipboard and local cursor if read-only */
+    if (guac_client_data->read_only == 0) {
+
+        /* Clipboard */
+        rfb_client->GotXCutText = guac_vnc_cut_text;
+
+        /* Set remote cursor */
+        if (guac_client_data->remote_cursor)
+            rfb_client->appData.useRemoteCursor = FALSE;
+
+        else {
+            /* Enable client-side cursor */
+            rfb_client->appData.useRemoteCursor = TRUE;
+            rfb_client->GotCursorShape = guac_vnc_cursor;
+        }
+    }
+
+    /* Password */
+    rfb_client->GetPassword = guac_vnc_get_password;
+
+    /* Depth */
+    guac_vnc_set_pixel_format(rfb_client, guac_client_data->color_depth);
+
+    /* Hook into allocation so we can handle resize. */
+    guac_client_data->rfb_MallocFrameBuffer = rfb_client->MallocFrameBuffer;
+    rfb_client->MallocFrameBuffer = guac_vnc_malloc_framebuffer;
+    rfb_client->canHandleNewFBSize = 1;
+
+    /* Set hostname and port */
+    rfb_client->serverHost = strdup(guac_client_data->hostname);
+    rfb_client->serverPort = guac_client_data->port;
+
+#ifdef ENABLE_VNC_REPEATER
+    /* Set repeater parameters if specified */
+    if (guac_client_data->dest_host) {
+        rfb_client->destHost = strdup(guac_client_data->dest_host);
+        rfb_client->destPort = guac_client_data->dest_port;
+    }
+#endif
+
+#ifdef ENABLE_VNC_LISTEN
+    /* If reverse connection enabled, start listening */
+    if (guac_client_data->reverse_connect) {
+
+        guac_client_log(client, GUAC_LOG_INFO, "Listening for connections on port %i",
+                guac_client_data->port);
+
+        /* Listen for connection from server */
+        rfb_client->listenPort = guac_client_data->port;
+        if (listenForIncomingConnectionsNoFork(rfb_client,
+                    guac_client_data->listen_timeout*1000) <= 0)
+            return NULL;
+
+    }
+#endif
+
+    /* Set encodings if provided */
+    if (guac_client_data->encodings)
+        rfb_client->appData.encodingsString =
+            strdup(guac_client_data->encodings);
+
+    /* Connect */
+    if (rfbInitClient(rfb_client, NULL, NULL))
+        return rfb_client;
+
+    /* If connection fails, return NULL */
+    return NULL;
+
+}
+
+/**
+ * Sets the encoding of clipboard data exchanged with the VNC server to the
+ * encoding having the given name. If the name is left blank, or is invalid,
+ * the standard ISO8859-1 encoding will be used.
+ *
+ * @param client
+ *     The client to set the clipboard encoding of.
+ *
+ * @param name
+ *     The name of the encoding to use for all clipboard data. Valid values
+ *     are: "ISO8859-1", "UTF-8", "UTF-16", "CP1252", or "".
+ *
+ * @return
+ *     Zero if the chosen encoding is standard for VNC, or non-zero if the VNC
+ *     standard is being violated.
+ */
+static int guac_vnc_set_clipboard_encoding(guac_client* client,
+        const char* name) {
+
+    vnc_guac_client_data* guac_client_data =
+        (vnc_guac_client_data*) client->data;
+
+    /* Use ISO8859-1 if explicitly selected or blank */
+    if (name[0] == '\0' || strcmp(name, "ISO8859-1") == 0) {
+        guac_client_data->clipboard_reader = GUAC_READ_ISO8859_1;
+        guac_client_data->clipboard_writer = GUAC_WRITE_ISO8859_1;
+        return 0;
+    }
+
+    /* UTF-8 */
+    if (strcmp(name, "UTF-8") == 0) {
+        guac_client_data->clipboard_reader = GUAC_READ_UTF8;
+        guac_client_data->clipboard_writer = GUAC_WRITE_UTF8;
+        return 1;
+    }
+
+    /* UTF-16 */
+    if (strcmp(name, "UTF-16") == 0) {
+        guac_client_data->clipboard_reader = GUAC_READ_UTF16;
+        guac_client_data->clipboard_writer = GUAC_WRITE_UTF16;
+        return 1;
+    }
+
+    /* CP1252 */
+    if (strcmp(name, "CP1252") == 0) {
+        guac_client_data->clipboard_reader = GUAC_READ_CP1252;
+        guac_client_data->clipboard_writer = GUAC_WRITE_CP1252;
+        return 1;
+    }
+
+    /* If encoding unrecognized, warn and default to ISO8859-1 */
+    guac_client_log(client, GUAC_LOG_WARNING,
+            "Encoding '%s' is invalid. Defaulting to ISO8859-1.", name);
+
+    guac_client_data->clipboard_reader = GUAC_READ_ISO8859_1;
+    guac_client_data->clipboard_writer = GUAC_WRITE_ISO8859_1;
+    return 0;
+
+}
+
 int guac_client_init(guac_client* client, int argc, char** argv) {
 
     rfbClient* rfb_client;
 
     vnc_guac_client_data* guac_client_data;
 
-    int read_only;
+    int retries_remaining;
 
     /* Set up libvncclient logging */
     rfbClientLog = guac_vnc_client_log_info;
@@ -117,8 +298,7 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
     /*** PARSE ARGUMENTS ***/
 
     if (argc != VNC_ARGS_COUNT) {
-        guac_protocol_send_error(client->socket, "Wrong argument count received.");
-        guac_socket_flush(client->socket);
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Wrong argument count received.");
         return 1;
     }
 
@@ -126,41 +306,93 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
     guac_client_data = malloc(sizeof(vnc_guac_client_data));
     client->data = guac_client_data;
 
-    /* Set read-only flag */
-    read_only = (strcmp(argv[IDX_READ_ONLY], "true") == 0);
+    guac_client_data->hostname = strdup(argv[IDX_HOSTNAME]);
+    guac_client_data->port = atoi(argv[IDX_PORT]);
+    guac_client_data->password = strdup(argv[IDX_PASSWORD]); /* NOTE: freed by libvncclient */
+    guac_client_data->default_surface = NULL;
 
-    /* Set red/blue swap flag */
+    /* Set flags */
+    guac_client_data->remote_cursor = (strcmp(argv[IDX_CURSOR], "remote") == 0);
     guac_client_data->swap_red_blue = (strcmp(argv[IDX_SWAP_RED_BLUE], "true") == 0);
+    guac_client_data->read_only     = (strcmp(argv[IDX_READ_ONLY], "true") == 0);
 
-    /* Freed after use by libvncclient */
-    guac_client_data->password = strdup(argv[IDX_PASSWORD]);
+    /* Parse color depth */
+    guac_client_data->color_depth = atoi(argv[IDX_COLOR_DEPTH]);
 
-    /*** INIT RFB CLIENT ***/
+#ifdef ENABLE_VNC_REPEATER
+    /* Set repeater parameters if specified */
+    if (argv[IDX_DEST_HOST][0] != '\0')
+        guac_client_data->dest_host = strdup(argv[IDX_DEST_HOST]);
+    else
+        guac_client_data->dest_host = NULL;
 
-    rfb_client = rfbGetClient(8, 3, 4); /* 32-bpp client */
+    if (argv[IDX_DEST_PORT][0] != '\0')
+        guac_client_data->dest_port = atoi(argv[IDX_DEST_PORT]);
+#endif
 
-    /* Store Guac client in rfb client */
-    rfbClientSetClientData(rfb_client, __GUAC_CLIENT, client);
+    /* Set encodings if specified */
+    if (argv[IDX_ENCODINGS][0] != '\0')
+        guac_client_data->encodings = strdup(argv[IDX_ENCODINGS]);
+    else
+        guac_client_data->encodings = NULL;
 
-    /* Framebuffer update handler */
-    rfb_client->GotFrameBufferUpdate = guac_vnc_update;
-    rfb_client->GotCopyRect = guac_vnc_copyrect;
+    /* Parse autoretry */
+    if (argv[IDX_AUTORETRY][0] != '\0')
+        retries_remaining = atoi(argv[IDX_AUTORETRY]);
+    else
+        retries_remaining = 0; 
 
-    /* Do not handle clipboard and local cursor if read-only */
-    if (read_only == 0) {
-        /* Enable client-side cursor */
-        rfb_client->GotCursorShape = guac_vnc_cursor;
-        rfb_client->appData.useRemoteCursor = TRUE;
+#ifdef ENABLE_VNC_LISTEN
+    /* Set reverse-connection flag */
+    guac_client_data->reverse_connect =
+        (strcmp(argv[IDX_REVERSE_CONNECT], "true") == 0);
 
-        /* Clipboard */
-        rfb_client->GotXCutText = guac_vnc_cut_text;
-    }
+    /* Parse listen timeout */
+    if (argv[IDX_LISTEN_TIMEOUT][0] != '\0')
+        guac_client_data->listen_timeout = atoi(argv[IDX_LISTEN_TIMEOUT]);
+    else
+        guac_client_data->listen_timeout = 5000;
+#endif
 
-    /* Password */
-    rfb_client->GetPassword = guac_vnc_get_password;
+    /* Init clipboard */
+    guac_client_data->clipboard = guac_common_clipboard_alloc(GUAC_VNC_CLIPBOARD_MAX_LENGTH);
 
-    /* Depth */
-    guac_vnc_set_pixel_format(rfb_client, atoi(argv[IDX_COLOR_DEPTH]));
+    /* Configure clipboard encoding */
+    if (guac_vnc_set_clipboard_encoding(client, argv[IDX_CLIPBOARD_ENCODING]))
+        guac_client_log(client, GUAC_LOG_INFO,
+                "Using non-standard VNC clipboard encoding: '%s'.",
+                argv[IDX_CLIPBOARD_ENCODING]);
+
+    /* Ensure connection is kept alive during lengthy connects */
+    guac_socket_require_keep_alive(client->socket);
+
+    /* Attempt connection */
+    rfb_client = __guac_vnc_get_client(client);
+
+    /* If unsuccessful, retry as many times as specified */
+    while (!rfb_client && retries_remaining > 0) {
+
+        struct timespec guac_vnc_connect_interval = {
+            .tv_sec  =  GUAC_VNC_CONNECT_INTERVAL/1000,
+            .tv_nsec = (GUAC_VNC_CONNECT_INTERVAL%1000)*1000000
+        };
+
+        guac_client_log(client, GUAC_LOG_INFO,
+                "Connect failed. Waiting %ims before retrying...",
+                GUAC_VNC_CONNECT_INTERVAL);
+
+        /* Wait for given interval then retry */
+        nanosleep(&guac_vnc_connect_interval, NULL);
+        rfb_client = __guac_vnc_get_client(client);
+        retries_remaining--;
+
+    }
+
+    /* If the final connect attempt fails, return error */
+    if (!rfb_client) {
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Unable to connect to VNC server.");
+        return 1;
+    }
 
 #ifdef ENABLE_PULSE
     guac_client_data->audio_enabled =
@@ -169,7 +401,10 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
     /* If an encoding is available, load an audio stream */
     if (guac_client_data->audio_enabled) {    
 
-        guac_client_data->audio = guac_audio_stream_alloc(client, NULL);
+        guac_client_data->audio = guac_audio_stream_alloc(client, NULL,
+                GUAC_VNC_AUDIO_RATE,
+                GUAC_VNC_AUDIO_CHANNELS,
+                GUAC_VNC_AUDIO_BPS);
 
         /* Load servername if specified */
         if (argv[IDX_AUDIO_SERVERNAME][0] != '\0')
@@ -181,7 +416,7 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
         /* If successful, init audio system */
         if (guac_client_data->audio != NULL) {
             
-            guac_client_log_info(client,
+            guac_client_log(client, GUAC_LOG_INFO,
                     "Audio will be encoded as %s",
                     guac_client_data->audio->encoder->mimetype);
 
@@ -195,43 +430,96 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
 
         /* Otherwise, audio loading failed */
         else
-            guac_client_log_info(client,
+            guac_client_log(client, GUAC_LOG_INFO,
                     "No available audio encoding. Sound disabled.");
 
     } /* end if audio enabled */
 #endif
 
-    /* Hook into allocation so we can handle resize. */
-    guac_client_data->rfb_MallocFrameBuffer = rfb_client->MallocFrameBuffer;
-    rfb_client->MallocFrameBuffer = guac_vnc_malloc_framebuffer;
-    rfb_client->canHandleNewFBSize = 1;
+#ifdef ENABLE_COMMON_SSH
+    guac_common_ssh_init(client);
 
-    /* Set hostname and port */
-    rfb_client->serverHost = strdup(argv[0]);
-    rfb_client->serverPort = atoi(argv[1]);
+    /* Connect via SSH if SFTP is enabled */
+    if (strcmp(argv[IDX_ENABLE_SFTP], "true") == 0) {
 
-#ifdef ENABLE_VNC_REPEATER
-    /* Set repeater parameters if specified */
-    if(argv[IDX_DEST_HOST][0] != '\0')
-        rfb_client->destHost = strdup(argv[IDX_DEST_HOST]);
+        guac_client_log(client, GUAC_LOG_DEBUG,
+                "Connecting via SSH for SFTP filesystem access.");
 
-    if(argv[IDX_DEST_PORT][0] != '\0')
-        rfb_client->destPort = atoi(argv[IDX_DEST_PORT]);
-#endif
+        guac_client_data->sftp_user =
+            guac_common_ssh_create_user(argv[IDX_SFTP_USERNAME]);
 
-    /* Set encodings if specified */
-    if (argv[IDX_ENCODINGS][0] != '\0')
-        rfb_client->appData.encodingsString = guac_client_data->encodings
-            = strdup(argv[IDX_ENCODINGS]);
-    else
-        guac_client_data->encodings = NULL;
+        /* Import private key, if given */
+        if (argv[IDX_SFTP_PRIVATE_KEY][0] != '\0') {
+
+            guac_client_log(client, GUAC_LOG_DEBUG,
+                    "Authenticating with private key.");
+
+            /* Abort if private key cannot be read */
+            if (guac_common_ssh_user_import_key(guac_client_data->sftp_user,
+                        argv[IDX_SFTP_PRIVATE_KEY],
+                        argv[IDX_SFTP_PASSPHRASE])) {
+                guac_common_ssh_destroy_user(guac_client_data->sftp_user);
+                return 1;
+            }
+
+        }
+
+        /* Otherwise, use specified password */
+        else {
+            guac_client_log(client, GUAC_LOG_DEBUG,
+                    "Authenticating with password.");
+            guac_common_ssh_user_set_password(guac_client_data->sftp_user,
+                    argv[IDX_SFTP_PASSWORD]);
+        }
+
+        /* Parse hostname - use VNC hostname by default */
+        const char* sftp_hostname = argv[IDX_SFTP_HOSTNAME];
+        if (sftp_hostname[0] == '\0')
+            sftp_hostname = guac_client_data->hostname;
+
+        /* Parse port, defaulting to standard SSH port */
+        const char* sftp_port = argv[IDX_SFTP_PORT];
+        if (sftp_port[0] == '\0')
+            sftp_port = "22";
+
+        /* Attempt SSH connection */
+        guac_client_data->sftp_session =
+            guac_common_ssh_create_session(client, sftp_hostname, sftp_port,
+                    guac_client_data->sftp_user);
+
+        /* Fail if SSH connection does not succeed */
+        if (guac_client_data->sftp_session == NULL) {
+            /* Already aborted within guac_common_ssh_create_session() */
+            guac_common_ssh_destroy_user(guac_client_data->sftp_user);
+            return 1;
+        }
+
+        /* Load and expose filesystem */
+        guac_client_data->sftp_filesystem =
+            guac_common_ssh_create_sftp_filesystem(
+                    guac_client_data->sftp_session, "/");
+
+        /* Abort if SFTP connection fails */
+        if (guac_client_data->sftp_filesystem == NULL) {
+            guac_common_ssh_destroy_session(guac_client_data->sftp_session);
+            guac_common_ssh_destroy_user(guac_client_data->sftp_user);
+            return 1;
+        }
+
+        /* Configure destination for basic uploads, if specified */
+        if (argv[IDX_SFTP_DIRECTORY][0] != '\0')
+            guac_common_ssh_sftp_set_upload_path(
+                    guac_client_data->sftp_filesystem,
+                    argv[IDX_SFTP_DIRECTORY]);
+
+        /* Set file handler for basic uploads */
+        client->file_handler = guac_vnc_sftp_file_handler;
+
+        guac_client_log(client, GUAC_LOG_DEBUG,
+                "SFTP connection succeeded.");
 
-    /* Connect */
-    if (!rfbInitClient(rfb_client, NULL, NULL)) {
-        guac_protocol_send_error(client->socket, "Error initializing VNC client");
-        guac_socket_flush(client->socket);
-        return 1;
     }
+#endif
 
     /* Set remaining client data */
     guac_client_data->rfb_client = rfb_client;
@@ -241,20 +529,32 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
     /* Set handlers */
     client->handle_messages = vnc_guac_client_handle_messages;
     client->free_handler = vnc_guac_client_free_handler;
-    if (read_only == 0) {
-        /* Do not handle mouse/keyboard/clipboard if read-only */
+
+    /* If not read-only, set input handlers and pointer */
+    if (guac_client_data->read_only == 0) {
+
+        /* Only handle mouse/keyboard/clipboard if not read-only */
         client->mouse_handler = vnc_guac_client_mouse_handler;
         client->key_handler = vnc_guac_client_key_handler;
-        client->clipboard_handler = vnc_guac_client_clipboard_handler;
+        client->clipboard_handler = guac_vnc_clipboard_handler;
+
+        /* If not read-only but cursor is remote, set a dot cursor */
+        if (guac_client_data->remote_cursor)
+            guac_common_set_dot_cursor(client);
+
+        /* Otherwise, set pointer until explicitly requested otherwise */
+        else
+            guac_common_set_pointer_cursor(client);
+
     }
 
     /* Send name */
     guac_protocol_send_name(client->socket, rfb_client->desktopName);
 
-    /* Send size */
-    guac_protocol_send_size(client->socket,
-            GUAC_DEFAULT_LAYER, rfb_client->width, rfb_client->height);
-
+    /* Create default surface */
+    guac_client_data->default_surface = guac_common_surface_alloc(client,
+            client->socket, GUAC_DEFAULT_LAYER,
+            rfb_client->width, rfb_client->height);
     return 0;
 
 }
diff --git a/src/protocols/vnc/client.h b/src/protocols/vnc/client.h
index 5c8e35c..428ffff 100644
--- a/src/protocols/vnc/client.h
+++ b/src/protocols/vnc/client.h
@@ -1,51 +1,48 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Original Code is libguac-client-vnc.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef __GUAC_VNC_CLIENT_H
 #define __GUAC_VNC_CLIENT_H
 
-#include <guacamole/client.h>
+#include "config.h"
+#include "guac_clipboard.h"
+#include "guac_surface.h"
+#include "guac_iconv.h"
+
 #include <guacamole/audio.h>
+#include <guacamole/layer.h>
 #include <rfb/rfbclient.h>
 
 #ifdef ENABLE_PULSE
 #include <pulse/pulseaudio.h>
 #endif
 
+#ifdef ENABLE_COMMON_SSH
+#include "guac_sftp.h"
+#include "guac_ssh.h"
+#include "guac_ssh_user.h"
+#endif
+
 /**
  * The maximum duration of a frame in milliseconds.
  */
@@ -56,20 +53,112 @@
  * milliseconds. If the server is silent for at least this amount of time, the
  * frame will be considered finished.
  */
-#define GUAC_VNC_FRAME_TIMEOUT 0
+#define GUAC_VNC_FRAME_TIMEOUT 10
+
+/**
+ * The number of milliseconds to wait between connection attempts.
+ */
+#define GUAC_VNC_CONNECT_INTERVAL 1000
+
+/**
+ * The maximum number of bytes to allow within the clipboard.
+ */
+#define GUAC_VNC_CLIPBOARD_MAX_LENGTH 262144
 
 extern char* __GUAC_CLIENT;
 
+/**
+ * VNC-specific client data.
+ */
 typedef struct vnc_guac_client_data {
-    
+
+    /**
+     * The underlying VNC client.
+     */
     rfbClient* rfb_client;
+
+    /**
+     * The original framebuffer malloc procedure provided by the initialized
+     * rfbClient.
+     */
     MallocFrameBufferProc rfb_MallocFrameBuffer;
 
+    /**
+     * Whether copyrect  was used to produce the latest update received
+     * by the VNC server.
+     */
     int copy_rect_used;
+
+    /**
+     * The hostname of the VNC server (or repeater) to connect to.
+     */
+    char* hostname;
+
+    /**
+     * The port of the VNC server (or repeater) to connect to.
+     */
+    int port;
+
+    /**
+     * The password given in the arguments.
+     */
     char* password;
+
+    /**
+     * Space-separated list of encodings to use within the VNC session.
+     */
     char* encodings;
+
+    /**
+     * Whether the red and blue components of each color should be swapped.
+     * This is mainly used for VNC servers that do not properly handle
+     * colors.
+     */
     int swap_red_blue;
 
+    /**
+     * The color depth to request, in bits.
+     */
+    int color_depth;
+
+    /**
+     * Whether this connection is read-only, and user input should be dropped.
+     */
+    int read_only;
+
+    /**
+     * The VNC host to connect to, if using a repeater.
+     */
+    char* dest_host;
+
+    /**
+     * The VNC port to connect to, if using a repeater.
+     */
+    int dest_port;
+
+#ifdef ENABLE_VNC_LISTEN
+    /**
+     * Whether not actually connecting to a VNC server, but rather listening
+     * for a connection from the VNC server (reverse connection).
+     */
+    int reverse_connect;
+
+    /**
+     * The maximum amount of time to wait when listening for connections, in
+     * milliseconds.
+     */
+    int listen_timeout;
+#endif
+
+    /**
+     * Whether the cursor should be rendered on the server (remote) or on the
+     * client (local).
+     */
+    int remote_cursor;
+
+    /**
+     * The layer holding the cursor image.
+     */
     guac_layer* cursor;
     
     /**
@@ -93,7 +182,44 @@ typedef struct vnc_guac_client_data {
      */
     pa_threaded_mainloop* pa_mainloop;
 #endif
-    
+
+    /**
+     * Internal clipboard.
+     */
+    guac_common_clipboard* clipboard;
+
+    /**
+     * Default surface.
+     */
+    guac_common_surface* default_surface;
+
+#ifdef ENABLE_COMMON_SSH
+    /**
+     * The user and credentials used to authenticate for SFTP.
+     */
+    guac_common_ssh_user* sftp_user;
+
+    /**
+     * The SSH session used for SFTP.
+     */
+    guac_common_ssh_session* sftp_session;
+
+    /**
+     * The exposed filesystem object, implemented with SFTP.
+     */
+    guac_object* sftp_filesystem;
+#endif
+
+    /**
+     * Clipboard encoding-specific reader.
+     */
+    guac_iconv_read* clipboard_reader;
+
+    /**
+     * Clipboard encoding-specific writer.
+     */
+    guac_iconv_write* clipboard_writer;
+
 } vnc_guac_client_data;
 
 #endif
diff --git a/src/protocols/vnc/clipboard.c b/src/protocols/vnc/clipboard.c
new file mode 100644
index 0000000..6f3a391
--- /dev/null
+++ b/src/protocols/vnc/clipboard.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+#include "client.h"
+#include "clipboard.h"
+#include "guac_clipboard.h"
+#include "guac_iconv.h"
+
+#include <guacamole/client.h>
+#include <guacamole/stream.h>
+#include <rfb/rfbclient.h>
+
+int guac_vnc_clipboard_handler(guac_client* client, guac_stream* stream,
+        char* mimetype) {
+
+    /* Clear clipboard and prepare for new data */
+    vnc_guac_client_data* client_data = (vnc_guac_client_data*) client->data;
+    guac_common_clipboard_reset(client_data->clipboard, mimetype);
+
+    /* Set handlers for clipboard stream */
+    stream->blob_handler = guac_vnc_clipboard_blob_handler;
+    stream->end_handler = guac_vnc_clipboard_end_handler;
+
+    return 0;
+}
+
+int guac_vnc_clipboard_blob_handler(guac_client* client, guac_stream* stream,
+        void* data, int length) {
+
+    /* Append new data */
+    vnc_guac_client_data* client_data = (vnc_guac_client_data*) client->data;
+    guac_common_clipboard_append(client_data->clipboard, (char*) data, length);
+
+    return 0;
+}
+
+int guac_vnc_clipboard_end_handler(guac_client* client, guac_stream* stream) {
+
+    vnc_guac_client_data* client_data = (vnc_guac_client_data*) client->data;
+    rfbClient* rfb_client = client_data->rfb_client;
+
+    char output_data[GUAC_VNC_CLIPBOARD_MAX_LENGTH];
+
+    const char* input = client_data->clipboard->buffer;
+    char* output = output_data;
+    guac_iconv_write* writer = client_data->clipboard_writer;
+
+    /* Convert clipboard contents */
+    guac_iconv(GUAC_READ_UTF8, &input, client_data->clipboard->length,
+               writer, &output, sizeof(output_data));
+
+    /* Send via VNC */
+    SendClientCutText(rfb_client, output_data, output - output_data);
+
+    return 0;
+}
+
diff --git a/src/protocols/vnc/clipboard.h b/src/protocols/vnc/clipboard.h
new file mode 100644
index 0000000..5ff2ef7
--- /dev/null
+++ b/src/protocols/vnc/clipboard.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _GUAC_VNC_CLIPBOARD_H
+#define _GUAC_VNC_CLIPBOARD_H
+
+#include "config.h"
+
+#include <guacamole/client.h>
+#include <guacamole/stream.h>
+
+/**
+ * Handler for inbound clipboard data.
+ */
+int guac_vnc_clipboard_handler(guac_client* client, guac_stream* stream,
+        char* mimetype);
+
+/**
+ * Handler for stream data related to clipboard.
+ */
+int guac_vnc_clipboard_blob_handler(guac_client* client, guac_stream* stream,
+        void* data, int length);
+
+/**
+ * Handler for end-of-stream related to clipboard.
+ */
+int guac_vnc_clipboard_end_handler(guac_client* client, guac_stream* stream);
+
+#endif
+
diff --git a/src/protocols/vnc/convert.c b/src/protocols/vnc/convert.c
deleted file mode 100644
index 8bc9e8d..0000000
--- a/src/protocols/vnc/convert.c
+++ /dev/null
@@ -1,116 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-vnc.
- *
- * The Initial Developer of the Original Code is
- * James Muehlner.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include <stdlib.h>
-#include <string.h>
-#include <iconv.h>
-#include <errno.h>
-
-char* convert(const char* from_charset, const char* to_charset, const char* input) {
-    size_t input_remaining;
-    size_t output_remaining;
-    size_t bytes_converted = 0;
-    char* output;
-    char* output_buffer;
-    char* new_buffer;
-    char* input_buffer;
-    size_t output_length;
-    iconv_t cd;
- 
-    cd = iconv_open(to_charset, from_charset);
-    
-    if(cd == (iconv_t) -1)
-        /* Cannot convert due to invalid character set */
-        return NULL;
- 
-    input_remaining = strlen(input);
-    input_buffer = (char*) input;
- 
-    /* Start the output buffer the same size as the input buffer */
-    output_length = input_remaining;
- 
-    /* Leave some space at the end for NULL terminator */
-    if (!(output = (char*) malloc(output_length + 4))) {
-        /* Cannot convert due to memory allocation error */
-        iconv_close(cd);
-        return NULL;
-    }
- 
-    do {
-        output_buffer = output + bytes_converted;
-        output_remaining = output_length - bytes_converted;
- 
-        bytes_converted = iconv(cd, &input_buffer, 
-                &input_remaining, &output_buffer, &output_remaining);
-
-        if(bytes_converted == -1) {
-            if(errno == E2BIG) {
-                /* The output buffer is too small, so allocate more space */
-                bytes_converted = output_buffer - output;
-                output_length += input_remaining * 2 + 8;
-         
-                if (!(new_buffer = (char*) realloc(output, output_length + 4))) {
-                    /* Cannot convert due to memory allocation error */
-                    iconv_close(cd);
-                    free(output);
-                    return NULL;
-                }
-         
-                output = new_buffer;
-                output_buffer = output + bytes_converted;
-            } 
-            else if (errno == EILSEQ) {
-                /* Invalid sequence detected, return what's been converted so far */
-                break;
-            } 
-            else if (errno == EINVAL) {
-                /* Incomplete sequence detected, can be ignored */
-                break;
-            }
-        }
-    } while (input_remaining);
- 
-    /* Flush the iconv conversion */
-    iconv(cd, NULL, NULL, &output_buffer, &output_remaining);
-    iconv_close(cd);
- 
-    /* Add the NULL terminator */
-    memset(output_buffer, 0, 4);
- 
-    return output;
-}
-
diff --git a/src/protocols/vnc/convert.h b/src/protocols/vnc/convert.h
deleted file mode 100644
index 8b37322..0000000
--- a/src/protocols/vnc/convert.h
+++ /dev/null
@@ -1,53 +0,0 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-vnc.
- *
- * The Initial Developer of the Original Code is
- * James Muehlner.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef __GUAC_VNC_VNC_CONVERT_H
-#define __GUAC_VNC_VNC_CONVERT_H
-
-#include <iconv.h>
-
-/**
- * Converts a string from one charset to another. Returns a newly allocated string.
- * @param from_charset The string representing the character set to convert from.
- * @param to_charset The string representing the character set to convert to.
- * @return A newly allocated string that is the result of the conversion, or NULL
- *         if an error has occured.
- */
-char* convert (const char* from_charset, const char* to_charset, const char* input);
-
-#endif
-
diff --git a/src/protocols/vnc/guac_handlers.c b/src/protocols/vnc/guac_handlers.c
index 9b378f8..be380df 100644
--- a/src/protocols/vnc/guac_handlers.c
+++ b/src/protocols/vnc/guac_handlers.c
@@ -1,62 +1,81 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-vnc.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <iconv.h>
+#include "config.h"
 
-#include <rfb/rfbclient.h>
+#include "client.h"
+#include "guac_clipboard.h"
+#include "guac_surface.h"
 
 #include <guacamole/client.h>
+#include <guacamole/protocol.h>
+#include <guacamole/timestamp.h>
+#include <rfb/rfbclient.h>
 
-#include "client.h"
-#include "convert.h"
+#ifdef ENABLE_COMMON_SSH
+#include <guac_sftp.h>
+#include <guac_ssh.h>
+#include <guac_ssh_user.h>
+#endif
 
 #ifdef ENABLE_PULSE
 #include "pulse.h"
 #endif
 
+#include <stdlib.h>
+
+/**
+ * Waits until data is available to be read from the given rfbClient, and thus
+ * a call to HandleRFBServerMessages() should not block. If the timeout elapses
+ * before data is available, zero is returned.
+ *
+ * @param rfb_client
+ *     The rfbClient to wait for.
+ *
+ * @param timeout
+ *     The maximum amount of time to wait, in microseconds.
+ *
+ * @returns
+ *     A positive value if data is available, zero if the timeout elapses
+ *     before data becomes available, or a negative value on error.
+ */
+static int guac_vnc_wait_for_messages(rfbClient* rfb_client, int timeout) {
+
+    /* Do not explicitly wait while data is on the buffer */
+    if (rfb_client->buffered)
+        return 1;
+
+    /* If no data on buffer, wait for data on socket */
+    return WaitForMessage(rfb_client, timeout);
+
+}
+
 int vnc_guac_client_handle_messages(guac_client* client) {
 
-    rfbClient* rfb_client = ((vnc_guac_client_data*) client->data)->rfb_client;
+    vnc_guac_client_data* guac_client_data = (vnc_guac_client_data*) client->data;
+    rfbClient* rfb_client = guac_client_data->rfb_client;
 
     /* Initially wait for messages */
-    int wait_result = WaitForMessage(rfb_client, 1000000);
+    int wait_result = guac_vnc_wait_for_messages(rfb_client, 1000000);
     guac_timestamp frame_start = guac_timestamp_current();
     while (wait_result > 0) {
 
@@ -65,7 +84,7 @@ int vnc_guac_client_handle_messages(guac_client* client) {
 
         /* Handle any message received */
         if (!HandleRFBServerMessage(rfb_client)) {
-            guac_client_log_error(client, "Error handling VNC server message\n");
+            guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Error handling message from VNC server.");
             return 1;
         }
 
@@ -75,7 +94,7 @@ int vnc_guac_client_handle_messages(guac_client* client) {
 
         /* Wait again if frame remaining */
         if (frame_remaining > 0)
-            wait_result = WaitForMessage(rfb_client,
+            wait_result = guac_vnc_wait_for_messages(rfb_client,
                     GUAC_VNC_FRAME_TIMEOUT*1000);
         else
             break;
@@ -84,15 +103,15 @@ int vnc_guac_client_handle_messages(guac_client* client) {
 
     /* If an error occurs, log it and fail */
     if (wait_result < 0) {
-        guac_client_log_error(client, "Error waiting for VNC server message\n");
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Connection closed.");
         return 1;
     }
 
+    guac_common_surface_flush(guac_client_data->default_surface);
     return 0;
 
 }
 
-
 int vnc_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) {
 
     rfbClient* rfb_client = ((vnc_guac_client_data*) client->data)->rfb_client;
@@ -111,29 +130,6 @@ int vnc_guac_client_key_handler(guac_client* client, int keysym, int pressed) {
     return 0;
 }
 
-int vnc_guac_client_clipboard_handler(guac_client* client, char* data) {
-
-    rfbClient* rfb_client = ((vnc_guac_client_data*) client->data)->rfb_client;
-
-    /* Convert UTF-8 character data to ISO_8859-1 */
-    # if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2) || __GLIBC__ > 2 || _LIBICONV_VERSION >= 0x0105
-    char* iso_8559_1_data = convert("UTF-8", "ISO_8859-1//TRANSLIT", data);
-    #else
-    char* iso_8559_1_data = convert("UTF-8", "ISO_8859-1", data);
-    #endif
-
-    /* If the conversion was successful, send the converted character data. */
-    if(iso_8559_1_data) {
-        SendClientCutText(rfb_client, iso_8559_1_data, strlen(iso_8559_1_data));
-        free(iso_8559_1_data);
-    /* Otherwise, just send an empty string. */
-    } 
-    else
-        SendClientCutText(rfb_client, "", 0);
-
-    return 0;
-}
-
 int vnc_guac_client_free_handler(guac_client* client) {
 
     vnc_guac_client_data* guac_client_data = (vnc_guac_client_data*) client->data;
@@ -145,10 +141,32 @@ int vnc_guac_client_free_handler(guac_client* client) {
         guac_pa_stop_stream(client);
 #endif
 
+#ifdef ENABLE_COMMON_SSH
+    /* Free SFTP filesystem, if loaded */
+    if (guac_client_data->sftp_filesystem)
+        guac_common_ssh_destroy_sftp_filesystem(guac_client_data->sftp_filesystem);
+
+    /* Free SFTP session */
+    if (guac_client_data->sftp_session)
+        guac_common_ssh_destroy_session(guac_client_data->sftp_session);
+
+    /* Free SFTP user */
+    if (guac_client_data->sftp_user)
+        guac_common_ssh_destroy_user(guac_client_data->sftp_user);
+
+    guac_common_ssh_uninit();
+#endif
+
     /* Free encodings string, if used */
     if (guac_client_data->encodings != NULL)
         free(guac_client_data->encodings);
 
+    /* Free clipboard */
+    guac_common_clipboard_free(guac_client_data->clipboard);
+
+    /* Free surface */
+    guac_common_surface_free(guac_client_data->default_surface);
+
     /* Free generic data struct */
     free(client->data);
 
diff --git a/src/protocols/vnc/guac_handlers.h b/src/protocols/vnc/guac_handlers.h
index 766d9d6..6487941 100644
--- a/src/protocols/vnc/guac_handlers.h
+++ b/src/protocols/vnc/guac_handlers.h
@@ -1,49 +1,36 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-vnc.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef __GUAC_VNC_GUAC_HANDLERS_H
 #define __GUAC_VNC_GUAC_HANDLERS_H
 
+#include "config.h"
+
 #include <guacamole/client.h>
 
 int vnc_guac_client_handle_messages(guac_client* client);
 int vnc_guac_client_mouse_handler(guac_client* client, int x, int y, int mask);
 int vnc_guac_client_key_handler(guac_client* client, int keysym, int pressed);
-int vnc_guac_client_clipboard_handler(guac_client* client, char* data);
 int vnc_guac_client_free_handler(guac_client* client);
 
 #endif
diff --git a/src/protocols/vnc/pulse.c b/src/protocols/vnc/pulse.c
index 3480a8e..05418cd 100644
--- a/src/protocols/vnc/pulse.c
+++ b/src/protocols/vnc/pulse.c
@@ -1,47 +1,66 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * The Original Code is libguac-client-vnc.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "client.h"
+#include "pulse.h"
+
+#include <guacamole/audio.h>
+#include <guacamole/client.h>
+#include <guacamole/socket.h>
+#include <pulse/pulseaudio.h>
+
+/**
+ * Returns whether the given buffer contains only silence (only null bytes).
  *
- * Contributor(s):
+ * @param buffer
+ *     The audio buffer to check.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * @param length
+ *     The length of the buffer to check.
  *
- * ***** END LICENSE BLOCK ***** */
+ * @return
+ *     Non-zero if the audio buffer contains silence, zero otherwise.
+ */
+static int guac_pa_is_silence(const void* buffer, size_t length) {
 
-#include <guacamole/audio.h>
-#include <guacamole/client.h>
+    int i;
 
-#include <pulse/pulseaudio.h>
+    const unsigned char* current = (const unsigned char*) buffer;
 
-#include "client.h"
-#include "pulse.h"
+    /* For each byte in buffer */
+    for (i = 0; i < length; i++) {
+
+        /* If current value non-zero, then not silence */
+        if (*(current++))
+            return 0;
+
+    }
+
+    /* Otherwise, the buffer contains 100% silence */
+    return 1;
+
+}
 
 static void __stream_read_callback(pa_stream* stream, size_t length,
         void* data) {
@@ -55,17 +74,13 @@ static void __stream_read_callback(pa_stream* stream, size_t length,
     /* Read data */
     pa_stream_peek(stream, &buffer, &length);
 
-    /* Write data */
-    guac_audio_stream_write_pcm(audio, buffer, length);
+    /* Continuously write received PCM data */
+    if (!guac_pa_is_silence(buffer, length))
+        guac_audio_stream_write_pcm(audio, buffer, length);
 
-    /* Flush occasionally */
-    if (audio->pcm_bytes_written > GUAC_VNC_PCM_WRITE_RATE) {
-        guac_audio_stream_end(audio);
-        guac_audio_stream_begin(client_data->audio,
-                GUAC_VNC_AUDIO_RATE,
-                GUAC_VNC_AUDIO_CHANNELS,
-                GUAC_VNC_AUDIO_BPS);
-    }
+    /* Flush upon silence */
+    else
+        guac_audio_stream_flush(audio);
 
     /* Advance buffer */
     pa_stream_drop(stream);
@@ -79,28 +94,28 @@ static void __stream_state_callback(pa_stream* stream, void* data) {
     switch (pa_stream_get_state(stream)) {
 
         case PA_STREAM_UNCONNECTED:
-            guac_client_log_info(client,
+            guac_client_log(client, GUAC_LOG_INFO,
                     "PulseAudio stream currently unconnected");
             break;
 
         case PA_STREAM_CREATING:
-            guac_client_log_info(client, "PulseAudio stream being created...");
+            guac_client_log(client, GUAC_LOG_INFO, "PulseAudio stream being created...");
             break;
 
         case PA_STREAM_READY:
-            guac_client_log_info(client, "PulseAudio stream now ready");
+            guac_client_log(client, GUAC_LOG_INFO, "PulseAudio stream now ready");
             break;
 
         case PA_STREAM_FAILED:
-            guac_client_log_info(client, "PulseAudio stream connection failed");
+            guac_client_log(client, GUAC_LOG_INFO, "PulseAudio stream connection failed");
             break;
 
         case PA_STREAM_TERMINATED:
-            guac_client_log_info(client, "PulseAudio stream terminated");
+            guac_client_log(client, GUAC_LOG_INFO, "PulseAudio stream terminated");
             break;
 
         default:
-            guac_client_log_info(client,
+            guac_client_log(client, GUAC_LOG_INFO,
                     "Unknown PulseAudio stream state: 0x%x",
                     pa_stream_get_state(stream));
 
@@ -120,7 +135,7 @@ static void __context_get_sink_info_callback(pa_context* context,
     if (is_last)
         return;
 
-    guac_client_log_info(client, "Starting streaming from \"%s\"",
+    guac_client_log(client, GUAC_LOG_INFO, "Starting streaming from \"%s\"",
             info->description);
 
     /* Set format */
@@ -152,11 +167,11 @@ static void __context_get_server_info_callback(pa_context* context,
 
     /* If no default sink, cannot continue */
     if (info->default_sink_name == NULL) {
-        guac_client_log_error(client, "No default sink. Cannot stream audio.");
+        guac_client_log(client, GUAC_LOG_ERROR, "No default sink. Cannot stream audio.");
         return;
     }
 
-    guac_client_log_info(client, "Will use default sink: \"%s\"",
+    guac_client_log(client, GUAC_LOG_INFO, "Will use default sink: \"%s\"",
             info->default_sink_name);
 
     /* Wait for default sink information */
@@ -174,39 +189,39 @@ static void __context_state_callback(pa_context* context, void* data) {
     switch (pa_context_get_state(context)) {
 
         case PA_CONTEXT_UNCONNECTED:
-            guac_client_log_info(client,
+            guac_client_log(client, GUAC_LOG_INFO,
                     "PulseAudio reports it is unconnected");
             break;
 
         case PA_CONTEXT_CONNECTING:
-            guac_client_log_info(client, "Connecting to PulseAudio...");
+            guac_client_log(client, GUAC_LOG_INFO, "Connecting to PulseAudio...");
             break;
 
         case PA_CONTEXT_AUTHORIZING:
-            guac_client_log_info(client,
+            guac_client_log(client, GUAC_LOG_INFO,
                     "Authorizing PulseAudio connection...");
             break;
 
         case PA_CONTEXT_SETTING_NAME:
-            guac_client_log_info(client, "Sending client name...");
+            guac_client_log(client, GUAC_LOG_INFO, "Sending client name...");
             break;
 
         case PA_CONTEXT_READY:
-            guac_client_log_info(client, "PulseAudio now ready");
+            guac_client_log(client, GUAC_LOG_INFO, "PulseAudio now ready");
             pa_operation_unref(pa_context_get_server_info(context,
                         __context_get_server_info_callback, client));
             break;
 
         case PA_CONTEXT_FAILED:
-            guac_client_log_info(client, "PulseAudio connection failed");
+            guac_client_log(client, GUAC_LOG_INFO, "PulseAudio connection failed");
             break;
 
         case PA_CONTEXT_TERMINATED:
-            guac_client_log_info(client, "PulseAudio connection terminated");
+            guac_client_log(client, GUAC_LOG_INFO, "PulseAudio connection terminated");
             break;
 
         default:
-            guac_client_log_info(client,
+            guac_client_log(client, GUAC_LOG_INFO,
                     "Unknown PulseAudio context state: 0x%x",
                     pa_context_get_state(context));
 
@@ -219,11 +234,7 @@ void guac_pa_start_stream(guac_client* client) {
     vnc_guac_client_data* client_data = (vnc_guac_client_data*) client->data;
     pa_context* context;
 
-    guac_client_log_info(client, "Starting audio stream");
-    guac_audio_stream_begin(client_data->audio,
-                GUAC_VNC_AUDIO_RATE,
-                GUAC_VNC_AUDIO_CHANNELS,
-                GUAC_VNC_AUDIO_BPS);
+    guac_client_log(client, GUAC_LOG_INFO, "Starting audio stream");
 
     /* Init main loop */
     client_data->pa_mainloop = pa_threaded_mainloop_new();
@@ -250,7 +261,7 @@ void guac_pa_stop_stream(guac_client* client) {
     /* Stop loop */
     pa_threaded_mainloop_stop(client_data->pa_mainloop);
 
-    guac_client_log_info(client, "Audio stream finished");
+    guac_client_log(client, GUAC_LOG_INFO, "Audio stream finished");
 
 }
 
diff --git a/src/protocols/vnc/pulse.h b/src/protocols/vnc/pulse.h
index 000b778..62a1515 100644
--- a/src/protocols/vnc/pulse.h
+++ b/src/protocols/vnc/pulse.h
@@ -1,43 +1,31 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-vnc.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef __GUAC_VNC_PULSE_H
 #define __GUAC_VNC_PULSE_H
 
+#include "config.h"
+
 /**
  * The number of bytes to request for the audio fragments received from
  * PulseAudio.
diff --git a/src/protocols/vnc/sftp.c b/src/protocols/vnc/sftp.c
new file mode 100644
index 0000000..5986633
--- /dev/null
+++ b/src/protocols/vnc/sftp.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "client.h"
+#include "guac_sftp.h"
+#include "sftp.h"
+
+#include <guacamole/client.h>
+#include <guacamole/stream.h>
+
+int guac_vnc_sftp_file_handler(guac_client* client, guac_stream* stream,
+        char* mimetype, char* filename) {
+
+    vnc_guac_client_data* client_data = (vnc_guac_client_data*) client->data;
+    guac_object* filesystem = client_data->sftp_filesystem;
+
+    /* Handle file upload */
+    return guac_common_ssh_sftp_handle_file_stream(filesystem, stream,
+            mimetype, filename);
+
+}
+
diff --git a/src/protocols/vnc/sftp.h b/src/protocols/vnc/sftp.h
new file mode 100644
index 0000000..2477f33
--- /dev/null
+++ b/src/protocols/vnc/sftp.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GUAC_VNC_SFTP_H
+#define GUAC_VNC_SFTP_H
+
+#include "config.h"
+
+#include <guacamole/client.h>
+#include <guacamole/stream.h>
+
+/**
+ * Handles an incoming stream from a Guacamole "file" instruction, saving the
+ * contents of that stream to the file having the given name.
+ *
+ * @param client
+ *     The client receiving the uploaded file.
+ *
+ * @param stream
+ *     The stream through which the uploaded file data will be received.
+ *
+ * @param mimetype
+ *     The mimetype of the data being received.
+ *
+ * @param filename
+ *     The filename of the file to write to.
+ *
+ * @return
+ *     Zero if the incoming stream has been handled successfully, non-zero on
+ *     failure.
+ */
+int guac_vnc_sftp_file_handler(guac_client* client, guac_stream* stream,
+        char* mimetype, char* filename);
+
+#endif
+
diff --git a/src/protocols/vnc/vnc_handlers.c b/src/protocols/vnc/vnc_handlers.c
index d4e217e..7e7ccee 100644
--- a/src/protocols/vnc/vnc_handlers.c
+++ b/src/protocols/vnc/vnc_handlers.c
@@ -1,61 +1,49 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-vnc.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <stdlib.h>
-#include <time.h>
-#include <syslog.h>
-#include <iconv.h>
-
-#include <cairo/cairo.h>
+#include "config.h"
 
-#include <rfb/rfbclient.h>
+#include "client.h"
+#include "guac_iconv.h"
+#include "guac_surface.h"
 
-#include <guacamole/socket.h>
-#include <guacamole/protocol.h>
+#include <cairo/cairo.h>
 #include <guacamole/client.h>
-
-#include "client.h"
-#include "convert.h"
+#include <guacamole/layer.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+#include <rfb/rfbclient.h>
+#include <rfb/rfbproto.h>
 
 /* Define cairo_format_stride_for_width() if missing */
 #ifndef HAVE_CAIRO_FORMAT_STRIDE_FOR_WIDTH
 #define cairo_format_stride_for_width(format, width) (width*4)
 #endif
 
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+
 void guac_vnc_cursor(rfbClient* client, int x, int y, int w, int h, int bpp) {
 
     guac_client* gc = rfbClientGetClientData(client, __GUAC_CLIENT);
@@ -133,8 +121,8 @@ void guac_vnc_cursor(rfbClient* client, int x, int y, int w, int h, int bpp) {
     /* Send cursor data*/
     surface = cairo_image_surface_create_for_data(buffer, CAIRO_FORMAT_ARGB32, w, h, stride);
     
-    guac_protocol_send_png(socket,
-            GUAC_COMP_SRC, cursor_layer, 0, 0, surface);
+    guac_client_stream_png(gc, socket, GUAC_COMP_SRC, cursor_layer,
+            0, 0, surface);
     
     /* Update cursor */
     guac_protocol_send_cursor(socket, x, y, cursor_layer, 0, 0, w, h);
@@ -147,12 +135,10 @@ void guac_vnc_cursor(rfbClient* client, int x, int y, int w, int h, int bpp) {
     free(client->rcMask);
 }
 
-
 void guac_vnc_update(rfbClient* client, int x, int y, int w, int h) {
 
     guac_client* gc = rfbClientGetClientData(client, __GUAC_CLIENT);
     vnc_guac_client_data* guac_client_data = (vnc_guac_client_data*) gc->data;
-    guac_socket* socket = gc->socket;
 
     int dx, dy;
 
@@ -233,7 +219,7 @@ void guac_vnc_update(rfbClient* client, int x, int y, int w, int h) {
     /* For now, only use default layer */
     surface = cairo_image_surface_create_for_data(buffer, CAIRO_FORMAT_RGB24, w, h, stride);
 
-    guac_protocol_send_png(socket, GUAC_COMP_OVER, GUAC_DEFAULT_LAYER, x, y, surface);
+    guac_common_surface_draw(guac_client_data->default_surface, x, y, surface);
 
     /* Free surface */
     cairo_surface_destroy(surface);
@@ -244,12 +230,11 @@ void guac_vnc_update(rfbClient* client, int x, int y, int w, int h) {
 void guac_vnc_copyrect(rfbClient* client, int src_x, int src_y, int w, int h, int dest_x, int dest_y) {
 
     guac_client* gc = rfbClientGetClientData(client, __GUAC_CLIENT);
-    guac_socket* socket = gc->socket;
+    vnc_guac_client_data* guac_client_data = (vnc_guac_client_data*) gc->data;
 
     /* For now, only use default layer */
-    guac_protocol_send_copy(socket,
-                            GUAC_DEFAULT_LAYER, src_x,  src_y, w, h,
-            GUAC_COMP_OVER, GUAC_DEFAULT_LAYER, dest_x, dest_y);
+    guac_common_surface_copy(guac_client_data->default_surface, src_x,  src_y, w, h,
+                             guac_client_data->default_surface, dest_x, dest_y);
 
     ((vnc_guac_client_data*) gc->data)->copy_rect_used = 1;
 
@@ -303,9 +288,9 @@ rfbBool guac_vnc_malloc_framebuffer(rfbClient* rfb_client) {
     guac_client* gc = rfbClientGetClientData(rfb_client, __GUAC_CLIENT);
     vnc_guac_client_data* guac_client_data = (vnc_guac_client_data*) gc->data;
 
-    /* Send new size */
-    guac_protocol_send_size(gc->socket,
-            GUAC_DEFAULT_LAYER, rfb_client->width, rfb_client->height);
+    /* Resize surface */
+    if (guac_client_data->default_surface != NULL)
+        guac_common_surface_resize(guac_client_data->default_surface, rfb_client->width, rfb_client->height);
 
     /* Use original, wrapped proc */
     return guac_client_data->rfb_MallocFrameBuffer(rfb_client);
@@ -314,36 +299,52 @@ rfbBool guac_vnc_malloc_framebuffer(rfbClient* rfb_client) {
 void guac_vnc_cut_text(rfbClient* client, const char* text, int textlen) {
 
     guac_client* gc = rfbClientGetClientData(client, __GUAC_CLIENT);
-    guac_socket* socket = gc->socket;
+    vnc_guac_client_data* client_data = (vnc_guac_client_data*) gc->data;
 
-    /* Convert ASCII character data to UTF-8 */
-    char* utf8_text = convert("ISO_8859-1", "UTF-8", text);
+    char received_data[GUAC_VNC_CLIPBOARD_MAX_LENGTH];
 
-    guac_protocol_send_clipboard(socket, utf8_text);
+    const char* input = text;
+    char* output = received_data;
+    guac_iconv_read* reader = client_data->clipboard_reader;
 
-    free(utf8_text);
+    /* Convert clipboard contents */
+    guac_iconv(reader, &input, textlen,
+               GUAC_WRITE_UTF8, &output, sizeof(received_data));
+
+    /* Send converted data */
+    guac_common_clipboard_reset(client_data->clipboard, "text/plain");
+    guac_common_clipboard_append(client_data->clipboard, received_data, output - received_data);
+    guac_common_clipboard_send(client_data->clipboard, gc);
 
 }
 
 void guac_vnc_client_log_info(const char* format, ...) {
 
+    char message[2048];
+
+    /* Copy log message into buffer */
     va_list args;
     va_start(args, format);
-
-    vsyslog(LOG_INFO, format, args);
-
+    vsnprintf(message, sizeof(message), format, args);
     va_end(args);
 
+    /* Log to syslog */
+    syslog(LOG_INFO, "%s", message);
+
 }
 
 void guac_vnc_client_log_error(const char* format, ...) {
 
+    char message[2048];
+
+    /* Copy log message into buffer */
     va_list args;
     va_start(args, format);
-
-    vsyslog(LOG_ERR, format, args);
-
+    vsnprintf(message, sizeof(message), format, args);
     va_end(args);
 
+    /* Log to syslog */
+    syslog(LOG_ERR, "%s", message);
+
 }
 
diff --git a/src/protocols/vnc/vnc_handlers.h b/src/protocols/vnc/vnc_handlers.h
index 92730db..7e6870d 100644
--- a/src/protocols/vnc/vnc_handlers.h
+++ b/src/protocols/vnc/vnc_handlers.h
@@ -1,43 +1,31 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-vnc.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef __GUAC_VNC_VNC_HANDLERS_H
 #define __GUAC_VNC_VNC_HANDLERS_H
 
+#include "config.h"
+
 #include <rfb/rfbclient.h>
 
 void guac_vnc_cursor(rfbClient* client, int x, int y, int w, int h, int bpp);
diff --git a/src/terminal/Makefile.am b/src/terminal/Makefile.am
new file mode 100644
index 0000000..40aca24
--- /dev/null
+++ b/src/terminal/Makefile.am
@@ -0,0 +1,73 @@
+#
+# Copyright (C) 2015 Glyptodon LLC
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+AUTOMAKE_OPTIONS = foreign 
+ACLOCAL_AMFLAGS = -I m4
+
+noinst_LTLIBRARIES = libguac_terminal.la
+
+noinst_HEADERS =                \
+    blank.h                     \
+    buffer.h                    \
+    char_mappings.h             \
+    common.h                    \
+    cursor.h                    \
+    display.h                   \
+    ibar.h                      \
+    packet.h                    \
+    pointer.h                   \
+    scrollbar.h                 \
+    terminal.h                  \
+    terminal_handlers.h         \
+    types.h
+
+libguac_terminal_la_SOURCES =   \
+    blank.c                     \
+    buffer.c                    \
+    char_mappings.c             \
+    common.c                    \
+    cursor.c                    \
+    display.c                   \
+    ibar.c                      \
+    packet.c                    \
+    pointer.c                   \
+    scrollbar.c                 \
+    terminal.c                  \
+    terminal_handlers.c
+
+libguac_terminal_la_CFLAGS = \
+    -Werror -Wall -pedantic  \
+    @COMMON_INCLUDE@         \
+    @LIBGUAC_INCLUDE@        \
+    @PANGO_CFLAGS@           \
+    @PANGOCAIRO_CFLAGS@
+
+libguac_terminal_la_LIBADD = \
+    @LIBGUAC_LTLIB@
+
+libguac_terminal_la_LDFLAGS = \
+    @CAIRO_LIBS@              \
+    @MATH_LIBS@               \
+    @PANGO_LIBS@              \
+    @PANGOCAIRO_LIBS@         \
+    @PTHREAD_LIBS@
+
diff --git a/src/terminal/Makefile.in b/src/terminal/Makefile.in
new file mode 100644
index 0000000..929fbe3
--- /dev/null
+++ b/src/terminal/Makefile.in
@@ -0,0 +1,783 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+ at SET_MAKE@
+
+#
+# Copyright (C) 2015 Glyptodon LLC
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/terminal
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(top_srcdir)/depcomp $(noinst_HEADERS)
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
+	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+	$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libguac_terminal_la_DEPENDENCIES =
+am_libguac_terminal_la_OBJECTS = libguac_terminal_la-blank.lo \
+	libguac_terminal_la-buffer.lo \
+	libguac_terminal_la-char_mappings.lo \
+	libguac_terminal_la-common.lo libguac_terminal_la-cursor.lo \
+	libguac_terminal_la-display.lo libguac_terminal_la-ibar.lo \
+	libguac_terminal_la-packet.lo libguac_terminal_la-pointer.lo \
+	libguac_terminal_la-scrollbar.lo \
+	libguac_terminal_la-terminal.lo \
+	libguac_terminal_la-terminal_handlers.lo
+libguac_terminal_la_OBJECTS = $(am_libguac_terminal_la_OBJECTS)
+AM_V_lt = $(am__v_lt_ at AM_V@)
+am__v_lt_ = $(am__v_lt_ at AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+libguac_terminal_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(libguac_terminal_la_CFLAGS) $(CFLAGS) \
+	$(libguac_terminal_la_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_ at AM_V@)
+am__v_P_ = $(am__v_P_ at AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_ at AM_V@)
+am__v_GEN_ = $(am__v_GEN_ at AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_ at AM_V@)
+am__v_at_ = $(am__v_at_ at AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I. at am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_ at AM_V@)
+am__v_CC_ = $(am__v_CC_ at AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_ at AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_ at AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
+SOURCES = $(libguac_terminal_la_SOURCES)
+DIST_SOURCES = $(libguac_terminal_la_SOURCES)
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CAIRO_LIBS = @CAIRO_LIBS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+COMMON_INCLUDE = @COMMON_INCLUDE@
+COMMON_LTLIB = @COMMON_LTLIB@
+COMMON_SSH_INCLUDE = @COMMON_SSH_INCLUDE@
+COMMON_SSH_LTLIB = @COMMON_SSH_LTLIB@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CUNIT_LIBS = @CUNIT_LIBS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+JPEG_LIBS = @JPEG_LIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBADD_DLOPEN = @LIBADD_DLOPEN@
+LIBGUAC_INCLUDE = @LIBGUAC_INCLUDE@
+LIBGUAC_LTLIB = @LIBGUAC_LTLIB@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MATH_LIBS = @MATH_LIBS@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGO_CFLAGS = @PANGO_CFLAGS@
+PANGO_LIBS = @PANGO_LIBS@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_LIBS = @PNG_LIBS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+PULSE_LIBS = @PULSE_LIBS@
+RANLIB = @RANLIB@
+RDP_LIBS = @RDP_LIBS@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SSH_LIBS = @SSH_LIBS@
+SSL_LIBS = @SSL_LIBS@
+STRIP = @STRIP@
+TELNET_LIBS = @TELNET_LIBS@
+TERMINAL_INCLUDE = @TERMINAL_INCLUDE@
+TERMINAL_LTLIB = @TERMINAL_LTLIB@
+UUID_LIBS = @UUID_LIBS@
+VERSION = @VERSION@
+VNC_LIBS = @VNC_LIBS@
+VORBIS_LIBS = @VORBIS_LIBS@
+WEBP_LIBS = @WEBP_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+init_dir = @init_dir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AUTOMAKE_OPTIONS = foreign 
+ACLOCAL_AMFLAGS = -I m4
+noinst_LTLIBRARIES = libguac_terminal.la
+noinst_HEADERS = \
+    blank.h                     \
+    buffer.h                    \
+    char_mappings.h             \
+    common.h                    \
+    cursor.h                    \
+    display.h                   \
+    ibar.h                      \
+    packet.h                    \
+    pointer.h                   \
+    scrollbar.h                 \
+    terminal.h                  \
+    terminal_handlers.h         \
+    types.h
+
+libguac_terminal_la_SOURCES = \
+    blank.c                     \
+    buffer.c                    \
+    char_mappings.c             \
+    common.c                    \
+    cursor.c                    \
+    display.c                   \
+    ibar.c                      \
+    packet.c                    \
+    pointer.c                   \
+    scrollbar.c                 \
+    terminal.c                  \
+    terminal_handlers.c
+
+libguac_terminal_la_CFLAGS = \
+    -Werror -Wall -pedantic  \
+    @COMMON_INCLUDE@         \
+    @LIBGUAC_INCLUDE@        \
+    @PANGO_CFLAGS@           \
+    @PANGOCAIRO_CFLAGS@
+
+libguac_terminal_la_LIBADD = \
+    @LIBGUAC_LTLIB@
+
+libguac_terminal_la_LDFLAGS = \
+    @CAIRO_LIBS@              \
+    @MATH_LIBS@               \
+    @PANGO_LIBS@              \
+    @PANGOCAIRO_LIBS@         \
+    @PTHREAD_LIBS@
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/terminal/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --foreign src/terminal/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure:  $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+	-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+	@list='$(noinst_LTLIBRARIES)'; \
+	locs=`for p in $$list; do echo $$p; done | \
+	      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+	      sort -u`; \
+	test -z "$$locs" || { \
+	  echo rm -f $${locs}; \
+	  rm -f $${locs}; \
+	}
+
+libguac_terminal.la: $(libguac_terminal_la_OBJECTS) $(libguac_terminal_la_DEPENDENCIES) $(EXTRA_libguac_terminal_la_DEPENDENCIES) 
+	$(AM_V_CCLD)$(libguac_terminal_la_LINK)  $(libguac_terminal_la_OBJECTS) $(libguac_terminal_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_terminal_la-blank.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_terminal_la-buffer.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_terminal_la-char_mappings.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_terminal_la-common.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_terminal_la-cursor.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_terminal_la-display.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_terminal_la-ibar.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_terminal_la-packet.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_terminal_la-pointer.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_terminal_la-scrollbar.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_terminal_la-terminal.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libguac_terminal_la-terminal_handlers.Plo at am__quote@
+
+.c.o:
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+ at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+ at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+ at am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+libguac_terminal_la-blank.lo: blank.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_terminal_la_CFLAGS) $(CFLAGS) -MT libguac_terminal_la-blank.lo -MD -MP -MF $(DEPDIR)/libguac_terminal_la-blank.Tpo -c -o libguac_terminal_la-blank.lo `test -f 'blank.c' || echo '$(srcdir)/'`blank.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_terminal_la-blank.Tpo $(DEPDIR)/libguac_terminal_la-blank.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='blank.c' object='libguac_terminal_la-blank.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_terminal_la_CFLAGS) $(CFLAGS) -c -o libguac_terminal_la-blank.lo `test -f 'blank.c' || echo '$(srcdir)/'`blank.c
+
+libguac_terminal_la-buffer.lo: buffer.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_terminal_la_CFLAGS) $(CFLAGS) -MT libguac_terminal_la-buffer.lo -MD -MP -MF $(DEPDIR)/libguac_terminal_la-buffer.Tpo -c -o libguac_terminal_la-buffer.lo `test -f 'buffer.c' || echo '$(srcdir)/'`buffer.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_terminal_la-buffer.Tpo $(DEPDIR)/libguac_terminal_la-buffer.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='buffer.c' object='libguac_terminal_la-buffer.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_terminal_la_CFLAGS) $(CFLAGS) -c -o libguac_terminal_la-buffer.lo `test -f 'buffer.c' || echo '$(srcdir)/'`buffer.c
+
+libguac_terminal_la-char_mappings.lo: char_mappings.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_terminal_la_CFLAGS) $(CFLAGS) -MT libguac_terminal_la-char_mappings.lo -MD -MP -MF $(DEPDIR)/libguac_terminal_la-char_mappings.Tpo -c -o libguac_terminal_la-char_mappings.lo `test -f 'char_mappings.c' || echo '$(srcdir)/'`char_mappings.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_terminal_la-char_mappings.Tpo $(DEPDIR)/libguac_terminal_la-char_mappings.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='char_mappings.c' object='libguac_terminal_la-char_mappings.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_terminal_la_CFLAGS) $(CFLAGS) -c -o libguac_terminal_la-char_mappings.lo `test -f 'char_mappings.c' || echo '$(srcdir)/'`char_mappings.c
+
+libguac_terminal_la-common.lo: common.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_terminal_la_CFLAGS) $(CFLAGS) -MT libguac_terminal_la-common.lo -MD -MP -MF $(DEPDIR)/libguac_terminal_la-common.Tpo -c -o libguac_terminal_la-common.lo `test -f 'common.c' || echo '$(srcdir)/'`common.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_terminal_la-common.Tpo $(DEPDIR)/libguac_terminal_la-common.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='common.c' object='libguac_terminal_la-common.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_terminal_la_CFLAGS) $(CFLAGS) -c -o libguac_terminal_la-common.lo `test -f 'common.c' || echo '$(srcdir)/'`common.c
+
+libguac_terminal_la-cursor.lo: cursor.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_terminal_la_CFLAGS) $(CFLAGS) -MT libguac_terminal_la-cursor.lo -MD -MP -MF $(DEPDIR)/libguac_terminal_la-cursor.Tpo -c -o libguac_terminal_la-cursor.lo `test -f 'cursor.c' || echo '$(srcdir)/'`cursor.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_terminal_la-cursor.Tpo $(DEPDIR)/libguac_terminal_la-cursor.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='cursor.c' object='libguac_terminal_la-cursor.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_terminal_la_CFLAGS) $(CFLAGS) -c -o libguac_terminal_la-cursor.lo `test -f 'cursor.c' || echo '$(srcdir)/'`cursor.c
+
+libguac_terminal_la-display.lo: display.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_terminal_la_CFLAGS) $(CFLAGS) -MT libguac_terminal_la-display.lo -MD -MP -MF $(DEPDIR)/libguac_terminal_la-display.Tpo -c -o libguac_terminal_la-display.lo `test -f 'display.c' || echo '$(srcdir)/'`display.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_terminal_la-display.Tpo $(DEPDIR)/libguac_terminal_la-display.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='display.c' object='libguac_terminal_la-display.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_terminal_la_CFLAGS) $(CFLAGS) -c -o libguac_terminal_la-display.lo `test -f 'display.c' || echo '$(srcdir)/'`display.c
+
+libguac_terminal_la-ibar.lo: ibar.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_terminal_la_CFLAGS) $(CFLAGS) -MT libguac_terminal_la-ibar.lo -MD -MP -MF $(DEPDIR)/libguac_terminal_la-ibar.Tpo -c -o libguac_terminal_la-ibar.lo `test -f 'ibar.c' || echo '$(srcdir)/'`ibar.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_terminal_la-ibar.Tpo $(DEPDIR)/libguac_terminal_la-ibar.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='ibar.c' object='libguac_terminal_la-ibar.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_terminal_la_CFLAGS) $(CFLAGS) -c -o libguac_terminal_la-ibar.lo `test -f 'ibar.c' || echo '$(srcdir)/'`ibar.c
+
+libguac_terminal_la-packet.lo: packet.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_terminal_la_CFLAGS) $(CFLAGS) -MT libguac_terminal_la-packet.lo -MD -MP -MF $(DEPDIR)/libguac_terminal_la-packet.Tpo -c -o libguac_terminal_la-packet.lo `test -f 'packet.c' || echo '$(srcdir)/'`packet.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_terminal_la-packet.Tpo $(DEPDIR)/libguac_terminal_la-packet.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='packet.c' object='libguac_terminal_la-packet.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_terminal_la_CFLAGS) $(CFLAGS) -c -o libguac_terminal_la-packet.lo `test -f 'packet.c' || echo '$(srcdir)/'`packet.c
+
+libguac_terminal_la-pointer.lo: pointer.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_terminal_la_CFLAGS) $(CFLAGS) -MT libguac_terminal_la-pointer.lo -MD -MP -MF $(DEPDIR)/libguac_terminal_la-pointer.Tpo -c -o libguac_terminal_la-pointer.lo `test -f 'pointer.c' || echo '$(srcdir)/'`pointer.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_terminal_la-pointer.Tpo $(DEPDIR)/libguac_terminal_la-pointer.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='pointer.c' object='libguac_terminal_la-pointer.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_terminal_la_CFLAGS) $(CFLAGS) -c -o libguac_terminal_la-pointer.lo `test -f 'pointer.c' || echo '$(srcdir)/'`pointer.c
+
+libguac_terminal_la-scrollbar.lo: scrollbar.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_terminal_la_CFLAGS) $(CFLAGS) -MT libguac_terminal_la-scrollbar.lo -MD -MP -MF $(DEPDIR)/libguac_terminal_la-scrollbar.Tpo -c -o libguac_terminal_la-scrollbar.lo `test -f 'scrollbar.c' || echo '$(srcdir)/'`scrollbar.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_terminal_la-scrollbar.Tpo $(DEPDIR)/libguac_terminal_la-scrollbar.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='scrollbar.c' object='libguac_terminal_la-scrollbar.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_terminal_la_CFLAGS) $(CFLAGS) -c -o libguac_terminal_la-scrollbar.lo `test -f 'scrollbar.c' || echo '$(srcdir)/'`scrollbar.c
+
+libguac_terminal_la-terminal.lo: terminal.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_terminal_la_CFLAGS) $(CFLAGS) -MT libguac_terminal_la-terminal.lo -MD -MP -MF $(DEPDIR)/libguac_terminal_la-terminal.Tpo -c -o libguac_terminal_la-terminal.lo `test -f 'terminal.c' || echo '$(srcdir)/'`terminal.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_terminal_la-terminal.Tpo $(DEPDIR)/libguac_terminal_la-terminal.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='terminal.c' object='libguac_terminal_la-terminal.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_terminal_la_CFLAGS) $(CFLAGS) -c -o libguac_terminal_la-terminal.lo `test -f 'terminal.c' || echo '$(srcdir)/'`terminal.c
+
+libguac_terminal_la-terminal_handlers.lo: terminal_handlers.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_terminal_la_CFLAGS) $(CFLAGS) -MT libguac_terminal_la-terminal_handlers.lo -MD -MP -MF $(DEPDIR)/libguac_terminal_la-terminal_handlers.Tpo -c -o libguac_terminal_la-terminal_handlers.lo `test -f 'terminal_handlers.c' || echo '$(srcdir)/'`terminal_handlers.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libguac_terminal_la-terminal_handlers.Tpo $(DEPDIR)/libguac_terminal_la-terminal_handlers.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='terminal_handlers.c' object='libguac_terminal_la-terminal_handlers.lo' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libguac_terminal_la_CFLAGS) $(CFLAGS) -c -o libguac_terminal_la-terminal_handlers.lo `test -f 'terminal_handlers.c' || echo '$(srcdir)/'`terminal_handlers.c
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+	mostlyclean-am
+
+distclean: distclean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
+	clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \
+	ctags-am distclean distclean-compile distclean-generic \
+	distclean-libtool distclean-tags distdir dvi dvi-am html \
+	html-am info info-am install install-am install-data \
+	install-data-am install-dvi install-dvi-am install-exec \
+	install-exec-am install-html install-html-am install-info \
+	install-info-am install-man install-pdf install-pdf-am \
+	install-ps install-ps-am install-strip installcheck \
+	installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+	tags tags-am uninstall uninstall-am
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/terminal/blank.c b/src/terminal/blank.c
new file mode 100644
index 0000000..f216527
--- /dev/null
+++ b/src/terminal/blank.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "cursor.h"
+
+#include <cairo/cairo.h>
+#include <guacamole/client.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+
+guac_terminal_cursor* guac_terminal_create_blank(guac_client* client) {
+
+    guac_socket* socket = client->socket;
+    guac_terminal_cursor* cursor = guac_terminal_cursor_alloc(client);
+
+    /* Set buffer to a single 1x1 transparent rectangle */
+    guac_protocol_send_rect(socket, cursor->buffer, 0, 0, 1, 1);
+    guac_protocol_send_cfill(socket, GUAC_COMP_SRC, cursor->buffer,
+            0x00, 0x00, 0x00, 0x00);
+
+    /* Initialize cursor properties */
+    cursor->width  = 1;
+    cursor->height = 1;
+    cursor->hotspot_x = 0;
+    cursor->hotspot_y = 0;
+
+    return cursor;
+
+}
+
diff --git a/src/terminal/blank.h b/src/terminal/blank.h
new file mode 100644
index 0000000..0d22797
--- /dev/null
+++ b/src/terminal/blank.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef _GUAC_TERMINAL_BLANK_H
+#define _GUAC_TERMINAL_BLANK_H
+
+#include "config.h"
+
+#include "cursor.h"
+
+#include <cairo/cairo.h>
+#include <guacamole/client.h>
+
+/**
+ * Creates a new blank cursor, returning the corresponding cursor object.
+ *
+ * @param client The guac_client to send the cursor to.
+ * @return A new cursor which must be free'd via guac_terminal_cursor_free()/
+ */
+guac_terminal_cursor* guac_terminal_create_blank(guac_client* client);
+
+#endif
diff --git a/src/protocols/ssh/buffer.c b/src/terminal/buffer.c
similarity index 73%
rename from src/protocols/ssh/buffer.c
rename to src/terminal/buffer.c
index 15fb698..f728b61 100644
--- a/src/protocols/ssh/buffer.c
+++ b/src/terminal/buffer.c
@@ -1,46 +1,33 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-ssh.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <stdlib.h>
-#include <string.h>
+#include "config.h"
 
 #include "buffer.h"
 #include "common.h"
 
+#include <stdlib.h>
+#include <string.h>
+
 guac_terminal_buffer* guac_terminal_buffer_alloc(int rows, guac_terminal_char* default_character) {
 
     /* Allocate scrollback */
@@ -195,19 +182,32 @@ void guac_terminal_buffer_copy_rows(guac_terminal_buffer* buffer,
 void guac_terminal_buffer_set_columns(guac_terminal_buffer* buffer, int row,
         int start_column, int end_column, guac_terminal_char* character) {
 
-    int i;
+    int i, j;
     guac_terminal_char* current;
 
+    /* Build continuation char (for multicolumn characters) */
+    guac_terminal_char continuation_char;
+    continuation_char.value = GUAC_CHAR_CONTINUATION;
+    continuation_char.attributes = character->attributes;
+    continuation_char.width = 0; /* Not applicable for GUAC_CHAR_CONTINUATION */
+
     /* Get and expand row */
     guac_terminal_buffer_row* buffer_row = guac_terminal_buffer_get_row(buffer, row, end_column+1);
 
     /* Set values */
     current = &(buffer_row->characters[start_column]);
-    for (i=start_column; i<=end_column; i++)
+    for (i = start_column; i <= end_column; i += character->width) {
+
         *(current++) = *character;
 
+        /* Store any required continuation characters */
+        for (j=1; j < character->width; j++)
+            *(current++) = continuation_char;
+
+    }
+
     /* Update length depending on row written */
-    if (row >= buffer->length) 
+    if (character->value != 0 && row >= buffer->length) 
         buffer->length = row+1;
 
 }
diff --git a/src/protocols/ssh/buffer.h b/src/terminal/buffer.h
similarity index 62%
rename from src/protocols/ssh/buffer.h
rename to src/terminal/buffer.h
index eb9edb4..1ae7a4d 100644
--- a/src/protocols/ssh/buffer.h
+++ b/src/terminal/buffer.h
@@ -1,42 +1,30 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-ssh.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef _GUAC_TERMINAL_BUFFER_H
+#define _GUAC_TERMINAL_BUFFER_H
 
-#ifndef _SSH_GUAC_BUFFER_H
-#define _SSH_GUAC_BUFFER_H
+#include "config.h"
 
 #include "types.h"
 
diff --git a/src/protocols/ssh/char_mappings.c b/src/terminal/char_mappings.c
similarity index 77%
rename from src/protocols/ssh/char_mappings.c
rename to src/terminal/char_mappings.c
index 483091b..18f709a 100644
--- a/src/protocols/ssh/char_mappings.c
+++ b/src/terminal/char_mappings.c
@@ -1,39 +1,26 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Original Code is libguac-client-ssh.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
 
 const int vt100_map[] = {
     ' ',    '!',    '"',    '#',    '$',    '%',    '&',    '\'',   '(',    ')',    '*',    '+',    ',',    '-',    '.',    '/',
diff --git a/src/terminal/char_mappings.h b/src/terminal/char_mappings.h
new file mode 100644
index 0000000..f59ae71
--- /dev/null
+++ b/src/terminal/char_mappings.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef _GUAC_TERMINAL_CHAR_MAPPINGS_H
+#define _GUAC_TERMINAL_CHAR_MAPPINGS_H
+
+#include "config.h"
+
+/**
+ * VT100 graphics mapping. Each entry is the corresponding Unicode codepoint
+ * for the character N+32, where N is the index of the element in the array.
+ * All characters less than 32 are universally mapped to themselves.
+ */
+extern const int vt100_map[];
+
+/**
+ * Null graphics mapping. Each entry is the corresponding Unicode codepoint
+ * for the character N+32, where N is the index of the element in the array.
+ * All characters less than 32 are universally mapped to themselves.
+ */
+extern const int null_map[];
+
+/**
+ * User graphics mapping. Each entry is the corresponding Unicode codepoint
+ * for the character N+32, where N is the index of the element in the array.
+ * All characters less than 32 are universally mapped to themselves.
+ */
+extern const int user_map[];
+
+#endif
+
diff --git a/src/terminal/common.c b/src/terminal/common.c
new file mode 100644
index 0000000..cf69c5e
--- /dev/null
+++ b/src/terminal/common.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+#include "types.h"
+
+#include <stdbool.h>
+#include <unistd.h>
+
+int guac_terminal_fit_to_range(int value, int min, int max) {
+
+    if (value < min) return min;
+    if (value > max) return max;
+
+    return value;
+
+}
+
+int guac_terminal_encode_utf8(int codepoint, char* utf8) {
+
+    int i;
+    int mask, bytes;
+
+    /* 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 {
+        *(utf8++) = '?';
+        return 1;
+    }
+
+    /* Offset buffer by size */
+    utf8 += bytes - 1;
+
+    /* Add trailing bytes, if any */
+    for (i=1; i<bytes; i++) {
+        *(utf8--) = 0x80 | (codepoint & 0x3F);
+        codepoint >>= 6;
+    }
+
+    /* Set initial byte */
+    *utf8 = mask | codepoint;
+
+    /* Done */
+    return bytes;
+
+}
+
+bool guac_terminal_has_glyph(int codepoint) {
+    return
+           codepoint != 0
+        && codepoint != ' '
+        && codepoint != GUAC_CHAR_CONTINUATION;
+}
+
+int guac_terminal_write_all(int fd, const char* buffer, int size) {
+
+    int remaining = size;
+    while (remaining > 0) {
+
+        /* Attempt to write data */
+        int ret_val = write(fd, buffer, remaining);
+        if (ret_val <= 0)
+            return -1;
+
+        /* If successful, contine with what data remains (if any) */
+        remaining -= ret_val;
+        buffer += ret_val;
+
+    }
+
+    return size;
+
+}
+
+int guac_terminal_fill_buffer(int fd, char* buffer, int size) {
+
+    int remaining = size;
+    while (remaining > 0) {
+
+        /* Attempt to read data */
+        int ret_val = read(fd, buffer, remaining);
+        if (ret_val <= 0)
+            return -1;
+
+        /* If successful, continue with what space remains (if any) */
+        remaining -= ret_val;
+        buffer += ret_val;
+
+    }
+
+    return size;
+
+}
+
diff --git a/src/terminal/common.h b/src/terminal/common.h
new file mode 100644
index 0000000..7c9ced7
--- /dev/null
+++ b/src/terminal/common.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef _GUAC_TERMINAL_COMMON_H
+#define _GUAC_TERMINAL_COMMON_H
+
+#include "config.h"
+
+#include <stdbool.h>
+
+/**
+ * Returns the closest value to the value given that is also
+ * within the given range.
+ */
+int guac_terminal_fit_to_range(int value, int min, int max);
+
+/**
+ * Encodes the given codepoint as UTF-8, storing the result within the
+ * provided buffer, and returning the number of bytes stored.
+ */
+int guac_terminal_encode_utf8(int codepoint, char* utf8);
+
+/**
+ * Returns whether a codepoint has a corresponding glyph, or is rendered
+ * as a blank space.
+ */
+bool guac_terminal_has_glyph(int codepoint);
+
+/**
+ * Similar to write, but automatically retries the write operation until
+ * an error occurs.
+ */
+int guac_terminal_write_all(int fd, const char* buffer, int size);
+
+/**
+ * Similar to read, but automatically retries the read until an error occurs,
+ * filling all available space within the buffer. Unless it is known that the
+ * given amount of space is available on the file descriptor, there is a good
+ * chance this function will block.
+ *
+ * @param fd
+ *     The file descriptor to read data from.
+ *
+ * @param buffer
+ *     The buffer to store data within.
+ *
+ * @param size
+ *     The number of bytes available within the buffer.
+ *
+ * @return
+ *     The number of bytes read if successful, or a negative value if an error
+ *     occurs.
+ */
+int guac_terminal_fill_buffer(int fd, char* buffer, int size);
+
+#endif
+
diff --git a/src/terminal/cursor.c b/src/terminal/cursor.c
new file mode 100644
index 0000000..9bd0026
--- /dev/null
+++ b/src/terminal/cursor.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "cursor.h"
+
+#include <stdlib.h>
+
+#include <guacamole/client.h>
+#include <guacamole/protocol.h>
+
+guac_terminal_cursor* guac_terminal_cursor_alloc(guac_client* client) {
+
+    /* Alloc new cursor, initialize buffer */
+    guac_terminal_cursor* cursor = malloc(sizeof(guac_terminal_cursor));
+    cursor->buffer = guac_client_alloc_buffer(client);
+
+    return cursor;
+
+}
+
+void guac_terminal_cursor_free(guac_client* client, guac_terminal_cursor* cursor) {
+
+    /* Free buffer */
+    guac_client_free_buffer(client, cursor->buffer);
+
+    /* Free cursor */
+    free(cursor);
+
+}
+
+void guac_terminal_set_cursor(guac_client* client, guac_terminal_cursor* cursor) {
+
+    /* Set cursor */
+    guac_protocol_send_cursor(client->socket,
+            cursor->hotspot_x, cursor->hotspot_y,
+            cursor->buffer,
+            0, 0, cursor->width, cursor->height);
+
+}
+
diff --git a/src/terminal/cursor.h b/src/terminal/cursor.h
new file mode 100644
index 0000000..ffa5ca8
--- /dev/null
+++ b/src/terminal/cursor.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef _GUAC_TERMINAL_CURSOR_H
+#define _GUAC_TERMINAL_CURSOR_H
+
+#include "config.h"
+
+#include <guacamole/client.h>
+#include <guacamole/layer.h>
+
+typedef struct guac_terminal_cursor {
+
+    /**
+     * A buffer allocated with guac_client_alloc_buffer() that contains the
+     * cursor image.
+     */
+    guac_layer* buffer;
+
+    /**
+     * The width of the cursor in pixels.
+     */
+    int width;
+
+    /**
+     * The height of the cursor in pixels.
+     */
+    int height;
+
+    /**
+     * The X coordinate of the cursor hotspot.
+     */
+    int hotspot_x;
+
+    /**
+     * The Y coordinate of the cursor hotspot.
+     */
+    int hotspot_y;
+
+} guac_terminal_cursor;
+
+/**
+ * Allocates a new cursor, pre-populating the cursor with a newly-allocated
+ * buffer.
+ */
+guac_terminal_cursor* guac_terminal_cursor_alloc(guac_client* client);
+
+/**
+ * Frees the buffer associated with this cursor as well as the cursor itself.
+ */
+void guac_terminal_cursor_free(guac_client* client, guac_terminal_cursor* cursor);
+
+/**
+ * Set the remote cursor.
+ */
+void guac_terminal_set_cursor(guac_client* client, guac_terminal_cursor* cursor);
+
+#endif
diff --git a/src/protocols/ssh/display.c b/src/terminal/display.c
similarity index 76%
rename from src/protocols/ssh/display.c
rename to src/terminal/display.c
index 5d2976a..5a3b40f 100644
--- a/src/protocols/ssh/display.c
+++ b/src/terminal/display.c
@@ -1,48 +1,43 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-ssh.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
 
+#include "common.h"
+#include "display.h"
+#include "guac_surface.h"
+#include "types.h"
+
+#include <math.h>
 #include <stdlib.h>
 #include <string.h>
+#include <wchar.h>
+
+#include <cairo/cairo.h>
+#include <glib-object.h>
 #include <guacamole/client.h>
 #include <guacamole/protocol.h>
-
-#include "common.h"
-#include "types.h"
-#include "display.h"
+#include <guacamole/socket.h>
+#include <pango/pangocairo.h>
 
 const guac_terminal_color guac_terminal_palette[16] = {
 
@@ -129,15 +124,43 @@ int __guac_terminal_hash_codepoint(int codepoint) {
 }
 
 /**
- * Returns the location of the given character in the glyph cache layer,
- * sending it first if necessary. The location returned is in characters,
- * and thus must be multiplied by the glyph width to obtain the actual
- * location within the glyph cache layer.
+ * Sets the attributes of the display such that future glyphs will render as
+ * expected.
  */
-int __guac_terminal_get_glyph(guac_terminal_display* display, int codepoint) {
+int __guac_terminal_set_colors(guac_terminal_display* display,
+        guac_terminal_attributes* attributes) {
 
-    guac_socket* socket = display->client->socket;
-    int location;
+    int background, foreground;
+
+    /* Handle reverse video */
+    if (attributes->reverse != attributes->cursor) {
+        background = attributes->foreground;
+        foreground = attributes->background;
+    }
+    else {
+        foreground = attributes->foreground;
+        background = attributes->background;
+    }
+
+    /* Handle bold */
+    if (attributes->bold && foreground <= 7)
+        foreground += 8;
+
+    display->glyph_foreground = foreground;
+    display->glyph_background = background;
+
+    return 0;
+
+}
+
+/**
+ * Sends the given character to the terminal at the given row and column,
+ * rendering the character immediately. This bypasses the guac_terminal_display
+ * mechanism and is intended for flushing of updates only.
+ */
+int __guac_terminal_set(guac_terminal_display* display, int row, int col, int codepoint) {
+
+    int width;
 
     int bytes;
     char utf8[4];
@@ -152,196 +175,93 @@ int __guac_terminal_get_glyph(guac_terminal_display* display, int codepoint) {
 
     cairo_surface_t* surface;
     cairo_t* cairo;
+    int surface_width, surface_height;
    
     PangoLayout* layout;
+    int layout_width, layout_height;
+    int ideal_layout_width, ideal_layout_height;
 
-    /* Get codepoint hash */
-    int hashcode = __guac_terminal_hash_codepoint(codepoint);
+    /* Calculate width in columns */
+    width = wcwidth(codepoint);
+    if (width < 0)
+        width = 1;
 
-    /* If something already stored here, either same codepoint or collision */
-    if (display->glyphs[hashcode].location) {
-
-        location = display->glyphs[hashcode].location - 1;
-
-        /* If match, return match. */
-        if (display->glyphs[hashcode].codepoint == codepoint)
-            return location;
-
-        /* Otherwise, reuse location */
-
-    }
-
-    /* If no collision, allocate new glyph location */
-    else
-        location = display->next_glyph++;
+    /* Do nothing if glyph is empty */
+    if (width == 0)
+        return 0;
 
     /* Convert to UTF-8 */
     bytes = guac_terminal_encode_utf8(codepoint, utf8);
 
+    surface_width = width * display->char_width;
+    surface_height = display->char_height;
+
+    ideal_layout_width = surface_width * PANGO_SCALE;
+    ideal_layout_height = surface_height * PANGO_SCALE;
+
     /* Prepare surface */
-    surface = cairo_image_surface_create(
-            CAIRO_FORMAT_ARGB32,
-            display->char_width, display->char_height);
+    surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
+                                         surface_width, surface_height);
     cairo = cairo_create(surface);
 
+    /* Fill background */
+    cairo_set_source_rgb(cairo,
+            background->red   / 255.0,
+            background->green / 255.0,
+            background->blue  / 255.0);
+
+    cairo_rectangle(cairo, 0, 0, surface_width, surface_height); 
+    cairo_fill(cairo);
+
     /* Get layout */
     layout = pango_cairo_create_layout(cairo);
     pango_layout_set_font_description(layout, display->font_desc);
     pango_layout_set_text(layout, utf8, bytes);
+    pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
+
+    pango_layout_get_size(layout, &layout_width, &layout_height);
+
+    /* If layout bigger than available space, scale it back */
+    if (layout_width > ideal_layout_width || layout_height > ideal_layout_height) {
+
+        double scale = fmin(ideal_layout_width  / (double) layout_width,
+                            ideal_layout_height / (double) layout_height);
+
+        cairo_scale(cairo, scale, scale);
+
+        /* Update layout to reflect scaled surface */
+        pango_layout_set_width(layout, ideal_layout_width / scale);
+        pango_layout_set_height(layout, ideal_layout_height / scale);
+        pango_cairo_update_layout(cairo, layout);
+
+    }
 
     /* Draw */
-    cairo_set_source_rgba(cairo,
+    cairo_set_source_rgb(cairo,
             color->red   / 255.0,
             color->green / 255.0,
-            color->blue  / 255.0,
-            1.0 /* alpha */ );
+            color->blue  / 255.0);
 
     cairo_move_to(cairo, 0.0, 0.0);
     pango_cairo_show_layout(cairo, layout);
 
+    /* Draw */
+    guac_common_surface_draw(display->display_surface,
+        display->char_width * col,
+        display->char_height * row,
+        surface);
+
     /* Free all */
     g_object_unref(layout);
     cairo_destroy(cairo);
-
-    /* Clear existing glyph (if any) */
-    guac_protocol_send_rect(socket, display->glyph_stroke,
-            location * display->char_width, 0,
-            display->char_width, display->char_height);
-
-    guac_protocol_send_cfill(socket, GUAC_COMP_ROUT, display->glyph_stroke,
-            0x00, 0x00, 0x00, 0xFF);
-
-    /* Send glyph */
-    guac_protocol_send_png(socket, GUAC_COMP_OVER, display->glyph_stroke, location * display->char_width, 0, surface);
-
-    /* Update filled glyphs */
-    guac_protocol_send_rect(socket, display->filled_glyphs,
-            location * display->char_width, 0,
-            display->char_width, display->char_height);
-
-    guac_protocol_send_cfill(socket, GUAC_COMP_OVER, display->filled_glyphs,
-            background->red,
-            background->green,
-            background->blue,
-            0xFF);
-
-    guac_protocol_send_copy(socket, display->glyph_stroke,
-            location * display->char_width, 0, display->char_width, display->char_height,
-            GUAC_COMP_OVER, display->filled_glyphs, location * display->char_width, 0);
-
-    display->glyphs[hashcode].location = location+1;
-    display->glyphs[hashcode].codepoint = codepoint;
-
     cairo_surface_destroy(surface);
 
-    /* Return glyph */
-    return location;
-
-}
-
-/**
- * Sets the attributes of the glyph cache layer such that future copies from
- * this layer will display as expected.
- */
-int __guac_terminal_set_colors(guac_terminal_display* display,
-        guac_terminal_attributes* attributes) {
-
-    guac_socket* socket = display->client->socket;
-    const guac_terminal_color* background_color;
-    int background, foreground;
-
-    /* Handle reverse video */
-    if (attributes->reverse != attributes->cursor) {
-        background = attributes->foreground;
-        foreground = attributes->background;
-    }
-    else {
-        foreground = attributes->foreground;
-        background = attributes->background;
-    }
-
-    /* Handle bold */
-    if (attributes->bold && foreground <= 7)
-        foreground += 8;
-
-    /* Get background color */
-    background_color = &guac_terminal_palette[background];
-
-    /* If foreground different from current, colorize */
-    if (foreground != display->glyph_foreground) {
-
-        /* Get color */
-        const guac_terminal_color* color =
-            &guac_terminal_palette[foreground];
-
-        /* Colorize letter */
-        guac_protocol_send_rect(socket, display->glyph_stroke,
-            0, 0,
-            display->char_width * display->next_glyph, display->char_height);
-
-        guac_protocol_send_cfill(socket, GUAC_COMP_ATOP, display->glyph_stroke,
-            color->red,
-            color->green,
-            color->blue,
-            255);
-
-    }
-
-    /* If any color change at all, update filled */
-    if (foreground != display->glyph_foreground
-            || background != display->glyph_background) {
-
-        /* Set background */
-        guac_protocol_send_rect(socket, display->filled_glyphs,
-            0, 0,
-            display->char_width * display->next_glyph, display->char_height);
-
-        guac_protocol_send_cfill(socket, GUAC_COMP_OVER, display->filled_glyphs,
-            background_color->red,
-            background_color->green,
-            background_color->blue,
-            255);
-
-        /* Copy stroke */
-        guac_protocol_send_copy(socket, display->glyph_stroke,
-
-            0, 0,
-            display->char_width * display->next_glyph, display->char_height,
-
-            GUAC_COMP_OVER, display->filled_glyphs,
-            0, 0);
-
-    }
-
-    display->glyph_foreground = foreground;
-    display->glyph_background = background;
-
     return 0;
 
 }
 
-/**
- * Sends the given character to the terminal at the given row and column,
- * rendering the charater immediately. This bypasses the guac_terminal_display
- * mechanism and is intended for flushing of updates only.
- */
-int __guac_terminal_set(guac_terminal_display* display, int row, int col, int codepoint) {
-
-    guac_socket* socket = display->client->socket;
-    int location = __guac_terminal_get_glyph(display, codepoint); 
-
-    return guac_protocol_send_copy(socket,
-        display->filled_glyphs,
-        location * display->char_width, 0, display->char_width, display->char_height,
-        GUAC_COMP_OVER, GUAC_DEFAULT_LAYER,
-        display->char_width * col,
-        display->char_height * row);
-
-}
-
-
 guac_terminal_display* guac_terminal_display_alloc(guac_client* client,
-        const char* font_name, int font_size,
+        const char* font_name, int font_size, int dpi,
         int foreground, int background) {
 
     PangoFontMap* font_map;
@@ -353,35 +273,41 @@ guac_terminal_display* guac_terminal_display_alloc(guac_client* client,
     guac_terminal_display* display = malloc(sizeof(guac_terminal_display));
     display->client = client;
 
-    memset(display->glyphs, 0, sizeof(display->glyphs));
-    display->glyph_stroke = guac_client_alloc_buffer(client);
-    display->filled_glyphs = guac_client_alloc_buffer(client);
-
+    /* Create default surface */
+    display->display_layer = guac_client_alloc_layer(client);
     display->select_layer = guac_client_alloc_layer(client);
+    display->display_surface = guac_common_surface_alloc(client,
+            client->socket, display->display_layer, 0, 0);
+
+    /* Select layer is a child of the display layer */
+    guac_protocol_send_move(client->socket, display->select_layer,
+            display->display_layer, 0, 0, 0);
 
     /* Get font */
     display->font_desc = pango_font_description_new();
     pango_font_description_set_family(display->font_desc, font_name);
     pango_font_description_set_weight(display->font_desc, PANGO_WEIGHT_NORMAL);
-    pango_font_description_set_size(display->font_desc, font_size*PANGO_SCALE);
+    pango_font_description_set_size(display->font_desc,
+            font_size * PANGO_SCALE * dpi / 96);
 
     font_map = pango_cairo_font_map_get_default();
     context = pango_font_map_create_context(font_map);
 
     font = pango_font_map_load_font(font_map, context, display->font_desc);
     if (font == NULL) {
-        guac_client_log_error(display->client, "Unable to get font \"%s\"", font_name);
+        guac_client_abort(display->client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Unable to get font \"%s\"", font_name);
         return NULL;
     }
 
     metrics = pango_font_get_metrics(font, NULL);
     if (metrics == NULL) {
-        guac_client_log_error(display->client, "Unable to get font metrics for font \"%s\"", font_name);
+        guac_client_abort(display->client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
+                "Unable to get font metrics for font \"%s\"", font_name);
         return NULL;
     }
 
-    display->glyph_foreground = foreground;
-    display->glyph_background = background;
+    display->default_foreground = display->glyph_foreground = foreground;
+    display->default_background = display->glyph_background = background;
 
     /* Calculate character dimensions */
     display->char_width =
@@ -526,14 +452,15 @@ void guac_terminal_display_set_columns(guac_terminal_display* display, int row,
     current = &(display->operations[row * display->width + start_column]);
 
     /* For each column in range */
-    for (i=start_column; i<=end_column; i++) {
+    for (i = start_column; i <= end_column; i += character->width) {
 
         /* Set operation */
         current->type      = GUAC_CHAR_SET;
         current->character = *character;
 
-        /* Next column */
-        current++;
+        /* Next character */
+        current += character->width;
+
     }
 
     /* If selection visible and committed, clear if update touches selection */
@@ -548,13 +475,14 @@ void guac_terminal_display_resize(guac_terminal_display* display, int width, int
     guac_terminal_operation* current;
     int x, y;
 
-    /* Fill with background color (index 0) */
+    /* Fill with background color */
     guac_terminal_char fill = {
         .value = 0,
         .attributes = {
-            .foreground = 0,
-            .background = 0
-        }
+            .foreground = display->default_background,
+            .background = display->default_background
+        },
+        .width = 1
     };
 
     /* Free old operations buffer */
@@ -592,9 +520,9 @@ void guac_terminal_display_resize(guac_terminal_display* display, int width, int
     display->width = width;
     display->height = height;
 
-    /* Send initial display size */
-    guac_protocol_send_size(display->client->socket,
-            GUAC_DEFAULT_LAYER,
+    /* Send display size */
+    guac_common_surface_resize(
+            display->display_surface,
             display->char_width  * width,
             display->char_height * height);
 
@@ -712,16 +640,15 @@ void __guac_terminal_display_flush_copy(guac_terminal_display* display) {
                 }
 
                 /* Send copy */
-                guac_protocol_send_copy(display->client->socket,
+                guac_common_surface_copy(
 
-                        GUAC_DEFAULT_LAYER,
+                        display->display_surface,
                         current->column * display->char_width,
                         current->row * display->char_height,
                         rect_width * display->char_width,
                         rect_height * display->char_height,
 
-                        GUAC_COMP_OVER,
-                        GUAC_DEFAULT_LAYER,
+                        display->display_surface,
                         col * display->char_width,
                         row * display->char_height);
 
@@ -849,18 +776,13 @@ void __guac_terminal_display_flush_clear(guac_terminal_display* display) {
                 }
 
                 /* Send rect */
-                guac_protocol_send_rect(display->client->socket,
-                        GUAC_DEFAULT_LAYER,
+                guac_common_surface_rect(
+                        display->display_surface,
                         col * display->char_width,
                         row * display->char_height,
                         rect_width * display->char_width,
-                        rect_height * display->char_height);
-
-                guac_protocol_send_cfill(display->client->socket,
-                        GUAC_COMP_OVER,
-                        GUAC_DEFAULT_LAYER,
-                        guac_color->red, guac_color->green, guac_color->blue,
-                        0xFF);
+                        rect_height * display->char_height,
+                        guac_color->red, guac_color->green, guac_color->blue);
 
             } /* end if clear operation */
 
@@ -872,6 +794,7 @@ void __guac_terminal_display_flush_clear(guac_terminal_display* display) {
 
 }
 
+
 void __guac_terminal_display_flush_set(guac_terminal_display* display) {
 
     guac_terminal_operation* current = display->operations;
@@ -884,13 +807,18 @@ void __guac_terminal_display_flush_set(guac_terminal_display* display) {
             /* Perform given operation */
             if (current->type == GUAC_CHAR_SET) {
 
+                int codepoint = current->character.value;
+
+                /* Use space if no glyph */
+                if (!guac_terminal_has_glyph(codepoint))
+                    codepoint = ' ';
+
                 /* Set attributes */
                 __guac_terminal_set_colors(display,
                         &(current->character.attributes));
 
                 /* Send character */
-                __guac_terminal_set(display, row, col,
-                        current->character.value);
+                __guac_terminal_set(display, row, col, codepoint);
 
                 /* Mark operation as handled */
                 current->type = GUAC_CHAR_NOP;
@@ -912,6 +840,9 @@ void guac_terminal_display_flush(guac_terminal_display* display) {
     __guac_terminal_display_flush_clear(display);
     __guac_terminal_display_flush_set(display);
 
+    /* Flush surface */
+    guac_common_surface_flush(display->display_surface);
+
 }
 
 void guac_terminal_display_commit_select(guac_terminal_display* display) {
@@ -932,7 +863,6 @@ void guac_terminal_display_select(guac_terminal_display* display,
     display->selection_end_row = end_row;
     display->selection_end_column = end_col;
 
-
     /* If single row, just need one rectangle */
     if (start_row == end_row) {
 
diff --git a/src/protocols/ssh/display.h b/src/terminal/display.h
similarity index 57%
rename from src/protocols/ssh/display.h
rename to src/terminal/display.h
index c0f5c52..d42d99b 100644
--- a/src/protocols/ssh/display.h
+++ b/src/terminal/display.h
@@ -1,48 +1,126 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-ssh.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#ifndef _SSH_GUAC_DISPLAY_H
-#define _SSH_GUAC_DISPLAY_H
 
-#include <pango/pangocairo.h>
-#include <guacamole/client.h>
+#ifndef _GUAC_TERMINAL_DISPLAY_H
+#define _GUAC_TERMINAL_DISPLAY_H
 
+#include "config.h"
+
+#include "guac_surface.h"
 #include "types.h"
 
+#include <guacamole/client.h>
+#include <guacamole/layer.h>
+#include <pango/pangocairo.h>
+
+#include <stdbool.h>
+
+/**
+ * The maximum width of any character, in columns.
+ */
+#define GUAC_TERMINAL_MAX_CHAR_WIDTH 2
+
+/**
+ * The index of black within the terminal color palette.
+ */
+#define GUAC_TERMINAL_COLOR_BLACK 0
+
+/**
+ * The index of low-intensity red within the terminal color palette.
+ */
+#define GUAC_TERMINAL_COLOR_DARK_RED 1
+
+/**
+ * The index of low-intensity green within the terminal color palette.
+ */
+#define GUAC_TERMINAL_COLOR_DARK_GREEN 2
+
+/**
+ * The index of brown within the terminal color palette.
+ */
+#define GUAC_TERMINAL_COLOR_BROWN 3
+
+/**
+ * The index of low-intensity blue within the terminal color palette.
+ */
+#define GUAC_TERMINAL_COLOR_DARK_BLUE 4
+
+/**
+ * The index of low-intensity magenta (purple) within the terminal color
+ * palette.
+ */
+#define GUAC_TERMINAL_COLOR_PURPLE 5
+
+/**
+ * The index of low-intensity cyan (teal) within the terminal color palette.
+ */
+#define GUAC_TERMINAL_COLOR_TEAL 6
+
+/**
+ * The index of low-intensity white (gray) within the terminal color palette.
+ */
+#define GUAC_TERMINAL_COLOR_GRAY 7
+
+/**
+ * The index of bright black (dark gray) within the terminal color palette.
+ */
+#define GUAC_TERMINAL_COLOR_DARK_GRAY 8
+
+/**
+ * The index of bright red within the terminal color palette.
+ */
+#define GUAC_TERMINAL_COLOR_RED 9
+
+/**
+ * The index of bright green within the terminal color palette.
+ */
+#define GUAC_TERMINAL_COLOR_GREEN 10
+
+/**
+ * The index of bright brown (yellow) within the terminal color palette.
+ */
+#define GUAC_TERMINAL_COLOR_YELLOW 11
+
+/**
+ * The index of bright blue within the terminal color palette.
+ */
+#define GUAC_TERMINAL_COLOR_BLUE 12
+
+/**
+ * The index of bright magenta within the terminal color palette.
+ */
+#define GUAC_TERMINAL_COLOR_MAGENTA 13
+
+/**
+ * The index of bright cyan within the terminal color palette.
+ */
+#define GUAC_TERMINAL_COLOR_CYAN 14
+
+/**
+ * The index of bright white within the terminal color palette.
+ */
+#define GUAC_TERMINAL_COLOR_WHITE 15
+
 /**
  * The available color palette. All integer colors within structures
  * here are indices into this palette.
@@ -102,25 +180,6 @@ typedef struct guac_terminal_operation {
 
 } guac_terminal_operation;
 
-
-/**
- * A cached glyph.
- */
-typedef struct guac_terminal_glyph {
-
-    /**
-     * The location within the glyph layer that this glyph can be found.
-     */
-    int location;
-
-    /**
-     * The codepoint currently stored at that location.
-     */
-    int codepoint;
-
-} guac_terminal_glyph;
-
-
 /**
  * Set of all pending operations for the currently-visible screen area.
  */
@@ -162,14 +221,14 @@ typedef struct guac_terminal_display {
     int char_height;
 
     /**
-     * Index of next glyph to create
+     * Default foreground color for all glyphs.
      */
-    int next_glyph;
+    int default_foreground;
 
     /**
-     * Index of locations for each glyph in the stroke and fill layers.
+     * Default background color for all glyphs and the terminal itself.
      */
-    guac_terminal_glyph glyphs[512];
+    int default_background;
 
     /**
      * Color of glyphs in copy buffer
@@ -182,21 +241,19 @@ typedef struct guac_terminal_display {
     int glyph_background;
 
     /**
-     * Layer above default layer which highlights selected text.
+     * The surface containing the actual terminal.
      */
-    guac_layer* select_layer;
+    guac_common_surface* display_surface;
 
     /**
-     * A single wide layer holding each glyph, with each glyph only
-     * colored with foreground color (background remains transparent).
+     * Layer which contains the actual terminal.
      */
-    guac_layer* glyph_stroke;
+    guac_layer* display_layer;
 
     /**
-     * A single wide layer holding each glyph, with each glyph properly
-     * colored with foreground and background color (no transparency at all).
+     * Sub-layer of display layer which highlights selected text.
      */
-    guac_layer* filled_glyphs;
+    guac_layer* select_layer;
 
     /**
      * Whether text is being selected.
@@ -230,7 +287,6 @@ typedef struct guac_terminal_display {
      */
     int selection_end_column;
 
-
 } guac_terminal_display;
 
 /**
@@ -238,7 +294,7 @@ typedef struct guac_terminal_display {
  * colors.
  */
 guac_terminal_display* guac_terminal_display_alloc(guac_client* client,
-        const char* font_name, int font_size,
+        const char* font_name, int font_size, int dpi,
         int foreground, int background);
 
 /**
diff --git a/src/terminal/ibar.c b/src/terminal/ibar.c
new file mode 100644
index 0000000..7ba69c3
--- /dev/null
+++ b/src/terminal/ibar.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "cursor.h"
+
+#include <cairo/cairo.h>
+#include <guacamole/client.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+
+/* Macros for prettying up the embedded image. */
+#define X 0x00,0x00,0x00,0xFF
+#define U 0x80,0x80,0x80,0xFF
+#define O 0xFF,0xFF,0xFF,0xFF
+#define _ 0x00,0x00,0x00,0x00
+
+/* Dimensions */
+const int guac_terminal_ibar_width  = 7;
+const int guac_terminal_ibar_height = 16;
+
+/* Format */
+const cairo_format_t guac_terminal_ibar_format = CAIRO_FORMAT_ARGB32;
+const int guac_terminal_ibar_stride = 28;
+
+/* Embedded pointer graphic */
+unsigned char guac_terminal_ibar[] = {
+
+        X,X,X,X,X,X,X,
+        X,O,O,U,O,O,X,
+        X,X,X,O,X,X,X,
+        _,_,X,O,X,_,_,
+        _,_,X,O,X,_,_,
+        _,_,X,O,X,_,_,
+        _,_,X,O,X,_,_,
+        _,_,X,O,X,_,_,
+        _,_,X,O,X,_,_,
+        _,_,X,O,X,_,_,
+        _,_,X,O,X,_,_,
+        _,_,X,O,X,_,_,
+        _,_,X,O,X,_,_,
+        X,X,X,O,X,X,X,
+        X,O,O,U,O,O,X,
+        X,X,X,X,X,X,X
+
+};
+
+guac_terminal_cursor* guac_terminal_create_ibar(guac_client* client) {
+
+    guac_socket* socket = client->socket;
+    guac_terminal_cursor* cursor = guac_terminal_cursor_alloc(client);
+
+    /* Draw to buffer */
+    cairo_surface_t* graphic = cairo_image_surface_create_for_data(
+            guac_terminal_ibar,
+            guac_terminal_ibar_format,
+            guac_terminal_ibar_width,
+            guac_terminal_ibar_height,
+            guac_terminal_ibar_stride);
+
+    guac_client_stream_png(client, socket, GUAC_COMP_SRC, cursor->buffer,
+            0, 0, graphic);
+    cairo_surface_destroy(graphic);
+
+    /* Initialize cursor properties */
+    cursor->width = guac_terminal_ibar_width;
+    cursor->height = guac_terminal_ibar_height;
+    cursor->hotspot_x = guac_terminal_ibar_width / 2;
+    cursor->hotspot_y = guac_terminal_ibar_height / 2;
+
+    return cursor;
+
+}
+
diff --git a/src/terminal/ibar.h b/src/terminal/ibar.h
new file mode 100644
index 0000000..bbbc657
--- /dev/null
+++ b/src/terminal/ibar.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef _GUAC_TERMINAL_IBAR_H
+#define _GUAC_TERMINAL_IBAR_H
+
+#include "config.h"
+
+#include <cairo/cairo.h>
+#include <guacamole/client.h>
+
+/**
+ * Width of the embedded mouse cursor graphic.
+ */
+extern const int guac_terminal_ibar_width;
+
+/**
+ * Height of the embedded mouse cursor graphic.
+ */
+extern const int guac_terminal_ibar_height;
+
+/**
+ * Number of bytes in each row of the embedded mouse cursor graphic.
+ */
+extern const int guac_terminal_ibar_stride;
+
+/**
+ * The Cairo grapic format of the mouse cursor graphic.
+ */
+extern const cairo_format_t guac_terminal_ibar_format;
+
+/**
+ * Embedded mouse cursor graphic.
+ */
+extern unsigned char guac_terminal_ibar[];
+
+/**
+ * Creates a new I-bar cursor, returning the corresponding cursor object.
+ *
+ * @param client The guac_client to send the cursor to.
+ * @return A new cursor which must be free'd via guac_terminal_cursor_free()/
+ */
+guac_terminal_cursor* guac_terminal_create_ibar(guac_client* client);
+
+#endif
diff --git a/src/terminal/packet.c b/src/terminal/packet.c
new file mode 100644
index 0000000..9b4ca7f
--- /dev/null
+++ b/src/terminal/packet.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "common.h"
+#include "packet.h"
+
+#include <string.h>
+
+int guac_terminal_packet_write(int fd, const void* data, int length) {
+
+    guac_terminal_packet out;
+
+    /* Do not attempt to write packets beyond maximum size */
+    if (length > GUAC_TERMINAL_PACKET_SIZE)
+        return -1;
+
+    /* Calculate final packet length */
+    int packet_length = sizeof(int) + length;
+
+    /* Copy data into packet */
+    out.length = length;
+    memcpy(out.data, data, length);
+
+    /* Write packet */
+    return guac_terminal_write_all(fd, (const char*) &out, packet_length);
+
+}
+
+int guac_terminal_packet_read(int fd, void* data, int length) {
+
+    int bytes;
+
+    /* Read buffers MUST be at least GUAC_TERMINAL_PACKET_SIZE */
+    if (length < GUAC_TERMINAL_PACKET_SIZE)
+        return -1;
+
+    /* Read length */
+    if (guac_terminal_fill_buffer(fd, (char*) &bytes, sizeof(int)) < 0)
+        return -1;
+
+    /* Read body */
+    if (guac_terminal_fill_buffer(fd, (char*) data, bytes) < 0)
+        return -1;
+
+    return bytes;
+
+}
+
diff --git a/src/terminal/packet.h b/src/terminal/packet.h
new file mode 100644
index 0000000..1220e31
--- /dev/null
+++ b/src/terminal/packet.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GUAC_TERMINAL_PACKET_H
+#define GUAC_TERMINAL_PACKET_H
+
+/**
+ * The maximum size of a packet written or read by the
+ * guac_terminal_packet_write() or guac_terminal_packet_read() functions.
+ */
+#define GUAC_TERMINAL_PACKET_SIZE 4096
+
+/**
+ * An arbitrary data packet with minimal framing.
+ */
+typedef struct guac_terminal_packet {
+
+    /**
+     * The number of bytes in the data portion of this packet.
+     */
+    int length;
+
+    /**
+     * Arbitrary data.
+     */
+    char data[GUAC_TERMINAL_PACKET_SIZE];
+
+} guac_terminal_packet;
+
+/**
+ * Writes a single packet of data to the given file descriptor. The provided
+ * length MUST be no greater than GUAC_TERMINAL_PACKET_SIZE. Zero-length
+ * writes are legal and do result in a packet being written to the file
+ * descriptor.
+ *
+ * @param fd
+ *     The file descriptor to write to.
+ *
+ * @param data
+ *     A buffer containing the data to write.
+ *
+ * @param length
+ *     The number of bytes to write to the file descriptor.
+ *
+ * @return
+ *     The number of bytes written on success, which may be zero if the data
+ *     length is zero, or a negative value on error.
+ */
+int guac_terminal_packet_write(int fd, const void* data, int length);
+
+/**
+ * Reads a single packet of data from the given file descriptor. The provided
+ * length MUST be at least GUAC_TERMINAL_PACKET_SIZE to ensure any packet
+ * read will fit in the buffer. Zero-length reads are possible if a zero-length
+ * packet was written.
+ *
+ * @param fd
+ *     The file descriptor to read from.
+ *
+ * @param data
+ *     The buffer to store data within.
+ *
+ * @param length
+ *     The number of bytes available within the buffer.
+ *
+ * @return
+ *     The number of bytes read on success, which may be zero if the read
+ *     packet had a length of zero, or a negative value on error.
+ */
+int guac_terminal_packet_read(int fd, void* data, int length);
+
+#endif
+
diff --git a/src/terminal/pointer.c b/src/terminal/pointer.c
new file mode 100644
index 0000000..72e99e3
--- /dev/null
+++ b/src/terminal/pointer.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "cursor.h"
+
+#include <cairo/cairo.h>
+#include <guacamole/client.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+
+/* Macros for prettying up the embedded image. */
+#define X 0x00,0x00,0x00,0xFF
+#define U 0x80,0x80,0x80,0xFF
+#define O 0xFF,0xFF,0xFF,0xFF
+#define _ 0x00,0x00,0x00,0x00
+
+/* Dimensions */
+const int guac_terminal_pointer_width  = 11;
+const int guac_terminal_pointer_height = 16;
+
+/* Format */
+const cairo_format_t guac_terminal_pointer_format = CAIRO_FORMAT_ARGB32;
+const int guac_terminal_pointer_stride = 44;
+
+/* Embedded pointer graphic */
+unsigned char guac_terminal_pointer[] = {
+
+        O,_,_,_,_,_,_,_,_,_,_,
+        O,O,_,_,_,_,_,_,_,_,_,
+        O,X,O,_,_,_,_,_,_,_,_,
+        O,X,X,O,_,_,_,_,_,_,_,
+        O,X,X,X,O,_,_,_,_,_,_,
+        O,X,X,X,X,O,_,_,_,_,_,
+        O,X,X,X,X,X,O,_,_,_,_,
+        O,X,X,X,X,X,X,O,_,_,_,
+        O,X,X,X,X,X,X,X,O,_,_,
+        O,X,X,X,X,X,X,X,X,O,_,
+        O,X,X,X,X,X,O,O,O,O,O,
+        O,X,X,O,X,X,O,_,_,_,_,
+        O,X,O,_,O,X,X,O,_,_,_,
+        O,O,_,_,O,X,X,O,_,_,_,
+        O,_,_,_,_,O,X,X,O,_,_,
+        _,_,_,_,_,O,O,O,O,_,_
+
+};
+
+guac_terminal_cursor* guac_terminal_create_pointer(guac_client* client) {
+
+    guac_socket* socket = client->socket;
+    guac_terminal_cursor* cursor = guac_terminal_cursor_alloc(client);
+
+    /* Draw to buffer */
+    cairo_surface_t* graphic = cairo_image_surface_create_for_data(
+            guac_terminal_pointer,
+            guac_terminal_pointer_format,
+            guac_terminal_pointer_width,
+            guac_terminal_pointer_height,
+            guac_terminal_pointer_stride);
+
+    guac_client_stream_png(client, socket, GUAC_COMP_SRC, cursor->buffer,
+            0, 0, graphic);
+    cairo_surface_destroy(graphic);
+
+    /* Initialize cursor properties */
+    cursor->width = guac_terminal_pointer_width;
+    cursor->height = guac_terminal_pointer_height;
+    cursor->hotspot_x = 0;
+    cursor->hotspot_y = 0;
+
+    return cursor;
+
+}
+
diff --git a/src/terminal/pointer.h b/src/terminal/pointer.h
new file mode 100644
index 0000000..aae2f3d
--- /dev/null
+++ b/src/terminal/pointer.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef GUAC_TERMINAL_POINTER_H
+#define GUAC_TERMINAL_POINTER_H
+
+#include "config.h"
+
+#include <cairo/cairo.h>
+#include <guacamole/client.h>
+
+/**
+ * Width of the embedded mouse cursor graphic.
+ */
+extern const int guac_terminal_pointer_width;
+
+/**
+ * Height of the embedded mouse cursor graphic.
+ */
+extern const int guac_terminal_pointer_height;
+
+/**
+ * Number of bytes in each row of the embedded mouse cursor graphic.
+ */
+extern const int guac_terminal_pointer_stride;
+
+/**
+ * The Cairo grapic format of the mouse cursor graphic.
+ */
+extern const cairo_format_t guac_terminal_pointer_format;
+
+/**
+ * Embedded mouse cursor graphic.
+ */
+extern unsigned char guac_terminal_pointer[];
+
+/**
+ * Creates a new pointer cursor, returning the corresponding cursor object.
+ *
+ * @param client
+ *     The guac_client to send the cursor to.
+ *
+ * @return
+ *     A new cursor which must be free'd via guac_terminal_cursor_free().
+ */
+guac_terminal_cursor* guac_terminal_create_pointer(guac_client* client);
+
+#endif
diff --git a/src/terminal/scrollbar.c b/src/terminal/scrollbar.c
new file mode 100644
index 0000000..785e909
--- /dev/null
+++ b/src/terminal/scrollbar.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+#include "scrollbar.h"
+
+#include <guacamole/client.h>
+#include <guacamole/layer.h>
+#include <guacamole/socket.h>
+#include <guacamole/protocol.h>
+
+#include <stdlib.h>
+
+guac_terminal_scrollbar* guac_terminal_scrollbar_alloc(guac_client* client,
+        const guac_layer* parent, int parent_width, int parent_height, int visible_area) {
+
+    /* Allocate scrollbar */
+    guac_terminal_scrollbar* scrollbar =
+        malloc(sizeof(guac_terminal_scrollbar));
+
+    /* Associate client */
+    scrollbar->client = client;
+
+    /* Init default min/max and value */
+    scrollbar->min   = 0;
+    scrollbar->max   = 0;
+    scrollbar->value = 0;
+
+    /* Init parent data */
+    scrollbar->parent        = parent;
+    scrollbar->parent_width  = 0;
+    scrollbar->parent_height = 0;
+    scrollbar->visible_area  = 0;
+
+    /* Init handle render state */
+    scrollbar->render_state.handle_x      = 0;
+    scrollbar->render_state.handle_y      = 0;
+    scrollbar->render_state.handle_width  = 0;
+    scrollbar->render_state.handle_height = 0;
+
+    /* Init container render state */
+    scrollbar->render_state.container_x      = 0;
+    scrollbar->render_state.container_y      = 0;
+    scrollbar->render_state.container_width  = 0;
+    scrollbar->render_state.container_height = 0;
+
+    /* Allocate and init layers */
+    scrollbar->container = guac_client_alloc_layer(client);
+    scrollbar->handle    = guac_client_alloc_layer(client);
+
+    /* Init mouse event state tracking */
+    scrollbar->dragging_handle = 0;
+
+    /* Reposition and resize to fit parent */
+    guac_terminal_scrollbar_parent_resized(scrollbar,
+            parent_width, parent_height, visible_area);
+
+    return scrollbar;
+
+}
+
+void guac_terminal_scrollbar_free(guac_terminal_scrollbar* scrollbar) {
+
+    /* Free layers */
+    guac_client_free_layer(scrollbar->client, scrollbar->handle);
+    guac_client_free_layer(scrollbar->client, scrollbar->container);
+
+    /* Free scrollbar */
+    free(scrollbar);
+
+}
+
+/**
+ * Calculates the state of the scroll bar, given its minimum, maximum, current
+ * values, and the state of any dragging operation. The resulting render state
+ * will not be reflected graphically unless the scrollbar is flushed, and any
+ * resulting value will not be assigned to the scrollbar unless explicitly set
+ * with guac_terminal_scrollbar_set_value().
+ *
+ * @param scrollbar
+ *     The scrollbar whose state should be calculated.
+ *
+ * @param render_state
+ *     A pointer to an existing guac_terminal_scrollbar_render_state that will
+ *     be populated with the calculated result.
+ *
+ * @param value
+ *     A pointer to an existing int that will be populated with the updated
+ *     scrollbar value.
+ */
+static void calculate_state(guac_terminal_scrollbar* scrollbar,
+        guac_terminal_scrollbar_render_state* render_state,
+        int* value) {
+
+    /* Use unchanged current value by default */
+    *value = scrollbar->value;
+
+    /* Calculate container dimensions */
+    render_state->container_width  = GUAC_TERMINAL_SCROLLBAR_WIDTH;
+    render_state->container_height = scrollbar->parent_height;
+
+    /* Calculate container position */
+    render_state->container_x = scrollbar->parent_width
+                              - render_state->container_width;
+
+    render_state->container_y = 0;
+
+    /* Calculate handle dimensions */
+    render_state->handle_width  = render_state->container_width
+                                - GUAC_TERMINAL_SCROLLBAR_PADDING*2;
+
+    /* Handle can be no bigger than the scrollbar itself */
+    int max_handle_height = render_state->container_height
+                          - GUAC_TERMINAL_SCROLLBAR_PADDING*2;
+
+    /* Calculate legal delta between scroll values */
+    int scroll_delta;
+    if (scrollbar->max > scrollbar->min)
+        scroll_delta = scrollbar->max - scrollbar->min;
+    else
+        scroll_delta = 0;
+
+    /* Scale handle relative to visible area vs. scrolling region size */
+    int proportional_height = max_handle_height
+                            * scrollbar->visible_area
+                            / (scroll_delta + scrollbar->visible_area);
+
+    /* Ensure handle is no smaller than minimum height */
+    if (proportional_height > GUAC_TERMINAL_SCROLLBAR_MIN_HEIGHT)
+        render_state->handle_height = proportional_height;
+    else
+        render_state->handle_height = GUAC_TERMINAL_SCROLLBAR_MIN_HEIGHT;
+
+    /* Ensure handle is no larger than maximum height */
+    if (render_state->handle_height > max_handle_height)
+        render_state->handle_height = max_handle_height;
+
+    /* Calculate handle X position */
+    render_state->handle_x = GUAC_TERMINAL_SCROLLBAR_PADDING;
+
+    /* Calculate handle Y range */
+    int min_handle_y = GUAC_TERMINAL_SCROLLBAR_PADDING;
+    int max_handle_y = min_handle_y + max_handle_height
+                     - render_state->handle_height;
+
+    /* Position handle relative to mouse if being dragged */
+    if (scrollbar->dragging_handle) {
+
+        int dragged_handle_y = scrollbar->drag_current_y
+                             - scrollbar->drag_offset_y;
+
+        /* Keep handle within bounds */
+        if (dragged_handle_y < min_handle_y)
+            dragged_handle_y = min_handle_y;
+        else if (dragged_handle_y > max_handle_y)
+            dragged_handle_y = max_handle_y;
+
+        render_state->handle_y = dragged_handle_y;
+
+        /* Calculate scrollbar value */
+        if (max_handle_y > min_handle_y) {
+            *value = scrollbar->min
+                   + (dragged_handle_y - min_handle_y)
+                      * scroll_delta
+                      / (max_handle_y - min_handle_y);
+        }
+
+    }
+
+    /* Handle Y position is relative to current scroll value */
+    else if (scroll_delta > 0)
+        render_state->handle_y = min_handle_y
+                               + (max_handle_y - min_handle_y)
+                                  * (scrollbar->value - scrollbar->min)
+                                  / scroll_delta;
+
+    /* ... unless there is only one possible scroll value */
+    else
+        render_state->handle_y = GUAC_TERMINAL_SCROLLBAR_PADDING;
+
+}
+
+void guac_terminal_scrollbar_flush(guac_terminal_scrollbar* scrollbar) {
+
+    guac_socket* socket = scrollbar->client->socket;
+
+    /* Get old state */
+    int old_value = scrollbar->value;
+    guac_terminal_scrollbar_render_state* old_state = &scrollbar->render_state;
+
+    /* Calculate new state */
+    int new_value;
+    guac_terminal_scrollbar_render_state new_state;
+    calculate_state(scrollbar, &new_state, &new_value);
+
+    /* Notify of scroll if value is changing */
+    if (new_value != old_value && scrollbar->scroll_handler)
+        scrollbar->scroll_handler(scrollbar, new_value);
+
+    /* Reposition container if moved */
+    if (old_state->container_x != new_state.container_x
+     || old_state->container_y != new_state.container_y) {
+
+        guac_protocol_send_move(socket,
+                scrollbar->container, scrollbar->parent,
+                new_state.container_x,
+                new_state.container_y,
+                0);
+
+    }
+
+    /* Resize and redraw container if size changed */
+    if (old_state->container_width  != new_state.container_width
+     || old_state->container_height != new_state.container_height) {
+
+        /* Set new size */
+        guac_protocol_send_size(socket, scrollbar->container,
+                new_state.container_width,
+                new_state.container_height);
+
+        /* Fill container with solid color */
+        guac_protocol_send_rect(socket, scrollbar->container, 0, 0,
+                new_state.container_width,
+                new_state.container_height);
+
+        guac_protocol_send_cfill(socket, GUAC_COMP_SRC, scrollbar->container,
+                0x80, 0x80, 0x80, 0x40);
+
+    }
+
+    /* Reposition handle if moved */
+    if (old_state->handle_x != new_state.handle_x
+     || old_state->handle_y != new_state.handle_y) {
+
+        guac_protocol_send_move(socket,
+                scrollbar->handle, scrollbar->container,
+                new_state.handle_x,
+                new_state.handle_y,
+                0);
+
+    }
+
+    /* Resize and redraw handle if size changed */
+    if (old_state->handle_width  != new_state.handle_width
+     || old_state->handle_height != new_state.handle_height) {
+
+        /* Send new size */
+        guac_protocol_send_size(socket, scrollbar->handle,
+                new_state.handle_width,
+                new_state.handle_height);
+
+        /* Fill and stroke handle with solid color */
+        guac_protocol_send_rect(socket, scrollbar->handle, 0, 0,
+                new_state.handle_width,
+                new_state.handle_height);
+
+        guac_protocol_send_cfill(socket, GUAC_COMP_SRC, scrollbar->handle,
+                0xA0, 0xA0, 0xA0, 0x8F);
+
+    }
+
+    /* Store current render state */
+    scrollbar->render_state = new_state;
+
+}
+
+void guac_terminal_scrollbar_set_bounds(guac_terminal_scrollbar* scrollbar,
+        int min, int max) {
+
+    /* Fit value within bounds */
+    if (scrollbar->value > max)
+        scrollbar->value = max;
+    else if (scrollbar->value < min)
+        scrollbar->value = min;
+
+    /* Update bounds */
+    scrollbar->min = min;
+    scrollbar->max = max;
+
+}
+
+void guac_terminal_scrollbar_set_value(guac_terminal_scrollbar* scrollbar,
+        int value) {
+
+    /* Fit value within bounds */
+    if (value > scrollbar->max)
+        value = scrollbar->max;
+    else if (value < scrollbar->min)
+        value = scrollbar->min;
+
+    /* Update value */
+    scrollbar->value = value;
+
+}
+
+void guac_terminal_scrollbar_parent_resized(guac_terminal_scrollbar* scrollbar,
+        int parent_width, int parent_height, int visible_area) {
+
+    /* Assign new dimensions */
+    scrollbar->parent_width  = parent_width;
+    scrollbar->parent_height = parent_height;
+    scrollbar->visible_area  = visible_area;
+
+}
+
+int guac_terminal_scrollbar_handle_mouse(guac_terminal_scrollbar* scrollbar,
+        int x, int y, int mask) {
+
+    /* Get container rectangle bounds */
+    int parent_left   = scrollbar->render_state.container_x;
+    int parent_top    = scrollbar->render_state.container_y;
+    int parent_right  = parent_left + scrollbar->render_state.container_width;
+    int parent_bottom = parent_top  + scrollbar->render_state.container_height;
+
+    /* Calculate handle rectangle bounds */
+    int handle_left   = parent_left + scrollbar->render_state.handle_x;
+    int handle_top    = parent_top  + scrollbar->render_state.handle_y;
+    int handle_right  = handle_left + scrollbar->render_state.handle_width;
+    int handle_bottom = handle_top  + scrollbar->render_state.handle_height;
+
+    /* Handle click on handle */
+    if (scrollbar->dragging_handle) {
+
+        /* Update drag while mouse button is held */
+        if (mask & GUAC_CLIENT_MOUSE_LEFT)
+            scrollbar->drag_current_y = y;
+
+        /* Stop drag if mouse button is released */
+        else
+            scrollbar->dragging_handle = 0;
+
+        /* Mouse event was handled by scrollbar */
+        return 1;
+
+    }
+    else if (mask == GUAC_CLIENT_MOUSE_LEFT
+            && x >= handle_left && x < handle_right
+            && y >= handle_top  && y < handle_bottom) {
+
+        /* Start drag */
+        scrollbar->dragging_handle = 1;
+        scrollbar->drag_offset_y = y - handle_top;
+        scrollbar->drag_current_y = y;
+
+        /* Mouse event was handled by scrollbar */
+        return 1;
+
+    }
+
+    /* Eat any events that occur within the scrollbar */
+    return x >= parent_left && x < parent_right
+        && y >= parent_top  && y < parent_bottom;
+
+}
+
diff --git a/src/terminal/scrollbar.h b/src/terminal/scrollbar.h
new file mode 100644
index 0000000..13c66b1
--- /dev/null
+++ b/src/terminal/scrollbar.h
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GUAC_TERMINAL_SCROLLBAR_H
+#define GUAC_TERMINAL_SCROLLBAR_H
+
+#include "config.h"
+
+#include <guacamole/client.h>
+#include <guacamole/layer.h>
+
+/**
+ * The width of the scrollbar, in pixels.
+ */
+#define GUAC_TERMINAL_SCROLLBAR_WIDTH 16 
+
+/**
+ * The number of pixels between the draggable handle of the scrollbar and the
+ * boundary of the containing layer.
+ */
+#define GUAC_TERMINAL_SCROLLBAR_PADDING 2
+
+/**
+ * The minimum height of the draggable handle of the scrollbar, in pixels.
+ */
+#define GUAC_TERMINAL_SCROLLBAR_MIN_HEIGHT 64
+
+/**
+ * The state of all scrollbar components, describing all variable aspects of
+ * the scrollbar's appearance.
+ */
+typedef struct guac_terminal_scrollbar_render_state {
+
+    /**
+     * The current X-coordinate of the upper-left corner of the scrollbar's
+     * handle. This value will be relative to the scrollbar's containing layer.
+     */
+    int handle_x;
+
+    /**
+     * The current Y-coordinate of the upper-left corner of the scrollbar's
+     * handle. This value will be relative to the scrollbar's containing layer.
+     */
+    int handle_y;
+
+    /**
+     * The width of the scrollbar's handle.
+     */
+    int handle_width;
+
+    /**
+     * The height of the scrollbar's handle.
+     */
+    int handle_height;
+
+    /**
+     * The current X-coordinate of the upper-left corner of the scrollbar's
+     * containing layer.
+     */
+    int container_x;
+
+    /**
+     * The current Y-coordinate of the upper-left corner of the scrollbar's
+     * containing layer.
+     */
+    int container_y;
+
+    /**
+     * The width of the scrollbar's containing layer.
+     */
+    int container_width;
+
+    /**
+     * The height of the scrollbar's containing layer.
+     */
+    int container_height;
+
+} guac_terminal_scrollbar_render_state;
+
+typedef struct guac_terminal_scrollbar guac_terminal_scrollbar;
+
+/**
+ * Handler which is called whenever the scrollbar value changes outside a call
+ * to guac_terminal_scrollbar_set_value().
+ */
+typedef void guac_terminal_scrollbar_scroll_handler(
+        guac_terminal_scrollbar* scrollbar, int value);
+
+/**
+ * A scrollbar, made up of a containing layer and inner draggable handle. The
+ * position of the handle within the layer represents the value of the
+ * scrollbar.
+ */
+struct guac_terminal_scrollbar {
+
+    /**
+     * The client associated with this scrollbar.
+     */
+    guac_client* client;
+
+    /**
+     * The layer containing the scrollbar.
+     */
+    const guac_layer* parent;
+
+    /**
+     * The width of the parent layer, in pixels.
+     */
+    int parent_width;
+
+    /**
+     * The height of the parent layer, in pixels.
+     */
+    int parent_height;
+
+    /**
+     * The scrollbar itself.
+     */
+    guac_layer* container;
+
+    /**
+     * The draggable handle within the scrollbar, representing the current
+     * scroll value.
+     */
+    guac_layer* handle;
+
+    /**
+     * The minimum scroll value.
+     */
+    int min;
+
+    /**
+     * The maximum scroll value.
+     */
+    int max;
+
+    /**
+     * The size of the visible area, in the same units as min and max.
+     */
+    int visible_area;
+
+    /**
+     * The current scroll value.
+     */
+    int value;
+
+    /**
+     * The current state of all variable, visible parts of the scrollbar.
+     */
+    guac_terminal_scrollbar_render_state render_state;
+
+    /**
+     * Whether the scrollbar handle is currently being dragged.
+     */
+    int dragging_handle;
+
+    /**
+     * The offset of the Y location of the mouse pointer when the dragging
+     * began, relative to the top of the scrollbar handle. If dragging is not
+     * in progress, this value is undefined.
+     */
+    int drag_offset_y;
+
+    /**
+     * The current Y location of the mouse pointer if dragging is in progress.
+     * If dragging is not in progress, this value is undefined.
+     */
+    int drag_current_y;
+
+    /**
+     * The function to call when the scrollbar handle is being dragged, and
+     * the new scrollbar value needs to be handled and assigned.
+     */
+    guac_terminal_scrollbar_scroll_handler* scroll_handler;
+
+    /**
+     * Arbitrary reference to data related to this scrollbar.
+     */
+    void* data;
+
+};
+
+/**
+ * Allocates a new scrollbar, associating that scrollbar with the given client
+ * and parent layer. The dimensions of the parent layer dictate the initial
+ * position of the scrollbar. Currently, the scrollbar is always anchored to
+ * the right edge of the parent layer.
+ *
+ * This will cause instructions to be written to the client's socket, but the
+ * client's socket will not be automatically flushed.
+ *
+ * @param client
+ *     The client to associate with the new scrollbar.
+ *
+ * @param parent
+ *     The layer which will contain the newly-allocated scrollbar.
+ *
+ * @param parent_width
+ *     The width of the parent layer, in pixels.
+ *
+ * @param parent_height
+ *     The height of the parent layer, in pixels.
+ *
+ * @param visible_area
+ *     The amount of scrollable data that can be shown within the parent layer
+ *     at any given time. This value uses the same units as min, max, and the
+ *     current scroll value.
+ *
+ * @return
+ *     A newly allocated scrollbar.
+ */
+guac_terminal_scrollbar* guac_terminal_scrollbar_alloc(guac_client* client,
+        const guac_layer* parent, int parent_width, int parent_height,
+        int visible_area);
+
+/**
+ * Frees the given scrollbar.
+ *
+ * @param scrollbar
+ *     The scrollbar to free.
+ */
+void guac_terminal_scrollbar_free(guac_terminal_scrollbar* scrollbar);
+
+/**
+ * Flushes the render state of the given scrollbar, updating the remote display
+ * accordingly.
+ *
+ * This may cause instructions to be written to the client's socket, but the
+ * client's socket will not be automatically flushed.
+ *
+ * @param scrollbar
+ *     The scrollbar whose render state is to be flushed.
+ */
+void guac_terminal_scrollbar_flush(guac_terminal_scrollbar* scrollbar);
+
+/**
+ * Sets the minimum and maximum allowed scroll values of the given scrollbar
+ * to the given values. If necessary, the current value of the scrollbar will
+ * be adjusted to fit within the new bounds.
+ *
+ * This may cause instructions to be written to the client's socket, but the
+ * client's socket will not be automatically flushed.
+ *
+ * @param scrollbar
+ *     The scrollbar whose bounds are changing.
+ *
+ * @param min
+ *     The new minimum value of the scrollbar.
+ *
+ * @param max
+ *     The new maximum value of the scrollbar.
+ */
+void guac_terminal_scrollbar_set_bounds(guac_terminal_scrollbar* scrollbar,
+        int min, int max);
+
+/**
+ * Sets the current value of the given scrollbar. If the value specified does
+ * not fall within the scrollbar's defined minimum and maximum values, the
+ * value will be adjusted to fit.
+ *
+ * This may cause instructions to be written to the client's socket, but the
+ * client's socket will not be automatically flushed.
+ *
+ * @param scrollbar
+ *     The scrollbar whose value is changing.
+ *
+ * @param value
+ *     The value to assign to the scrollbar. If the value if out of bounds, it
+ *     will be automatically adjusted to fit.
+ */
+void guac_terminal_scrollbar_set_value(guac_terminal_scrollbar* scrollbar,
+        int value);
+
+/**
+ * Notifies the scrollbar that the parent layer has been resized, and that the
+ * scrollbar may need to be repositioned or resized accordingly.
+ *
+ * This may cause instructions to be written to the client's socket, but the
+ * client's socket will not be automatically flushed.
+ *
+ * @param scrollbar
+ *     The scrollbar whose parent layer has been resized.
+ *
+ * @param parent_width
+ *     The new width of the parent layer, in pixels.
+ *
+ * @param parent_height
+ *     The new height of the parent layer, in pixels.
+ *
+ * @param visible_area
+ *     The amount of scrollable data that can be shown within the parent layer
+ *     at any given time. This value uses the same units as min, max, and the
+ *     current scroll value.
+ */
+void guac_terminal_scrollbar_parent_resized(guac_terminal_scrollbar* scrollbar,
+        int parent_width, int parent_height, int visible_area);
+
+/**
+ * Notifies the scrollbar of the current mouse state, allowing it to update
+ * itself with respect to button state and dragging.
+ *
+ * @param scrollbar
+ *     The scrollbar to notify of the current mouse state.
+ *
+ * @param x
+ *     The X coordinate of the mouse pointer.
+ *
+ * @param y
+ *     The Y coordinate of the mouse pointer.
+ *
+ * @param mask
+ *     The button mask, where the Nth bit of the button mask represents the
+ *     pressed state of the Nth mouse button, where button 0 is the left
+ *     mouse button, button 1 is the middle button, etc.
+ *
+ * @return
+ *     Zero if the mouse event was not handled by the scrollbar, non-zero
+ *     otherwise.
+ */
+int guac_terminal_scrollbar_handle_mouse(guac_terminal_scrollbar* scrollbar,
+        int x, int y, int mask);
+
+#endif
diff --git a/src/terminal/terminal.c b/src/terminal/terminal.c
new file mode 100644
index 0000000..cf7d84f
--- /dev/null
+++ b/src/terminal/terminal.c
@@ -0,0 +1,1730 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "buffer.h"
+#include "blank.h"
+#include "common.h"
+#include "cursor.h"
+#include "display.h"
+#include "ibar.h"
+#include "guac_clipboard.h"
+#include "packet.h"
+#include "pointer.h"
+#include "scrollbar.h"
+#include "terminal.h"
+#include "terminal_handlers.h"
+#include "types.h"
+
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <wchar.h>
+
+#include <guacamole/client.h>
+#include <guacamole/error.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+#include <guacamole/timestamp.h>
+
+/**
+ * Sets the given range of columns to the given character.
+ */
+static void __guac_terminal_set_columns(guac_terminal* terminal, int row,
+        int start_column, int end_column, guac_terminal_char* character) {
+
+    guac_terminal_display_set_columns(terminal->display, row + terminal->scroll_offset,
+            start_column, end_column, character);
+
+    guac_terminal_buffer_set_columns(terminal->buffer, row,
+            start_column, end_column, character);
+
+}
+
+/**
+ * Enforces a character break at the given edge, ensuring that the left side
+ * of the edge is the final column of a character, and the right side of the
+ * edge is the initial column of a DIFFERENT character.
+ *
+ * For a character in a column N, the left edge number is N, and the right
+ * edge is N+1.
+ */
+static void __guac_terminal_force_break(guac_terminal* terminal, int row, int edge) {
+
+    guac_terminal_buffer_row* buffer_row = guac_terminal_buffer_get_row(terminal->buffer, row, 0);
+
+    /* Ensure character to left of edge is unbroken */
+    if (edge > 0) {
+
+        int end_column = edge - 1;
+        int start_column = end_column;
+
+        guac_terminal_char* start_char = &(buffer_row->characters[start_column]);
+
+        /* Determine start column */
+        while (start_column > 0 && start_char->value == GUAC_CHAR_CONTINUATION) {
+            start_char--;
+            start_column--;
+        }
+
+        /* Advance to start of broken character if necessary */
+        if (start_char->value != GUAC_CHAR_CONTINUATION && start_char->width < end_column - start_column + 1) {
+            start_column += start_char->width;
+            start_char += start_char->width;
+        }
+
+        /* Clear character if broken */
+        if (start_char->value == GUAC_CHAR_CONTINUATION || start_char->width != end_column - start_column + 1) {
+
+            guac_terminal_char cleared_char;
+            cleared_char.value = ' ';
+            cleared_char.attributes = start_char->attributes;
+            cleared_char.width = 1;
+
+            __guac_terminal_set_columns(terminal, row, start_column, end_column, &cleared_char);
+
+        }
+
+    }
+
+    /* Ensure character to right of edge is unbroken */
+    if (edge >= 0 && edge < buffer_row->length) {
+
+        int start_column = edge;
+        int end_column = start_column;
+
+        guac_terminal_char* start_char = &(buffer_row->characters[start_column]);
+        guac_terminal_char* end_char = &(buffer_row->characters[end_column]);
+
+        /* Determine end column */
+        while (end_column+1 < buffer_row->length && (end_char+1)->value == GUAC_CHAR_CONTINUATION) {
+            end_char++;
+            end_column++;
+        }
+
+        /* Advance to start of broken character if necessary */
+        if (start_char->value != GUAC_CHAR_CONTINUATION && start_char->width < end_column - start_column + 1) {
+            start_column += start_char->width;
+            start_char += start_char->width;
+        }
+
+        /* Clear character if broken */
+        if (start_char->value == GUAC_CHAR_CONTINUATION || start_char->width != end_column - start_column + 1) {
+
+            guac_terminal_char cleared_char;
+            cleared_char.value = ' ';
+            cleared_char.attributes = start_char->attributes;
+            cleared_char.width = 1;
+
+            __guac_terminal_set_columns(terminal, row, start_column, end_column, &cleared_char);
+
+        }
+
+    }
+
+}
+
+void guac_terminal_reset(guac_terminal* term) {
+
+    int row;
+
+    /* Set current state */
+    term->char_handler = guac_terminal_echo; 
+    term->active_char_set = 0;
+    term->char_mapping[0] =
+    term->char_mapping[1] = NULL;
+
+    /* Reset cursor location */
+    term->cursor_row = term->visible_cursor_row = term->saved_cursor_row = 0;
+    term->cursor_col = term->visible_cursor_col = term->saved_cursor_col = 0;
+
+    /* Clear scrollback, buffer, and scroll region */
+    term->buffer->top = 0;
+    term->buffer->length = 0;
+    term->scroll_start = 0;
+    term->scroll_end = term->term_height - 1;
+    term->scroll_offset = 0;
+
+    /* Reset scrollbar bounds */
+    guac_terminal_scrollbar_set_bounds(term->scrollbar, term->term_height - term->buffer->length, 0);
+    guac_terminal_scrollbar_set_value(term->scrollbar, -term->scroll_offset);
+
+    /* Reset flags */
+    term->text_selected = false;
+    term->application_cursor_keys = false;
+    term->automatic_carriage_return = false;
+    term->insert_mode = false;
+
+    /* Reset tabs */
+    term->tab_interval = 8;
+    memset(term->custom_tabs, 0, sizeof(term->custom_tabs));
+
+    /* Clear terminal */
+    for (row=0; row<term->term_height; row++)
+        guac_terminal_set_columns(term, row, 0, term->term_width, &(term->default_char));
+
+}
+
+/**
+ * Paints or repaints the background of the terminal display. This painting
+ * occurs beneath the actual terminal and scrollbar layers, and thus will not
+ * overwrite any text or other content currently on the screen. This is only
+ * necessary to paint over parts of the terminal background which may otherwise
+ * be transparent (the default layer background).
+ *
+ * @param terminal
+ *     The terminal whose background should be painted or repainted.
+ *
+ * @param width
+ *     The width of the background to draw, in pixels.
+ *
+ * @param height
+ *     The height of the background to draw, in pixels.
+ */
+static void guac_terminal_paint_background(guac_terminal* terminal,
+        int width, int height) {
+
+    guac_terminal_display* display = terminal->display;
+    guac_client* client = display->client;
+    guac_socket* socket = client->socket;
+
+    /* Get background color */
+    const guac_terminal_color* color =
+        &guac_terminal_palette[display->default_background];
+
+    /* Paint background color */
+    guac_protocol_send_rect(socket, GUAC_DEFAULT_LAYER, 0, 0, width, height);
+    guac_protocol_send_cfill(socket, GUAC_COMP_OVER, GUAC_DEFAULT_LAYER,
+            color->red, color->green, color->blue, 0xFF);
+
+}
+
+guac_terminal* guac_terminal_create(guac_client* client,
+        const char* font_name, int font_size, int dpi,
+        int width, int height, const char* color_scheme) {
+
+    int default_foreground;
+    int default_background;
+
+    /* Default to "gray-black" color scheme if no scheme provided */
+    if (color_scheme == NULL || color_scheme[0] == '\0') {
+        default_foreground = GUAC_TERMINAL_COLOR_GRAY;
+        default_background = GUAC_TERMINAL_COLOR_BLACK;
+    }
+
+    /* Otherwise, parse color scheme */
+    else if (strcmp(color_scheme, GUAC_TERMINAL_SCHEME_GRAY_BLACK) == 0) {
+        default_foreground = GUAC_TERMINAL_COLOR_GRAY;
+        default_background = GUAC_TERMINAL_COLOR_BLACK;
+    }
+    else if (strcmp(color_scheme, GUAC_TERMINAL_SCHEME_BLACK_WHITE) == 0) {
+        default_foreground = GUAC_TERMINAL_COLOR_BLACK;
+        default_background = GUAC_TERMINAL_COLOR_WHITE;
+    }
+    else if (strcmp(color_scheme, GUAC_TERMINAL_SCHEME_GREEN_BLACK) == 0) {
+        default_foreground = GUAC_TERMINAL_COLOR_DARK_GREEN;
+        default_background = GUAC_TERMINAL_COLOR_BLACK;
+    }
+    else if (strcmp(color_scheme, GUAC_TERMINAL_SCHEME_WHITE_BLACK) == 0) {
+        default_foreground = GUAC_TERMINAL_COLOR_WHITE;
+        default_background = GUAC_TERMINAL_COLOR_BLACK;
+    }
+
+    /* If invalid, default to "gray-black" */
+    else {
+        guac_client_log(client, GUAC_LOG_WARNING,
+                "Invalid color scheme: \"%s\". Defaulting to \"gray-black\".",
+                color_scheme);
+        default_foreground = GUAC_TERMINAL_COLOR_GRAY;
+        default_background = GUAC_TERMINAL_COLOR_BLACK;
+    }
+
+    /* Build default character using default colors */
+    guac_terminal_char default_char = {
+        .value = 0,
+        .attributes = {
+            .foreground = default_foreground,
+            .background = default_background,
+            .bold       = false,
+            .reverse    = false,
+            .underscore = false
+        },
+        .width = 1
+    };
+
+    /* Calculate available display area */
+    int available_width = width - GUAC_TERMINAL_SCROLLBAR_WIDTH;
+    if (available_width < 0)
+        available_width = 0;
+
+    guac_terminal* term = malloc(sizeof(guac_terminal));
+    term->client = client;
+    term->upload_path_handler = NULL;
+    term->file_download_handler = NULL;
+
+    /* Init buffer */
+    term->buffer = guac_terminal_buffer_alloc(1000, &default_char);
+
+    /* Init display */
+    term->display = guac_terminal_display_alloc(client,
+            font_name, font_size, dpi,
+            default_char.attributes.foreground,
+            default_char.attributes.background);
+
+    /* Fail if display init failed */
+    if (term->display == NULL) {
+        guac_client_log(client, GUAC_LOG_DEBUG, "Display initialization failed");
+        free(term);
+        return NULL;
+    }
+
+    /* Init terminal state */
+    term->current_attributes = default_char.attributes;
+    term->default_char = default_char;
+
+    term->term_width   = available_width / term->display->char_width;
+    term->term_height  = height / term->display->char_height;
+
+    /* Open STDOUT pipe */
+    if (pipe(term->stdout_pipe_fd)) {
+        guac_error = GUAC_STATUS_SEE_ERRNO;
+        guac_error_message = "Unable to open pipe for STDOUT";
+        free(term);
+        return NULL;
+    }
+
+    /* Open STDIN pipe */
+    if (pipe(term->stdin_pipe_fd)) {
+        guac_error = GUAC_STATUS_SEE_ERRNO;
+        guac_error_message = "Unable to open pipe for STDIN";
+        free(term);
+        return NULL;
+    }
+
+    /* Init terminal lock */
+    pthread_mutex_init(&(term->lock), NULL);
+
+    /* Size display */
+    guac_protocol_send_size(term->display->client->socket,
+            GUAC_DEFAULT_LAYER, width, height);
+    guac_terminal_paint_background(term, width, height);
+    guac_terminal_display_resize(term->display,
+            term->term_width, term->term_height);
+
+    /* Allocate scrollbar */
+    term->scrollbar = guac_terminal_scrollbar_alloc(term->client,
+            GUAC_DEFAULT_LAYER, width, height, term->term_height);
+
+    /* Associate scrollbar with this terminal */
+    term->scrollbar->data = term;
+    term->scrollbar->scroll_handler = guac_terminal_scroll_handler;
+
+    /* Init terminal */
+    guac_terminal_reset(term);
+
+    term->mod_alt   =
+    term->mod_ctrl  =
+    term->mod_shift = 0;
+
+    /* Set up mouse cursors */
+    term->pointer_cursor = guac_terminal_create_pointer(client);
+    term->ibar_cursor    = guac_terminal_create_ibar(client);
+    term->blank_cursor   = guac_terminal_create_blank(client);
+
+    /* Initialize mouse cursor */
+    term->current_cursor = term->blank_cursor;
+    guac_terminal_set_cursor(term->client, term->current_cursor);
+
+    /* Allocate clipboard */
+    term->clipboard = guac_common_clipboard_alloc(GUAC_TERMINAL_CLIPBOARD_MAX_LENGTH);
+
+    return term;
+
+}
+
+void guac_terminal_free(guac_terminal* term) {
+    
+    /* Close terminal output pipe */
+    close(term->stdout_pipe_fd[1]);
+    close(term->stdout_pipe_fd[0]);
+
+    /* Close user input pipe */
+    close(term->stdin_pipe_fd[1]);
+    close(term->stdin_pipe_fd[0]);
+
+    /* Free display */
+    guac_terminal_display_free(term->display);
+
+    /* Free buffer */
+    guac_terminal_buffer_free(term->buffer);
+
+    /* Free clipboard */
+    guac_common_clipboard_free(term->clipboard);
+
+    /* Free scrollbar */
+    guac_terminal_scrollbar_free(term->scrollbar);
+
+    /* Free cursors */
+    guac_terminal_cursor_free(term->client, term->pointer_cursor);
+    guac_terminal_cursor_free(term->client, term->ibar_cursor);
+    guac_terminal_cursor_free(term->client, term->blank_cursor);
+
+}
+
+/**
+ * Waits for data to become available on the given file descriptor.
+ *
+ * @param fd
+ *    The file descriptor to wait on.
+ *
+ * @param msec_timeout
+ *    The maximum amount of time to wait, in milliseconds.
+ *
+ * @return
+ *    A positive if data is available, zero if the timeout has elapsed without
+ *    data becoming available, or negative if an error occurred.
+ */
+static int guac_terminal_wait_for_data(int fd, int msec_timeout) {
+
+    /* Build fd_set */
+    fd_set fds;
+    FD_ZERO(&fds);
+    FD_SET(fd, &fds);
+
+    /* Split millisecond timeout into seconds and microseconds */
+    struct timeval timeout = {
+        .tv_sec  =  msec_timeout / 1000,
+        .tv_usec = (msec_timeout % 1000) * 1000
+    };
+
+    /* Wait for data */
+    return select(fd+1, &fds, NULL, NULL, &timeout);
+
+}
+
+int guac_terminal_render_frame(guac_terminal* terminal) {
+
+    guac_client* client = terminal->client;
+    char buffer[GUAC_TERMINAL_PACKET_SIZE];
+
+    int wait_result;
+    int fd = terminal->stdout_pipe_fd[0];
+
+    /* Wait for data to be available */
+    wait_result = guac_terminal_wait_for_data(fd, 1000);
+    if (wait_result > 0) {
+
+        guac_terminal_lock(terminal);
+        guac_timestamp frame_start = guac_timestamp_current();
+
+        do {
+
+            guac_timestamp frame_end;
+            int frame_remaining;
+
+            int bytes_read;
+
+            /* Read data, write to terminal */
+            if ((bytes_read = guac_terminal_packet_read(fd,
+                            buffer, sizeof(buffer))) > 0) {
+
+                if (guac_terminal_write(terminal, buffer, bytes_read)) {
+                    guac_client_abort(client,
+                            GUAC_PROTOCOL_STATUS_SERVER_ERROR,
+                            "Error writing data");
+                    guac_terminal_unlock(terminal);
+                    return 1;
+                }
+
+            }
+
+            /* Notify on error */
+            if (bytes_read < 0) {
+                guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
+                        "Error reading data");
+                guac_terminal_unlock(terminal);
+                return 1;
+            }
+
+            /* Calculate time remaining in frame */
+            frame_end = guac_timestamp_current();
+            frame_remaining = frame_start + GUAC_TERMINAL_FRAME_DURATION
+                            - frame_end;
+
+            /* Wait again if frame remaining */
+            if (frame_remaining > 0)
+                wait_result = guac_terminal_wait_for_data(fd,
+                        GUAC_TERMINAL_FRAME_TIMEOUT);
+            else
+                break;
+
+        } while (wait_result > 0);
+
+        /* Flush terminal */
+        guac_terminal_flush(terminal);
+        guac_terminal_unlock(terminal);
+
+    }
+
+    /* Notify of any errors */
+    if (wait_result < 0) {
+        guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
+                "Error waiting for data");
+        return 1;
+    }
+
+    return 0;
+
+}
+
+int guac_terminal_read_stdin(guac_terminal* terminal, char* c, int size) {
+    int stdin_fd = terminal->stdin_pipe_fd[0];
+    return read(stdin_fd, c, size);
+}
+
+int guac_terminal_write_stdout(guac_terminal* terminal, const char* c,
+        int size) {
+
+    /* Write maximally-sized packets until only one packet remains */
+    while (size > GUAC_TERMINAL_PACKET_SIZE) {
+
+        /* Write maximally-sized packet */
+        if (guac_terminal_packet_write(terminal->stdout_pipe_fd[1], c,
+                GUAC_TERMINAL_PACKET_SIZE) < 0)
+            return -1;
+
+        /* Advance to next packet */
+        c    += GUAC_TERMINAL_PACKET_SIZE;
+        size -= GUAC_TERMINAL_PACKET_SIZE;
+
+    }
+
+    return guac_terminal_packet_write(terminal->stdout_pipe_fd[1], c, size);
+}
+
+int guac_terminal_notify(guac_terminal* terminal) {
+    return guac_terminal_packet_write(terminal->stdout_pipe_fd[1], NULL, 0);
+}
+
+int guac_terminal_printf(guac_terminal* terminal, const char* format, ...) {
+
+    int written;
+
+    va_list ap;
+    char buffer[1024];
+
+    /* Print to buffer */
+    va_start(ap, format);
+    written = vsnprintf(buffer, sizeof(buffer)-1, format, ap);
+    va_end(ap);
+
+    if (written < 0)
+        return written;
+
+    /* Write to STDOUT */
+    return guac_terminal_write_stdout(terminal, buffer, written);
+
+}
+
+void guac_terminal_prompt(guac_terminal* terminal, const char* title, char* str, int size, bool echo) {
+
+    int pos;
+    char in_byte;
+
+    /* Print title */
+    guac_terminal_printf(terminal, "%s", title);
+
+    /* Make room for null terminator */
+    size--;
+
+    /* Read bytes until newline */
+    pos = 0;
+    while (pos < size && guac_terminal_read_stdin(terminal, &in_byte, 1) == 1) {
+
+        /* Backspace */
+        if (in_byte == 0x7F) {
+
+            if (pos > 0) {
+                guac_terminal_printf(terminal, "\b \b");
+                pos--;
+            }
+        }
+
+        /* CR (end of input */
+        else if (in_byte == 0x0D) {
+            guac_terminal_printf(terminal, "\r\n");
+            break;
+        }
+
+        else {
+
+            /* Store character, update buffers */
+            str[pos++] = in_byte;
+
+            /* Print character if echoing */
+            if (echo)
+                guac_terminal_printf(terminal, "%c", in_byte);
+            else
+                guac_terminal_printf(terminal, "*");
+
+        }
+
+    }
+
+    /* Terminate string */
+    str[pos] = 0;
+
+}
+
+int guac_terminal_set(guac_terminal* term, int row, int col, int codepoint) {
+
+    int width;
+
+    /* Build character with current attributes */
+    guac_terminal_char guac_char;
+    guac_char.value = codepoint;
+    guac_char.attributes = term->current_attributes;
+
+    width = wcwidth(codepoint);
+    if (width < 0)
+        width = 1;
+
+    guac_char.width = width;
+
+    guac_terminal_set_columns(term, row, col, col + width - 1, &guac_char);
+
+    return 0;
+
+}
+
+void guac_terminal_commit_cursor(guac_terminal* term) {
+
+    guac_terminal_char* guac_char;
+
+    guac_terminal_buffer_row* old_row;
+    guac_terminal_buffer_row* new_row;
+
+    /* If no change, done */
+    if (term->visible_cursor_row == term->cursor_row && term->visible_cursor_col == term->cursor_col)
+        return;
+
+    /* Get old and new rows with cursor */
+    new_row = guac_terminal_buffer_get_row(term->buffer, term->cursor_row, term->cursor_col+1);
+    old_row = guac_terminal_buffer_get_row(term->buffer, term->visible_cursor_row, term->visible_cursor_col+1);
+
+    /* Clear cursor */
+    guac_char = &(old_row->characters[term->visible_cursor_col]);
+    guac_char->attributes.cursor = false;
+    guac_terminal_display_set_columns(term->display, term->visible_cursor_row + term->scroll_offset,
+            term->visible_cursor_col, term->visible_cursor_col, guac_char);
+
+    /* Set cursor */
+    guac_char = &(new_row->characters[term->cursor_col]);
+    guac_char->attributes.cursor = true;
+    guac_terminal_display_set_columns(term->display, term->cursor_row + term->scroll_offset,
+            term->cursor_col, term->cursor_col, guac_char);
+
+    term->visible_cursor_row = term->cursor_row;
+    term->visible_cursor_col = term->cursor_col;
+
+    return;
+
+}
+
+int guac_terminal_write(guac_terminal* term, const char* c, int size) {
+
+    while (size > 0) {
+        term->char_handler(term, *(c++));
+        size--;
+    }
+
+    return 0;
+
+}
+
+int guac_terminal_scroll_up(guac_terminal* term,
+        int start_row, int end_row, int amount) {
+
+    /* If scrolling entire display, update scroll offset */
+    if (start_row == 0 && end_row == term->term_height - 1) {
+
+        /* Scroll up visibly */
+        guac_terminal_display_copy_rows(term->display, start_row + amount, end_row, -amount);
+
+        /* Advance by scroll amount */
+        term->buffer->top += amount;
+        if (term->buffer->top >= term->buffer->available)
+            term->buffer->top -= term->buffer->available;
+
+        term->buffer->length += amount;
+        if (term->buffer->length > term->buffer->available)
+            term->buffer->length = term->buffer->available;
+
+        /* Reset scrollbar bounds */
+        guac_terminal_scrollbar_set_bounds(term->scrollbar, term->term_height - term->buffer->length, 0);
+
+        /* Update cursor location if within region */
+        if (term->visible_cursor_row >= start_row &&
+            term->visible_cursor_row <= end_row)
+            term->visible_cursor_row -= amount;
+
+    }
+
+    /* Otherwise, just copy row data upwards */
+    else
+        guac_terminal_copy_rows(term, start_row + amount, end_row, -amount);
+
+    /* Clear new area */
+    guac_terminal_clear_range(term,
+            end_row - amount + 1, 0,
+            end_row, term->term_width - 1);
+
+    return 0;
+}
+
+int guac_terminal_scroll_down(guac_terminal* term,
+        int start_row, int end_row, int amount) {
+
+    guac_terminal_copy_rows(term, start_row, end_row - amount, amount);
+
+    /* Clear new area */
+    guac_terminal_clear_range(term,
+            start_row, 0,
+            start_row + amount - 1, term->term_width - 1);
+
+    return 0;
+}
+
+int guac_terminal_clear_columns(guac_terminal* term,
+        int row, int start_col, int end_col) {
+
+    /* Build space */
+    guac_terminal_char blank;
+    blank.value = 0;
+    blank.attributes = term->current_attributes;
+    blank.width = 1;
+
+    /* Clear */
+    guac_terminal_set_columns(term,
+        row, start_col, end_col, &blank);
+
+    return 0;
+
+}
+
+int guac_terminal_clear_range(guac_terminal* term,
+        int start_row, int start_col,
+        int end_row, int end_col) {
+
+    /* If not at far left, must clear sub-region to far right */
+    if (start_col > 0) {
+
+        /* Clear from start_col to far right */
+        guac_terminal_clear_columns(term,
+            start_row, start_col, term->term_width - 1);
+
+        /* One less row to clear */
+        start_row++;
+    }
+
+    /* If not at far right, must clear sub-region to far left */
+    if (end_col < term->term_width - 1) {
+
+        /* Clear from far left to end_col */
+        guac_terminal_clear_columns(term, end_row, 0, end_col);
+
+        /* One less row to clear */
+        end_row--;
+
+    }
+
+    /* Remaining region now guaranteed rectangular. Clear, if possible */
+    if (start_row <= end_row) {
+
+        int row;
+        for (row=start_row; row<=end_row; row++) {
+
+            /* Clear entire row */
+            guac_terminal_clear_columns(term, row, 0, term->term_width - 1);
+
+        }
+
+    }
+
+    return 0;
+
+}
+
+void guac_terminal_scroll_display_down(guac_terminal* terminal,
+        int scroll_amount) {
+
+    int start_row, end_row;
+    int dest_row;
+    int row, column;
+
+    /* Limit scroll amount by size of scrollback buffer */
+    if (scroll_amount > terminal->scroll_offset)
+        scroll_amount = terminal->scroll_offset;
+
+    /* If not scrolling at all, don't bother trying */
+    if (scroll_amount <= 0)
+        return;
+
+    /* Shift screen up */
+    if (terminal->term_height > scroll_amount)
+        guac_terminal_display_copy_rows(terminal->display,
+                scroll_amount, terminal->term_height - 1,
+                -scroll_amount);
+
+    /* Advance by scroll amount */
+    terminal->scroll_offset -= scroll_amount;
+    guac_terminal_scrollbar_set_value(terminal->scrollbar, -terminal->scroll_offset);
+
+    /* Get row range */
+    end_row   = terminal->term_height - terminal->scroll_offset - 1;
+    start_row = end_row - scroll_amount + 1;
+    dest_row  = terminal->term_height - scroll_amount;
+
+    /* Draw new rows from scrollback */
+    for (row=start_row; row<=end_row; row++) {
+
+        /* Get row from scrollback */
+        guac_terminal_buffer_row* buffer_row =
+            guac_terminal_buffer_get_row(terminal->buffer, row, 0);
+
+        /* Clear row */
+        guac_terminal_display_set_columns(terminal->display,
+                dest_row, 0, terminal->display->width, &(terminal->default_char));
+
+        /* Draw row */
+        guac_terminal_char* current = buffer_row->characters;
+        for (column=0; column<buffer_row->length; column++) {
+
+            /* Only draw if not blank */
+            if (guac_terminal_has_glyph(current->value))
+                guac_terminal_display_set_columns(terminal->display, dest_row, column, column, current);
+
+            current++;
+
+        }
+
+        /* Next row */
+        dest_row++;
+
+    }
+
+    guac_terminal_notify(terminal);
+
+}
+
+void guac_terminal_scroll_display_up(guac_terminal* terminal,
+        int scroll_amount) {
+
+    int start_row, end_row;
+    int dest_row;
+    int row, column;
+
+    /* Limit scroll amount by size of scrollback buffer */
+    if (terminal->scroll_offset + scroll_amount > terminal->buffer->length - terminal->term_height)
+        scroll_amount = terminal->buffer->length - terminal->scroll_offset - terminal->term_height;
+
+    /* If not scrolling at all, don't bother trying */
+    if (scroll_amount <= 0)
+        return;
+
+    /* Shift screen down */
+    if (terminal->term_height > scroll_amount)
+        guac_terminal_display_copy_rows(terminal->display,
+                0, terminal->term_height - scroll_amount - 1,
+                scroll_amount);
+
+    /* Advance by scroll amount */
+    terminal->scroll_offset += scroll_amount;
+    guac_terminal_scrollbar_set_value(terminal->scrollbar, -terminal->scroll_offset);
+
+    /* Get row range */
+    start_row = -terminal->scroll_offset;
+    end_row   = start_row + scroll_amount - 1;
+    dest_row  = 0;
+
+    /* Draw new rows from scrollback */
+    for (row=start_row; row<=end_row; row++) {
+
+        /* Get row from scrollback */
+        guac_terminal_buffer_row* buffer_row = 
+            guac_terminal_buffer_get_row(terminal->buffer, row, 0);
+
+        /* Clear row */
+        guac_terminal_display_set_columns(terminal->display,
+                dest_row, 0, terminal->display->width, &(terminal->default_char));
+
+        /* Draw row */
+        guac_terminal_char* current = buffer_row->characters;
+        for (column=0; column<buffer_row->length; column++) {
+
+            /* Only draw if not blank */
+            if (guac_terminal_has_glyph(current->value))
+                guac_terminal_display_set_columns(terminal->display, dest_row, column, column, current);
+
+            current++;
+
+        }
+
+        /* Next row */
+        dest_row++;
+
+    }
+
+    guac_terminal_notify(terminal);
+
+}
+
+void guac_terminal_select_redraw(guac_terminal* terminal) {
+
+    int start_row = terminal->selection_start_row + terminal->scroll_offset;
+    int start_column = terminal->selection_start_column;
+
+    int end_row = terminal->selection_end_row + terminal->scroll_offset;
+    int end_column = terminal->selection_end_column;
+
+    /* Update start/end columns to include character width */
+    if (start_row > end_row || (start_row == end_row && start_column > end_column))
+        start_column += terminal->selection_start_width - 1;
+    else
+        end_column += terminal->selection_end_width - 1;
+
+    guac_terminal_display_select(terminal->display, start_row, start_column, end_row, end_column);
+
+}
+
+/**
+ * Locates the beginning of the character at the given row and column, updating
+ * the column to the starting column of that character. The width, if available,
+ * is returned. If the character has no defined width, 1 is returned.
+ */
+static int __guac_terminal_find_char(guac_terminal* terminal, int row, int* column) {
+
+    int start_column = *column;
+
+    guac_terminal_buffer_row* buffer_row = guac_terminal_buffer_get_row(terminal->buffer, row, 0);
+    if (start_column < buffer_row->length) {
+
+        /* Find beginning of character */
+        guac_terminal_char* start_char = &(buffer_row->characters[start_column]);
+        while (start_column > 0 && start_char->value == GUAC_CHAR_CONTINUATION) {
+            start_char--;
+            start_column--;
+        }
+
+        /* Use width, if available */
+        if (start_char->value != GUAC_CHAR_CONTINUATION) {
+            *column = start_column;
+            return start_char->width;
+        }
+
+    }
+
+    /* Default to one column wide */
+    return 1;
+
+}
+
+void guac_terminal_select_start(guac_terminal* terminal, int row, int column) {
+
+    int width = __guac_terminal_find_char(terminal, row, &column);
+
+    terminal->selection_start_row = 
+    terminal->selection_end_row   = row;
+
+    terminal->selection_start_column = 
+    terminal->selection_end_column   = column;
+
+    terminal->selection_start_width = 
+    terminal->selection_end_width   = width;
+
+    terminal->text_selected = true;
+
+    guac_terminal_select_redraw(terminal);
+
+}
+
+void guac_terminal_select_update(guac_terminal* terminal, int row, int column) {
+
+    /* Only update if selection has changed */
+    if (row != terminal->selection_end_row
+        || column <  terminal->selection_end_column
+        || column >= terminal->selection_end_column + terminal->selection_end_width) {
+
+        int width = __guac_terminal_find_char(terminal, row, &column);
+
+        terminal->selection_end_row = row;
+        terminal->selection_end_column = column;
+        terminal->selection_end_width = width;
+
+        guac_terminal_select_redraw(terminal);
+    }
+
+}
+
+int __guac_terminal_buffer_string(guac_terminal_buffer_row* row, int start, int end, char* string) {
+
+    int length = 0;
+    int i;
+    for (i=start; i<=end; i++) {
+
+        int codepoint = row->characters[i].value;
+
+        /* If not null (blank), add to string */
+        if (codepoint != 0 && codepoint != GUAC_CHAR_CONTINUATION) {
+            int bytes = guac_terminal_encode_utf8(codepoint, string);
+            string += bytes;
+            length += bytes;
+        }
+
+    }
+
+    return length;
+
+}
+
+void guac_terminal_select_end(guac_terminal* terminal, char* string) {
+
+    /* Deselect */
+    terminal->text_selected = false;
+    guac_terminal_display_commit_select(terminal->display);
+
+    guac_terminal_buffer_row* buffer_row;
+
+    int row;
+
+    int start_row, start_col;
+    int end_row, end_col;
+
+    /* Ensure proper ordering of start and end coords */
+    if (terminal->selection_start_row < terminal->selection_end_row
+        || (terminal->selection_start_row == terminal->selection_end_row
+            && terminal->selection_start_column < terminal->selection_end_column)) {
+
+        start_row = terminal->selection_start_row;
+        start_col = terminal->selection_start_column;
+        end_row   = terminal->selection_end_row;
+        end_col   = terminal->selection_end_column + terminal->selection_end_width - 1;
+
+    }
+    else {
+        end_row   = terminal->selection_start_row;
+        end_col   = terminal->selection_start_column + terminal->selection_start_width - 1;
+        start_row = terminal->selection_end_row;
+        start_col = terminal->selection_end_column;
+    }
+
+    /* If only one row, simply copy */
+    buffer_row = guac_terminal_buffer_get_row(terminal->buffer, start_row, 0);
+    if (end_row == start_row) {
+        if (buffer_row->length - 1 < end_col)
+            end_col = buffer_row->length - 1;
+        string += __guac_terminal_buffer_string(buffer_row, start_col, end_col, string);
+    }
+
+    /* Otherwise, copy multiple rows */
+    else {
+
+        /* Store first row */
+        string += __guac_terminal_buffer_string(buffer_row, start_col, buffer_row->length - 1, string);
+
+        /* Store all middle rows */
+        for (row=start_row+1; row<end_row; row++) {
+
+            buffer_row = guac_terminal_buffer_get_row(terminal->buffer, row, 0);
+
+            *(string++) = '\n';
+            string += __guac_terminal_buffer_string(buffer_row, 0, buffer_row->length - 1, string);
+
+        }
+
+        /* Store last row */
+        buffer_row = guac_terminal_buffer_get_row(terminal->buffer, end_row, 0);
+        if (buffer_row->length - 1 < end_col)
+            end_col = buffer_row->length - 1;
+
+        *(string++) = '\n';
+        string += __guac_terminal_buffer_string(buffer_row, 0, end_col, string);
+
+    }
+
+    /* Null terminator */
+    *string = 0;
+
+}
+
+void guac_terminal_copy_columns(guac_terminal* terminal, int row,
+        int start_column, int end_column, int offset) {
+
+    guac_terminal_display_copy_columns(terminal->display, row + terminal->scroll_offset,
+            start_column, end_column, offset);
+
+    guac_terminal_buffer_copy_columns(terminal->buffer, row,
+            start_column, end_column, offset);
+
+    /* Update cursor location if within region */
+    if (row == terminal->visible_cursor_row &&
+            terminal->visible_cursor_col >= start_column &&
+            terminal->visible_cursor_col <= end_column)
+        terminal->visible_cursor_col += offset;
+
+    /* Force breaks around destination region */
+    __guac_terminal_force_break(terminal, row, start_column + offset);
+    __guac_terminal_force_break(terminal, row, end_column + offset + 1);
+
+}
+
+void guac_terminal_copy_rows(guac_terminal* terminal,
+        int start_row, int end_row, int offset) {
+
+    guac_terminal_display_copy_rows(terminal->display,
+            start_row + terminal->scroll_offset, end_row + terminal->scroll_offset, offset);
+
+    guac_terminal_buffer_copy_rows(terminal->buffer,
+            start_row, end_row, offset);
+
+    /* Update cursor location if within region */
+    if (terminal->visible_cursor_row >= start_row &&
+        terminal->visible_cursor_row <= end_row)
+        terminal->visible_cursor_row += offset;
+
+}
+
+void guac_terminal_set_columns(guac_terminal* terminal, int row,
+        int start_column, int end_column, guac_terminal_char* character) {
+
+    __guac_terminal_set_columns(terminal, row, start_column, end_column, character);
+
+    /* If visible cursor in current row, preserve state */
+    if (row == terminal->visible_cursor_row
+            && terminal->visible_cursor_col >= start_column
+            && terminal->visible_cursor_col <= end_column) {
+
+        /* Create copy of character with cursor attribute set */
+        guac_terminal_char cursor_character = *character;
+        cursor_character.attributes.cursor = true;
+
+        __guac_terminal_set_columns(terminal, row,
+                terminal->visible_cursor_col, terminal->visible_cursor_col, &cursor_character);
+
+    }
+
+    /* Force breaks around destination region */
+    __guac_terminal_force_break(terminal, row, start_column);
+    __guac_terminal_force_break(terminal, row, end_column + 1);
+
+}
+
+static void __guac_terminal_redraw_rect(guac_terminal* term, int start_row, int start_col, int end_row, int end_col) {
+
+    int row, col;
+
+    /* Redraw region */
+    for (row=start_row; row<=end_row; row++) {
+
+        guac_terminal_buffer_row* buffer_row =
+            guac_terminal_buffer_get_row(term->buffer, row - term->scroll_offset, 0);
+
+        /* Clear row */
+        guac_terminal_display_set_columns(term->display,
+                row, start_col, end_col, &(term->default_char));
+
+        /* Copy characters */
+        for (col=start_col; col <= end_col && col < buffer_row->length; col++) {
+
+            /* Only redraw if not blank */
+            guac_terminal_char* c = &(buffer_row->characters[col]);
+            if (guac_terminal_has_glyph(c->value))
+                guac_terminal_display_set_columns(term->display, row, col, col, c);
+
+        }
+
+    }
+
+}
+
+/**
+ * Internal terminal resize routine. Accepts width/height in CHARACTERS
+ * (not pixels like the public function).
+ */
+static void __guac_terminal_resize(guac_terminal* term, int width, int height) {
+
+    /* If height is decreasing, shift display up */
+    if (height < term->term_height) {
+
+        int shift_amount;
+
+        /* Get number of rows actually occupying terminal space */
+        int used_height = term->buffer->length;
+        if (used_height > term->term_height)
+            used_height = term->term_height;
+
+        shift_amount = used_height - height;
+
+        /* If the new terminal bottom covers N rows, shift up N rows */
+        if (shift_amount > 0) {
+
+            guac_terminal_display_copy_rows(term->display,
+                    shift_amount, term->display->height - 1, -shift_amount);
+
+            /* Update buffer top and cursor row based on shift */
+            term->buffer->top += shift_amount;
+            term->cursor_row  -= shift_amount;
+            term->visible_cursor_row  -= shift_amount;
+
+            /* Redraw characters within old region */
+            __guac_terminal_redraw_rect(term, height - shift_amount, 0, height-1, width-1);
+
+        }
+
+    }
+
+    /* Resize display */
+    guac_terminal_display_flush(term->display);
+    guac_terminal_display_resize(term->display, width, height);
+
+    /* Reraw any characters on right if widening */
+    if (width > term->term_width)
+        __guac_terminal_redraw_rect(term, 0, term->term_width-1, height-1, width-1);
+
+    /* If height is increasing, shift display down */
+    if (height > term->term_height) {
+
+        /* If undisplayed rows exist in the buffer, shift them into view */
+        if (term->term_height < term->buffer->length) {
+
+            /* If the new terminal bottom reveals N rows, shift down N rows */
+            int shift_amount = height - term->term_height;
+
+            /* The maximum amount we can shift is the number of undisplayed rows */
+            int max_shift = term->buffer->length - term->term_height;
+
+            if (shift_amount > max_shift)
+                shift_amount = max_shift;
+
+            /* Update buffer top and cursor row based on shift */
+            term->buffer->top -= shift_amount;
+            term->cursor_row  += shift_amount;
+            term->visible_cursor_row  += shift_amount;
+
+            /* If scrolled enough, use scroll to fulfill entire resize */
+            if (term->scroll_offset >= shift_amount) {
+
+                term->scroll_offset -= shift_amount;
+                guac_terminal_scrollbar_set_value(term->scrollbar, -term->scroll_offset);
+
+                /* Draw characters from scroll at bottom */
+                __guac_terminal_redraw_rect(term, term->term_height, 0, term->term_height + shift_amount - 1, width-1);
+
+            }
+
+            /* Otherwise, fulfill with as much scroll as possible */
+            else {
+
+                /* Draw characters from scroll at bottom */
+                __guac_terminal_redraw_rect(term, term->term_height, 0, term->term_height + term->scroll_offset - 1, width-1);
+
+                /* Update shift_amount and scroll based on new rows */
+                shift_amount -= term->scroll_offset;
+                term->scroll_offset = 0;
+                guac_terminal_scrollbar_set_value(term->scrollbar, -term->scroll_offset);
+
+                /* If anything remains, move screen as necessary */
+                if (shift_amount > 0) {
+
+                    guac_terminal_display_copy_rows(term->display,
+                            0, term->display->height - shift_amount - 1, shift_amount);
+
+                    /* Draw characters at top from scroll */
+                    __guac_terminal_redraw_rect(term, 0, 0, shift_amount - 1, width-1);
+
+                }
+
+            }
+
+        } /* end if undisplayed rows exist */
+
+    }
+
+    /* Keep cursor on screen */
+    if (term->cursor_row < 0)       term->cursor_row = 0;
+    if (term->cursor_row >= height) term->cursor_row = height-1;
+    if (term->cursor_col < 0)       term->cursor_col = 0;
+    if (term->cursor_col >= width)  term->cursor_col = width-1;
+
+    /* Commit new dimensions */
+    term->term_width = width;
+    term->term_height = height;
+
+}
+
+int guac_terminal_resize(guac_terminal* terminal, int width, int height) {
+
+    guac_terminal_display* display = terminal->display;
+    guac_client* client = display->client;
+    guac_socket* socket = client->socket;
+
+    /* Acquire exclusive access to terminal */
+    guac_terminal_lock(terminal);
+
+    /* Calculate available display area */
+    int available_width = width - GUAC_TERMINAL_SCROLLBAR_WIDTH;
+    if (available_width < 0)
+        available_width = 0;
+
+    /* Calculate dimensions */
+    int rows    = height / display->char_height;
+    int columns = available_width / display->char_width;
+
+    /* Resize default layer to given pixel dimensions */
+    guac_protocol_send_size(socket, GUAC_DEFAULT_LAYER, width, height);
+    guac_terminal_paint_background(terminal, width, height);
+
+    /* Notify scrollbar of resize */
+    guac_terminal_scrollbar_parent_resized(terminal->scrollbar, width, height, rows);
+    guac_terminal_scrollbar_set_bounds(terminal->scrollbar, rows - terminal->buffer->length, 0);
+
+    /* Resize terminal if row/column dimensions have changed */
+    if (columns != terminal->term_width || rows != terminal->term_height) {
+
+        guac_client_log(client, GUAC_LOG_DEBUG,
+                "Resizing terminal to %ix%i", rows, columns);
+
+        /* Resize terminal */
+        __guac_terminal_resize(terminal, columns, rows);
+
+        /* Reset scroll region */
+        terminal->scroll_end = rows - 1;
+
+    }
+
+    /* Release terminal */
+    guac_terminal_unlock(terminal);
+
+    guac_terminal_notify(terminal);
+    return 0;
+
+}
+
+void guac_terminal_flush(guac_terminal* terminal) {
+    guac_terminal_commit_cursor(terminal);
+    guac_terminal_display_flush(terminal->display);
+    guac_terminal_scrollbar_flush(terminal->scrollbar);
+}
+
+void guac_terminal_lock(guac_terminal* terminal) {
+    pthread_mutex_lock(&(terminal->lock));
+}
+
+void guac_terminal_unlock(guac_terminal* terminal) {
+    pthread_mutex_unlock(&(terminal->lock));
+}
+
+int guac_terminal_send_data(guac_terminal* term, const char* data, int length) {
+    return guac_terminal_write_all(term->stdin_pipe_fd[1], data, length);
+}
+
+int guac_terminal_send_string(guac_terminal* term, const char* data) {
+    return guac_terminal_write_all(term->stdin_pipe_fd[1], data, strlen(data));
+}
+
+static int __guac_terminal_send_key(guac_terminal* term, int keysym, int pressed) {
+
+    /* Hide mouse cursor if not already hidden */
+    if (term->current_cursor != term->blank_cursor) {
+        term->current_cursor = term->blank_cursor;
+        guac_terminal_set_cursor(term->client, term->blank_cursor);
+        guac_terminal_notify(term);
+    }
+
+    /* Track modifiers */
+    if (keysym == 0xFFE3)
+        term->mod_ctrl = pressed;
+    else if (keysym == 0xFFE9)
+        term->mod_alt = pressed;
+    else if (keysym == 0xFFE1)
+        term->mod_shift = pressed;
+        
+    /* If key pressed */
+    else if (pressed) {
+
+        /* Ctrl+Shift+V shortcut for paste */
+        if (keysym == 'V' && term->mod_ctrl)
+            return guac_terminal_send_data(term, term->clipboard->buffer, term->clipboard->length);
+
+        /* Shift+PgUp / Shift+PgDown shortcuts for scrolling */
+        if (term->mod_shift) {
+
+            /* Page up */
+            if (keysym == 0xFF55) {
+                guac_terminal_scroll_display_up(term, term->term_height);
+                return 0;
+            }
+
+            /* Page down */
+            if (keysym == 0xFF56) {
+                guac_terminal_scroll_display_down(term, term->term_height);
+                return 0;
+            }
+
+        }
+
+        /* Reset scroll */
+        if (term->scroll_offset != 0)
+            guac_terminal_scroll_display_down(term, term->scroll_offset);
+
+        /* If alt being held, also send escape character */
+        if (term->mod_alt)
+            return guac_terminal_send_string(term, "\x1B");
+
+        /* Translate Ctrl+letter to control code */ 
+        if (term->mod_ctrl) {
+
+            char data;
+
+            /* Keysyms for '@' through '_' are all conveniently in C0 order */
+            if (keysym >= '@' && keysym <= '_')
+                data = (char) (keysym - '@');
+
+            /* Handle lowercase as well */
+            else if (keysym >= 'a' && keysym <= 'z')
+                data = (char) (keysym - 'a' + 1);
+
+            /* Ctrl+? is DEL (0x7f) */
+            else if (keysym == '?')
+                data = 0x7F;
+
+            /* Map Ctrl+2 to same result as Ctrl+@ */
+            else if (keysym == '2')
+                data = 0x00;
+
+            /* Map Ctrl+3 through Ctrl-7 to the remaining C0 characters such that Ctrl+6 is the same as Ctrl+^ */
+            else if (keysym >= '3' && keysym <= '7')
+                data = (char) (keysym - '3' + 0x1B);
+
+            /* Otherwise ignore */
+            else
+                return 0;
+
+            return guac_terminal_send_data(term, &data, 1);
+
+        }
+
+        /* Translate Unicode to UTF-8 */
+        else if ((keysym >= 0x00 && keysym <= 0xFF) || ((keysym & 0xFFFF0000) == 0x01000000)) {
+
+            int length;
+            char data[5];
+
+            length = guac_terminal_encode_utf8(keysym & 0xFFFF, data);
+            return guac_terminal_send_data(term, data, length);
+
+        }
+
+        /* Non-printable keys */
+        else {
+
+            if (keysym == 0xFF08) return guac_terminal_send_string(term, "\x7F"); /* Backspace */
+            if (keysym == 0xFF09) return guac_terminal_send_string(term, "\x09"); /* Tab */
+            if (keysym == 0xFF0D) return guac_terminal_send_string(term, "\x0D"); /* Enter */
+            if (keysym == 0xFF1B) return guac_terminal_send_string(term, "\x1B"); /* Esc */
+
+            if (keysym == 0xFF50) return guac_terminal_send_string(term, "\x1B[1~"); /* Home */
+
+            /* Arrow keys w/ application cursor */
+            if (term->application_cursor_keys) {
+                if (keysym == 0xFF51) return guac_terminal_send_string(term, "\x1BOD"); /* Left */
+                if (keysym == 0xFF52) return guac_terminal_send_string(term, "\x1BOA"); /* Up */
+                if (keysym == 0xFF53) return guac_terminal_send_string(term, "\x1BOC"); /* Right */
+                if (keysym == 0xFF54) return guac_terminal_send_string(term, "\x1BOB"); /* Down */
+            }
+            else {
+                if (keysym == 0xFF51) return guac_terminal_send_string(term, "\x1B[D"); /* Left */
+                if (keysym == 0xFF52) return guac_terminal_send_string(term, "\x1B[A"); /* Up */
+                if (keysym == 0xFF53) return guac_terminal_send_string(term, "\x1B[C"); /* Right */
+                if (keysym == 0xFF54) return guac_terminal_send_string(term, "\x1B[B"); /* Down */
+            }
+
+            if (keysym == 0xFF55) return guac_terminal_send_string(term, "\x1B[5~"); /* Page up */
+            if (keysym == 0xFF56) return guac_terminal_send_string(term, "\x1B[6~"); /* Page down */
+            if (keysym == 0xFF57) return guac_terminal_send_string(term, "\x1B[4~"); /* End */
+
+            if (keysym == 0xFF63) return guac_terminal_send_string(term, "\x1B[2~"); /* Insert */
+
+            if (keysym == 0xFFBE) return guac_terminal_send_string(term, "\x1B[[A"); /* F1  */
+            if (keysym == 0xFFBF) return guac_terminal_send_string(term, "\x1B[[B"); /* F2  */
+            if (keysym == 0xFFC0) return guac_terminal_send_string(term, "\x1B[[C"); /* F3  */
+            if (keysym == 0xFFC1) return guac_terminal_send_string(term, "\x1B[[D"); /* F4  */
+            if (keysym == 0xFFC2) return guac_terminal_send_string(term, "\x1B[[E"); /* F5  */
+
+            if (keysym == 0xFFC3) return guac_terminal_send_string(term, "\x1B[17~"); /* F6  */
+            if (keysym == 0xFFC4) return guac_terminal_send_string(term, "\x1B[18~"); /* F7  */
+            if (keysym == 0xFFC5) return guac_terminal_send_string(term, "\x1B[19~"); /* F8  */
+            if (keysym == 0xFFC6) return guac_terminal_send_string(term, "\x1B[20~"); /* F9  */
+            if (keysym == 0xFFC7) return guac_terminal_send_string(term, "\x1B[21~"); /* F10 */
+            if (keysym == 0xFFC8) return guac_terminal_send_string(term, "\x1B[22~"); /* F11 */
+            if (keysym == 0xFFC9) return guac_terminal_send_string(term, "\x1B[23~"); /* F12 */
+
+            if (keysym == 0xFFFF) return guac_terminal_send_string(term, "\x1B[3~"); /* Delete */
+
+            /* Ignore unknown keys */
+            guac_client_log(term->client, GUAC_LOG_DEBUG,
+                    "Ignoring unknown keysym: 0x%X", keysym);
+        }
+
+    }
+
+    return 0;
+
+}
+
+int guac_terminal_send_key(guac_terminal* term, int keysym, int pressed) {
+
+    int result;
+
+    guac_terminal_lock(term);
+    result = __guac_terminal_send_key(term, keysym, pressed);
+    guac_terminal_unlock(term);
+
+    return result;
+
+}
+
+static int __guac_terminal_send_mouse(guac_terminal* term, int x, int y, int mask) {
+
+    guac_client* client = term->client;
+    guac_socket* socket = client->socket;
+
+    /* Determine which buttons were just released and pressed */
+    int released_mask =  term->mouse_mask & ~mask;
+    int pressed_mask  = ~term->mouse_mask &  mask;
+
+    /* Notify scrollbar, do not handle anything handled by scrollbar */
+    if (guac_terminal_scrollbar_handle_mouse(term->scrollbar, x, y, mask)) {
+
+        /* Set pointer cursor if mouse is over scrollbar */
+        if (term->current_cursor != term->pointer_cursor) {
+            term->current_cursor = term->pointer_cursor;
+            guac_terminal_set_cursor(client, term->pointer_cursor);
+        }
+
+        guac_terminal_notify(term);
+        return 0;
+
+    }
+
+    term->mouse_mask = mask;
+
+    /* Show mouse cursor if not already shown */
+    if (term->current_cursor != term->ibar_cursor) {
+        term->current_cursor = term->ibar_cursor;
+        guac_terminal_set_cursor(client, term->ibar_cursor);
+        guac_terminal_notify(term);
+    }
+
+    /* Paste contents of clipboard on right or middle mouse button up */
+    if ((released_mask & GUAC_CLIENT_MOUSE_RIGHT) || (released_mask & GUAC_CLIENT_MOUSE_MIDDLE))
+        return guac_terminal_send_data(term, term->clipboard->buffer, term->clipboard->length);
+
+    /* If text selected, change state based on left mouse mouse button */
+    if (term->text_selected) {
+
+        /* If mouse button released, stop selection */
+        if (released_mask & GUAC_CLIENT_MOUSE_LEFT) {
+
+            int selected_length;
+
+            /* End selection and get selected text */
+            int selectable_size = term->term_width * term->term_height * sizeof(char);
+            char* string = malloc(selectable_size);
+            guac_terminal_select_end(term, string);
+
+            selected_length = strnlen(string, selectable_size);
+
+            /* Store new data */
+            guac_common_clipboard_reset(term->clipboard, "text/plain");
+            guac_common_clipboard_append(term->clipboard, string, selected_length);
+            free(string);
+
+            /* Send data */
+            guac_common_clipboard_send(term->clipboard, client);
+            guac_socket_flush(socket);
+
+        }
+
+        /* Otherwise, just update */
+        else
+            guac_terminal_select_update(term,
+                    y / term->display->char_height - term->scroll_offset,
+                    x / term->display->char_width);
+
+    }
+
+    /* Otherwise, if mouse button pressed AND moved, start selection */
+    else if (!(pressed_mask & GUAC_CLIENT_MOUSE_LEFT) &&
+               mask         & GUAC_CLIENT_MOUSE_LEFT)
+        guac_terminal_select_start(term,
+                y / term->display->char_height - term->scroll_offset,
+                x / term->display->char_width);
+
+    /* Scroll up if wheel moved up */
+    if (released_mask & GUAC_CLIENT_MOUSE_SCROLL_UP)
+        guac_terminal_scroll_display_up(term, GUAC_TERMINAL_WHEEL_SCROLL_AMOUNT);
+
+    /* Scroll down if wheel moved down */
+    if (released_mask & GUAC_CLIENT_MOUSE_SCROLL_DOWN)
+        guac_terminal_scroll_display_down(term, GUAC_TERMINAL_WHEEL_SCROLL_AMOUNT);
+
+    return 0;
+
+}
+
+int guac_terminal_send_mouse(guac_terminal* term, int x, int y, int mask) {
+
+    int result;
+
+    guac_terminal_lock(term);
+    result = __guac_terminal_send_mouse(term, x, y, mask);
+    guac_terminal_unlock(term);
+
+    return result;
+
+}
+
+void guac_terminal_scroll_handler(guac_terminal_scrollbar* scrollbar, int value) {
+
+    guac_terminal* terminal = (guac_terminal*) scrollbar->data;
+
+    /* Calculate change in scroll offset */
+    int delta = -value - terminal->scroll_offset;
+
+    /* Update terminal based on change in scroll offset */
+    if (delta < 0)
+        guac_terminal_scroll_display_down(terminal, -delta);
+    else if (delta > 0)
+        guac_terminal_scroll_display_up(terminal, delta);
+
+    /* Update scrollbar value */
+    guac_terminal_scrollbar_set_value(scrollbar, value);
+
+}
+
+void guac_terminal_clipboard_reset(guac_terminal* term, const char* mimetype) {
+    guac_common_clipboard_reset(term->clipboard, mimetype);
+}
+
+void guac_terminal_clipboard_append(guac_terminal* term, const void* data, int length) {
+    guac_common_clipboard_append(term->clipboard, data, length);
+}
+
+int guac_terminal_sendf(guac_terminal* term, const char* format, ...) {
+
+    int written;
+
+    va_list ap;
+    char buffer[1024];
+
+    /* Print to buffer */
+    va_start(ap, format);
+    written = vsnprintf(buffer, sizeof(buffer)-1, format, ap);
+    va_end(ap);
+
+    if (written < 0)
+        return written;
+
+    /* Write to STDIN */
+    return guac_terminal_write_all(term->stdin_pipe_fd[1], buffer, written);
+
+}
+
+void guac_terminal_set_tab(guac_terminal* term, int column) {
+
+    int i;
+
+    /* Search for available space, set if available */
+    for (i=0; i<GUAC_TERMINAL_MAX_TABS; i++) {
+
+        /* Set tab if space free */
+        if (term->custom_tabs[i] == 0) {
+            term->custom_tabs[i] = column+1;
+            break;
+        }
+
+    }
+
+}
+
+void guac_terminal_unset_tab(guac_terminal* term, int column) {
+
+    int i;
+
+    /* Search for given tab, unset if found */
+    for (i=0; i<GUAC_TERMINAL_MAX_TABS; i++) {
+
+        /* Unset tab if found */
+        if (term->custom_tabs[i] == column+1) {
+            term->custom_tabs[i] = 0;
+            break;
+        }
+
+    }
+
+}
+
+void guac_terminal_clear_tabs(guac_terminal* term) {
+    term->tab_interval = 0;
+    memset(term->custom_tabs, 0, sizeof(term->custom_tabs));
+}
+
+int guac_terminal_next_tab(guac_terminal* term, int column) {
+
+    int i;
+
+    /* Determine tab stop from interval */
+    int tabstop;
+    if (term->tab_interval != 0)
+        tabstop = (column / term->tab_interval + 1) * term->tab_interval;
+    else
+        tabstop = term->term_width - 1;
+
+    /* Walk custom tabs, trying to find an earlier occurrence */
+    for (i=0; i<GUAC_TERMINAL_MAX_TABS; i++) {
+
+        int custom_tabstop = term->custom_tabs[i] - 1;
+        if (custom_tabstop != -1 && custom_tabstop > column && custom_tabstop < tabstop)
+            tabstop = custom_tabstop;
+
+    }
+
+    return tabstop;
+}
+
diff --git a/src/protocols/ssh/terminal.h b/src/terminal/terminal.h
similarity index 53%
rename from src/protocols/ssh/terminal.h
rename to src/terminal/terminal.h
index 569cce4..488692d 100644
--- a/src/protocols/ssh/terminal.h
+++ b/src/terminal/terminal.h
@@ -1,51 +1,54 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac-client-ssh.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#ifndef _SSH_GUAC_TERMINAL_H
-#define _SSH_GUAC_TERMINAL_H
 
-#include <stdbool.h>
+#ifndef _GUAC_TERMINAL_H
+#define _GUAC_TERMINAL_H
+
+#include "config.h"
+
+#include "buffer.h"
+#include "cursor.h"
+#include "display.h"
+#include "guac_clipboard.h"
+#include "scrollbar.h"
+#include "types.h"
+
 #include <pthread.h>
+#include <stdbool.h>
 
 #include <guacamole/client.h>
+#include <guacamole/stream.h>
 
-#include "types.h"
-#include "display.h"
-#include "buffer.h"
+/**
+ * The maximum duration of a single frame, in milliseconds.
+ */
+#define GUAC_TERMINAL_FRAME_DURATION 40
+
+/**
+ * The maximum amount of time to wait for more data before declaring a frame
+ * complete, in milliseconds.
+ */
+#define GUAC_TERMINAL_FRAME_TIMEOUT 10
 
 /**
  * The maximum number of custom tab stops.
@@ -55,7 +58,32 @@
 /**
  * The number of rows to scroll per scroll wheel event.
  */
-#define GUAC_SSH_WHEEL_SCROLL_AMOUNT 3
+#define GUAC_TERMINAL_WHEEL_SCROLL_AMOUNT 3
+
+/**
+ * The maximum number of bytes to allow within the clipboard.
+ */
+#define GUAC_TERMINAL_CLIPBOARD_MAX_LENGTH 262144
+
+/**
+ * The name of the color scheme having black foreground and white background.
+ */
+#define GUAC_TERMINAL_SCHEME_BLACK_WHITE "black-white"
+
+/**
+ * The name of the color scheme having gray foreground and black background.
+ */
+#define GUAC_TERMINAL_SCHEME_GRAY_BLACK "gray-black"
+
+/**
+ * The name of the color scheme having green foreground and black background.
+ */
+#define GUAC_TERMINAL_SCHEME_GREEN_BLACK "green-black"
+
+/**
+ * The name of the color scheme having white foreground and black background.
+ */
+#define GUAC_TERMINAL_SCHEME_WHITE_BLACK "white-black"
 
 typedef struct guac_terminal guac_terminal;
 
@@ -64,7 +92,17 @@ typedef struct guac_terminal guac_terminal;
  * the current char handler for the terminal is called and given that
  * character.
  */
-typedef int guac_terminal_char_handler(guac_terminal* term, char c);
+typedef int guac_terminal_char_handler(guac_terminal* term, unsigned char c);
+
+/**
+ * Handler for setting the destination path for file uploads.
+ */
+typedef void guac_terminal_upload_path_handler(guac_client* client, char* path);
+
+/**
+ * Handler for creating an outbound file download stream for a specified file.
+ */
+typedef guac_stream* guac_terminal_file_download_handler(guac_client* client, char* filename);
 
 /**
  * Represents a terminal emulator which uses a given Guacamole client to
@@ -78,6 +116,18 @@ struct guac_terminal {
     guac_client* client;
 
     /**
+     * Called whenever the necessary terminal codes are sent to change
+     * the path for future file uploads.
+     */
+    guac_terminal_upload_path_handler* upload_path_handler;
+
+    /**
+     * Called whenever the necessary terminal codes are sent to initiate
+     * a download of a given remote file.
+     */
+    guac_terminal_file_download_handler* file_download_handler;
+
+    /**
      * Lock which restricts simultaneous access to this terminal via the root
      * guac_terminal_* functions.
      */
@@ -100,6 +150,11 @@ struct guac_terminal {
     int stdin_pipe_fd[2];
 
     /**
+     * Graphical representation of the current scroll state.
+     */
+    guac_terminal_scrollbar* scrollbar;
+
+    /**
      * The relative offset of the display. A positive value indicates that
      * many rows have been scrolled into view, zero indicates that no
      * scrolling has occurred. Negative values are illegal.
@@ -228,6 +283,11 @@ struct guac_terminal {
     int selection_start_column;
 
     /**
+     * The width of the character at selection start.
+     */
+    int selection_start_width;
+
+    /**
      * The row that the selection ends at.
      */
     int selection_end_row;
@@ -238,6 +298,11 @@ struct guac_terminal {
     int selection_end_column;
 
     /**
+     * The width of the character at selection end.
+     */
+    int selection_end_width;
+
+    /**
      * Whether the cursor (arrow) keys should send cursor sequences
      * or application sequences (DECCKM).
      */
@@ -253,25 +318,199 @@ struct guac_terminal {
      */
     bool insert_mode;
 
+    /**
+     * Whether the alt key is currently being held down.
+     */
+    int mod_alt;
+
+    /**
+     * Whether the control key is currently being held down.
+     */
+    int mod_ctrl;
+
+    /**
+     * Whether the shift key is currently being held down.
+     */
+    int mod_shift;
+
+    /**
+     * The current mouse button state.
+     */
+    int mouse_mask;
+
+    /**
+     * The cached pointer cursor.
+     */
+    guac_terminal_cursor* pointer_cursor;
+
+    /**
+     * The cached I-bar cursor.
+     */
+    guac_terminal_cursor* ibar_cursor;
+
+    /**
+     * The cached invisible (blank) cursor.
+     */
+    guac_terminal_cursor* blank_cursor;
+
+    /**
+     * The current cursor, used to avoid re-setting the cursor.
+     */
+    guac_terminal_cursor* current_cursor;
+
+    /**
+     * The current contents of the clipboard.
+     */
+    guac_common_clipboard* clipboard;
+
 };
 
 /**
  * Creates a new guac_terminal, having the given width and height, and
  * rendering to the given client.
+ *
+ * @param client
+ *     The client to which the terminal will be rendered.
+ *
+ * @param font_name
+ *     The name of the font to use when rendering glyphs.
+ *
+ * @param font_size
+ *     The size of each glyph, in points.
+ *
+ * @param dpi
+ *     The DPI of the display. The given font size will be adjusted to produce
+ *     glyphs at the given DPI.
+ *
+ * @param width
+ *     The width of the terminal, in pixels.
+ *
+ * @param height
+ *     The height of the terminal, in pixels.
+ *
+ * @param color_scheme
+ *     The name of the color scheme to use. This string must be one of the
+ *     names defined by the GUAC_TERMINAL_SCHEME_* constants. If blank or NULL,
+ *     the default scheme of GUAC_TERMINAL_SCHEME_GRAY_BLACK will be used. If
+ *     invalid, a warning will be logged, and the terminal will fall back on
+ *     GUAC_TERMINAL_SCHEME_GRAY_BLACK.
+ *
+ * @return
+ *     A new guac_terminal having the given font, dimensions, and attributes
+ *     which renders all text to the given client.
  */
 guac_terminal* guac_terminal_create(guac_client* client,
-        const char* font_name, int font_size,
-        int width, int height);
+        const char* font_name, int font_size, int dpi,
+        int width, int height, const char* color_scheme);
 
 /**
- * Resets the state of the given terminal, as if it were just allocated.
+ * Frees all resources associated with the given terminal.
  */
-void guac_terminal_reset(guac_terminal* term);
+void guac_terminal_free(guac_terminal* term);
 
 /**
- * Frees all resources associated with the given terminal.
+ * Renders a single frame of terminal data. If data is not yet available,
+ * this function will block until data is written.
  */
-void guac_terminal_free(guac_terminal* term);
+int guac_terminal_render_frame(guac_terminal* terminal);
+
+/**
+ * Reads from this terminal's STDIN. Input comes from key and mouse events
+ * supplied by calls to guac_terminal_send_key() and
+ * guac_terminal_send_mouse(). If input is not yet available, this function
+ * will block.
+ */
+int guac_terminal_read_stdin(guac_terminal* terminal, char* c, int size);
+
+/**
+ * Writes to this terminal's STDOUT. This function may block until space
+ * is freed in the output buffer by guac_terminal_render_frame().
+ */
+int guac_terminal_write_stdout(guac_terminal* terminal, const char* c, int size);
+
+/**
+ * Notifies the terminal that an event has occurred and the terminal should
+ * flush itself when reasonable.
+ *
+ * @param terminal
+ *     The terminal to notify.
+ *
+ * @return
+ *     Zero if notification succeeded, non-zero if an error occurred while
+ *     notifying the terminal.
+ */
+int guac_terminal_notify(guac_terminal* terminal);
+
+/**
+ * Reads a single line from this terminal's STDIN. Input is retrieved in
+ * the same manner as guac_terminal_read_stdin() and the same restrictions
+ * apply.
+ */
+void guac_terminal_prompt(guac_terminal* terminal, const char* title, char* str, int size, bool echo);
+
+/**
+ * Writes the given format string and arguments to this terminal's STDOUT in
+ * the same manner as printf(). This function may block until space is
+ * freed in the output buffer by guac_terminal_render_frame().
+ */
+int guac_terminal_printf(guac_terminal* terminal, const char* format, ...);
+
+/**
+ * Handles the given key event, sending data, scrolling, pasting clipboard
+ * data, etc. as necessary.
+ */
+int guac_terminal_send_key(guac_terminal* term, int keysym, int pressed);
+
+/**
+ * Handles the given mouse event, sending data, scrolling, pasting clipboard
+ * data, etc. as necessary.
+ */
+int guac_terminal_send_mouse(guac_terminal* term, int x, int y, int mask);
+
+/**
+ * Handles a scroll event received from the scrollbar associated with a
+ * terminal.
+ *
+ * @param scrollbar
+ *     The scrollbar that has been scrolled.
+ *
+ * @param value
+ *     The new value that should be stored within the scrollbar, and
+ *     represented within the terminal display.
+ */
+void guac_terminal_scroll_handler(guac_terminal_scrollbar* scrollbar, int value);
+
+/**
+ * Clears the current clipboard contents and sets the mimetype for future
+ * contents.
+ */
+void guac_terminal_clipboard_reset(guac_terminal* term, const char* mimetype);
+
+/**
+ * Appends the given data to the current clipboard.
+ */
+void guac_terminal_clipboard_append(guac_terminal* term, const void* data, int length);
+
+
+/* INTERNAL FUNCTIONS */
+
+
+/**
+ * Acquires exclusive access to the terminal. Note that enforcing this
+ * exclusive access requires that ALL users of the terminal call this
+ * function before making further calls to the terminal.
+ */
+void guac_terminal_lock(guac_terminal* terminal);
+
+/**
+ * Releases exclusive access to the terminal.
+ */
+void guac_terminal_unlock(guac_terminal* terminal);
+
+/**
+ * Resets the state of the given terminal, as if it were just allocated.
+ */
+void guac_terminal_reset(guac_terminal* term);
 
 /**
  * Writes the given string of characters to the terminal.
@@ -345,8 +584,10 @@ void guac_terminal_select_update(guac_terminal* terminal, int row, int column);
  */
 void guac_terminal_select_end(guac_terminal* terminal, char* string);
 
+
 /* LOW-LEVEL TERMINAL OPERATIONS */
 
+
 /**
  * Copies the given range of columns to a new location, offset from
  * the original by the given number of columns.
@@ -371,7 +612,7 @@ void guac_terminal_set_columns(guac_terminal* terminal, int row,
 /**
  * Resize the terminal to the given dimensions.
  */
-void guac_terminal_resize(guac_terminal* term, int width, int height);
+int guac_terminal_resize(guac_terminal* term, int width, int height);
 
 /**
  * Flushes all pending operations within the given guac_terminal.
diff --git a/src/protocols/ssh/terminal_handlers.c b/src/terminal/terminal_handlers.c
similarity index 84%
rename from src/protocols/ssh/terminal_handlers.c
rename to src/terminal/terminal_handlers.c
index e555b00..49a45df 100644
--- a/src/protocols/ssh/terminal_handlers.c
+++ b/src/terminal/terminal_handlers.c
@@ -1,46 +1,37 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Original Code is libguac-client-ssh.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <stdlib.h>
+#include "config.h"
 
-#include "common.h"
+#include "char_mappings.h"
 #include "terminal.h"
 #include "terminal_handlers.h"
-#include "char_mappings.h"
+#include "types.h"
+
+#include <guacamole/client.h>
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <wchar.h>
 
 /**
  * Response string sent when identification is requested.
@@ -57,7 +48,9 @@
  */
 #define GUAC_TERMINAL_OK          "\x1B[0n"
 
-int guac_terminal_echo(guac_terminal* term, char c) {
+int guac_terminal_echo(guac_terminal* term, unsigned char c) {
+
+    int width;
 
     static int bytes_remaining = 0;
     static int codepoint = 0;
@@ -219,8 +212,12 @@ int guac_terminal_echo(guac_terminal* term, char c) {
                     term->cursor_col,
                     codepoint);
 
+            width = wcwidth(codepoint);
+            if (width < 0)
+                width = 1;
+
             /* Advance cursor */
-            term->cursor_col++;
+            term->cursor_col += width;
 
     }
 
@@ -228,7 +225,7 @@ int guac_terminal_echo(guac_terminal* term, char c) {
 
 }
 
-int guac_terminal_escape(guac_terminal* term, char c) {
+int guac_terminal_escape(guac_terminal* term, unsigned char c) {
 
     switch (c) {
 
@@ -312,6 +309,7 @@ int guac_terminal_escape(guac_terminal* term, char c) {
         /* Set Tab (HTS) */
         case 'H':
             guac_terminal_set_tab(term, term->cursor_col);
+            term->char_handler = guac_terminal_echo; 
             break;
 
         /* Reverse Linefeed */
@@ -344,7 +342,7 @@ int guac_terminal_escape(guac_terminal* term, char c) {
             break;
 
         default:
-            guac_client_log_info(term->client, "Unhandled ESC sequence: %c", c);
+            guac_client_log(term->client, GUAC_LOG_INFO, "Unhandled ESC sequence: %c", c);
             term->char_handler = guac_terminal_echo; 
 
     }
@@ -372,7 +370,7 @@ static const int* __guac_terminal_get_char_mapping(char c) {
 
 }
 
-int guac_terminal_g0_charset(guac_terminal* term, char c) {
+int guac_terminal_g0_charset(guac_terminal* term, unsigned char c) {
 
     term->char_mapping[0] = __guac_terminal_get_char_mapping(c);
     term->char_handler = guac_terminal_echo; 
@@ -380,7 +378,7 @@ int guac_terminal_g0_charset(guac_terminal* term, char c) {
 
 }
 
-int guac_terminal_g1_charset(guac_terminal* term, char c) {
+int guac_terminal_g1_charset(guac_terminal* term, unsigned char c) {
 
     term->char_mapping[1] = __guac_terminal_get_char_mapping(c);
     term->char_handler = guac_terminal_echo; 
@@ -407,13 +405,12 @@ static bool* __guac_terminal_get_flag(guac_terminal* term, int num, char private
         }
     }
 
-
     /* Unknown flag */
     return NULL;
 
 }
 
-int guac_terminal_csi(guac_terminal* term, char c) {
+int guac_terminal_csi(guac_terminal* term, unsigned char c) {
 
     /* CSI function arguments */
     static int argc = 0;
@@ -848,16 +845,15 @@ int guac_terminal_csi(guac_terminal* term, char c) {
 
                 break;
 
-
             /* Warn of unhandled codes */
             default:
                 if (c != ';') {
 
-                    guac_client_log_info(term->client,
+                    guac_client_log(term->client, GUAC_LOG_INFO,
                             "Unhandled CSI sequence: %c", c);
 
                     for (i=0; i<argc; i++)
-                        guac_client_log_info(term->client,
+                        guac_client_log(term->client, GUAC_LOG_INFO,
                                 " -> argv[%i] = %i", i, argv[i]);
 
                 }
@@ -890,14 +886,94 @@ int guac_terminal_csi(guac_terminal* term, char c) {
 
 }
 
-int guac_terminal_osc(guac_terminal* term, char c) {
-    /* TODO: Implement OSC */
-    if (c == 0x9C || c == 0x5C || c == 0x07) /* ECMA-48 ST (String Terminator */
-       term->char_handler = guac_terminal_echo; 
+int guac_terminal_set_directory(guac_terminal* term, unsigned char c) {
+
+    static char filename[2048];
+    static int length = 0;
+
+    /* Stop on ECMA-48 ST (String Terminator */
+    if (c == 0x9C || c == 0x5C || c == 0x07) {
+        filename[length++] = '\0';
+        term->char_handler = guac_terminal_echo;
+        if (term->upload_path_handler)
+            term->upload_path_handler(term->client, filename);
+        else
+            guac_client_log(term->client, GUAC_LOG_DEBUG,
+                    "Cannot set upload path. File is transfer not enabled.");
+        length = 0;
+    }
+
+    /* Otherwise, store character */
+    else if (length < sizeof(filename)-1)
+        filename[length++] = c;
+
+    return 0;
+
+}
+
+int guac_terminal_download(guac_terminal* term, unsigned char c) {
+
+    static char filename[2048];
+    static int length = 0;
+
+    /* Stop on ECMA-48 ST (String Terminator */
+    if (c == 0x9C || c == 0x5C || c == 0x07) {
+        filename[length++] = '\0';
+        term->char_handler = guac_terminal_echo;
+        if (term->file_download_handler)
+            term->file_download_handler(term->client, filename);
+        else
+            guac_client_log(term->client, GUAC_LOG_DEBUG,
+                    "Cannot send file. File is transfer not enabled.");
+        length = 0;
+    }
+
+    /* Otherwise, store character */
+    else if (length < sizeof(filename)-1)
+        filename[length++] = c;
+
+    return 0;
+
+}
+
+int guac_terminal_osc(guac_terminal* term, unsigned char c) {
+
+    static int operation = 0;
+
+    /* If digit, append to operation */
+    if (c >= '0' && c <= '9')
+        operation = operation * 10 + c - '0';
+
+    /* If end of parameter, check value */
+    else if (c == ';') {
+
+        /* Download OSC */
+        if (operation == 482200)
+            term->char_handler = guac_terminal_download;
+
+        /* Set upload directory OSC */
+        else if (operation == 482201)
+            term->char_handler = guac_terminal_set_directory;
+
+        /* Reset parameter for next OSC */
+        operation = 0;
+
+    }
+
+    /* Stop on ECMA-48 ST (String Terminator */
+    else if (c == 0x9C || c == 0x5C || c == 0x07)
+        term->char_handler = guac_terminal_echo;
+
+    /* Stop on unrecognized character */
+    else {
+        guac_client_log(term->client, GUAC_LOG_INFO, "Unexpected character in OSC: 0x%X", c);
+        term->char_handler = guac_terminal_echo;
+    }
+
     return 0;
 }
 
-int guac_terminal_ctrl_func(guac_terminal* term, char c) {
+int guac_terminal_ctrl_func(guac_terminal* term, unsigned char c) {
 
     int row;
 
diff --git a/src/terminal/terminal_handlers.h b/src/terminal/terminal_handlers.h
new file mode 100644
index 0000000..a6d8f22
--- /dev/null
+++ b/src/terminal/terminal_handlers.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef _GUAC_TERMINAL_HANDLERS
+#define _GUAC_TERMINAL_HANDLERS
+
+#include "config.h"
+
+#include "terminal.h"
+
+int guac_terminal_echo(guac_terminal* term, unsigned char c);
+int guac_terminal_escape(guac_terminal* term, unsigned char c);
+int guac_terminal_g0_charset(guac_terminal* term, unsigned char c);
+int guac_terminal_g1_charset(guac_terminal* term, unsigned char c);
+int guac_terminal_csi(guac_terminal* term, unsigned char c);
+int guac_terminal_download(guac_terminal* term, unsigned char c);
+int guac_terminal_set_directory(guac_terminal* term, unsigned char c);
+int guac_terminal_osc(guac_terminal* term, unsigned char c);
+int guac_terminal_ctrl_func(guac_terminal* term, unsigned char c);
+
+#endif
+
diff --git a/src/terminal/types.h b/src/terminal/types.h
new file mode 100644
index 0000000..98962be
--- /dev/null
+++ b/src/terminal/types.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef _GUAC_TERMINAL_TYPES_H
+#define _GUAC_TERMINAL_TYPES_H
+
+#include "config.h"
+
+#include <stdbool.h>
+
+/**
+ * A character which is not truly a character, but rather part of an
+ * existing character which spans multiple columns. The original
+ * character will be somewhere earlier in the row, separated from
+ * this character by a contiguous string of zero of more
+ * GUAC_CHAR_CONTINUATION characters.
+ */
+#define GUAC_CHAR_CONTINUATION -1
+
+/**
+ * An RGB color, where each component ranges from 0 to 255.
+ */
+typedef struct guac_terminal_color {
+
+    /**
+     * The red component of this color.
+     */
+    int red;
+
+    /**
+     * The green component of this color.
+     */
+    int green;
+
+    /**
+     * The blue component of this color.
+     */
+    int blue;
+
+} guac_terminal_color;
+
+/**
+ * Terminal attributes, as can be applied to a single character.
+ */
+typedef struct guac_terminal_attributes {
+
+    /**
+     * Whether the character should be rendered bold.
+     */
+    bool bold;
+
+    /**
+     * Whether the character should be rendered with reversed colors
+     * (background becomes foreground and vice-versa).
+     */
+    bool reverse;
+
+    /**
+     * Whether the associated character is highlighted by the cursor.
+     */
+    bool cursor;
+
+    /**
+     * Whether to render the character with underscore.
+     */
+    bool underscore;
+
+    /**
+     * The foreground color of this character, as a palette index.
+     */
+    int foreground;
+
+    /**
+     * The background color of this character, as a palette index.
+     */
+    int background;
+
+} guac_terminal_attributes;
+
+/**
+ * Represents a single character for display in a terminal, including actual
+ * character value, foreground color, and background color.
+ */
+typedef struct guac_terminal_char {
+
+    /**
+     * The Unicode codepoint of the character to display, or
+     * GUAC_CHAR_CONTINUATION if this character is part of
+     * another character which spans multiple columns.
+     */
+    int value;
+
+    /**
+     * The attributes of the character to display.
+     */
+    guac_terminal_attributes attributes;
+
+    /**
+     * The number of columns this character occupies. If the character is
+     * GUAC_CHAR_CONTINUATION, this value is undefined and not applicable.
+     */
+    int width;
+
+} guac_terminal_char;
+
+#endif
+
diff --git a/test-driver b/test-driver
new file mode 100755
index 0000000..d306056
--- /dev/null
+++ b/test-driver
@@ -0,0 +1,139 @@
+#! /bin/sh
+# test-driver - basic testsuite driver script.
+
+scriptversion=2013-07-13.22; # UTC
+
+# Copyright (C) 2011-2013 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# This file is maintained in Automake, please report
+# bugs to <bug-automake at gnu.org> or send patches to
+# <automake-patches at gnu.org>.
+
+# Make unconditional expansion of undefined variables an error.  This
+# helps a lot in preventing typo-related bugs.
+set -u
+
+usage_error ()
+{
+  echo "$0: $*" >&2
+  print_usage >&2
+  exit 2
+}
+
+print_usage ()
+{
+  cat <<END
+Usage:
+  test-driver --test-name=NAME --log-file=PATH --trs-file=PATH
+              [--expect-failure={yes|no}] [--color-tests={yes|no}]
+              [--enable-hard-errors={yes|no}] [--]
+              TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS]
+The '--test-name', '--log-file' and '--trs-file' options are mandatory.
+END
+}
+
+test_name= # Used for reporting.
+log_file=  # Where to save the output of the test script.
+trs_file=  # Where to save the metadata of the test run.
+expect_failure=no
+color_tests=no
+enable_hard_errors=yes
+while test $# -gt 0; do
+  case $1 in
+  --help) print_usage; exit $?;;
+  --version) echo "test-driver $scriptversion"; exit $?;;
+  --test-name) test_name=$2; shift;;
+  --log-file) log_file=$2; shift;;
+  --trs-file) trs_file=$2; shift;;
+  --color-tests) color_tests=$2; shift;;
+  --expect-failure) expect_failure=$2; shift;;
+  --enable-hard-errors) enable_hard_errors=$2; shift;;
+  --) shift; break;;
+  -*) usage_error "invalid option: '$1'";;
+   *) break;;
+  esac
+  shift
+done
+
+missing_opts=
+test x"$test_name" = x && missing_opts="$missing_opts --test-name"
+test x"$log_file"  = x && missing_opts="$missing_opts --log-file"
+test x"$trs_file"  = x && missing_opts="$missing_opts --trs-file"
+if test x"$missing_opts" != x; then
+  usage_error "the following mandatory options are missing:$missing_opts"
+fi
+
+if test $# -eq 0; then
+  usage_error "missing argument"
+fi
+
+if test $color_tests = yes; then
+  # Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'.
+  red='' # Red.
+  grn='' # Green.
+  lgn='' # Light green.
+  blu='' # Blue.
+  mgn='' # Magenta.
+  std=''     # No color.
+else
+  red= grn= lgn= blu= mgn= std=
+fi
+
+do_exit='rm -f $log_file $trs_file; (exit $st); exit $st'
+trap "st=129; $do_exit" 1
+trap "st=130; $do_exit" 2
+trap "st=141; $do_exit" 13
+trap "st=143; $do_exit" 15
+
+# Test script is run here.
+"$@" >$log_file 2>&1
+estatus=$?
+if test $enable_hard_errors = no && test $estatus -eq 99; then
+  estatus=1
+fi
+
+case $estatus:$expect_failure in
+  0:yes) col=$red res=XPASS recheck=yes gcopy=yes;;
+  0:*)   col=$grn res=PASS  recheck=no  gcopy=no;;
+  77:*)  col=$blu res=SKIP  recheck=no  gcopy=yes;;
+  99:*)  col=$mgn res=ERROR recheck=yes gcopy=yes;;
+  *:yes) col=$lgn res=XFAIL recheck=no  gcopy=yes;;
+  *:*)   col=$red res=FAIL  recheck=yes gcopy=yes;;
+esac
+
+# Report outcome to console.
+echo "${col}${res}${std}: $test_name"
+
+# Register the test result, and other relevant metadata.
+echo ":test-result: $res" > $trs_file
+echo ":global-test-result: $res" >> $trs_file
+echo ":recheck: $recheck" >> $trs_file
+echo ":copy-in-global-log: $gcopy" >> $trs_file
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 8d39010..b9a2bf4 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,63 +1,63 @@
-# ***** BEGIN LICENSE BLOCK *****
-# Version: MPL 1.1/GPL 2.0/LGPL 2.1
 #
-# The contents of this file are subject to the Mozilla Public License Version
-# 1.1 (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.mozilla.org/MPL/
+# Copyright (C) 2015 Glyptodon LLC
 #
-# Software distributed under the License is distributed on an "AS IS" basis,
-# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-# for the specific language governing rights and limitations under the
-# License.
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
 #
-# The Original Code is libguac.
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
 #
-# The Initial Developer of the Original Code is
-# Michael Jumper.
-# Portions created by the Initial Developer are Copyright (C) 2010
-# the Initial Developer. All Rights Reserved.
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
 #
-# Contributor(s):
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either the GNU General Public License Version 2 or later (the "GPL"), or
-# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-# in which case the provisions of the GPL or the LGPL are applicable instead
-# of those above. If you wish to allow use of your version of this file only
-# under the terms of either the GPL or the LGPL, and not to allow others to
-# use your version of this file under the terms of the MPL, indicate your
-# decision by deleting the provisions above and replace them with the notice
-# and other provisions required by the GPL or the LGPL. If you do not delete
-# the provisions above, a recipient may use your version of this file under
-# the terms of any one of the MPL, the GPL or the LGPL.
-#
-# ***** END LICENSE BLOCK *****
 
 AUTOMAKE_OPTIONS = foreign 
 ACLOCAL_AMFLAGS = -I m4
-AM_CFLAGS = -Werror -Wall -pedantic @LIBGUAC_INCLUDE@
 
 TESTS = test_libguac
 check_PROGRAMS = test_libguac
 
 noinst_HEADERS =          \
-	client/client_suite.h \
-	protocol/suite.h      \
-	util/util_suite.h
+    client/client_suite.h \
+    common/common_suite.h \
+    protocol/suite.h      \
+    util/util_suite.h
 
 test_libguac_SOURCES =           \
     test_libguac.c               \
-	client/client_suite.c        \
-	client/buffer_pool.c         \
-	client/layer_pool.c          \
-	protocol/suite.c             \
-	protocol/instruction_read.c  \
-	protocol/instruction_write.c \
-	protocol/nest_write.c        \
-	util/util_suite.c            \
-	util/guac_pool.c             \
-	util/guac_unicode.c
+    client/client_suite.c        \
+    client/buffer_pool.c         \
+    client/layer_pool.c          \
+    common/common_suite.c        \
+    common/guac_iconv.c          \
+    common/guac_string.c         \
+    common/guac_rect.c           \
+    protocol/suite.c             \
+    protocol/base64_decode.c     \
+    protocol/instruction_parse.c \
+    protocol/instruction_read.c  \
+    protocol/instruction_write.c \
+    protocol/nest_write.c        \
+    util/util_suite.c            \
+    util/guac_pool.c             \
+    util/guac_unicode.c
+
+test_libguac_CFLAGS =       \
+    -Werror -Wall -pedantic \
+    @COMMON_INCLUDE@        \
+    @LIBGUAC_INCLUDE@
 
-test_libguac_LDADD = @LIBGUAC_LTLIB@ @CUNIT_LIBS@
+test_libguac_LDADD = \
+    @COMMON_LTLIB@   \
+    @CUNIT_LIBS@     \
+    @LIBGUAC_LTLIB@
 
diff --git a/tests/Makefile.in b/tests/Makefile.in
index b5c2c35..497027b 100644
--- a/tests/Makefile.in
+++ b/tests/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.6 from Makefile.am.
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software
-# Foundation, Inc.
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
@@ -15,60 +14,74 @@
 
 @SET_MAKE@
 
-# ***** BEGIN LICENSE BLOCK *****
-# Version: MPL 1.1/GPL 2.0/LGPL 2.1
-#
-# The contents of this file are subject to the Mozilla Public License Version
-# 1.1 (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.mozilla.org/MPL/
 #
-# Software distributed under the License is distributed on an "AS IS" basis,
-# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-# for the specific language governing rights and limitations under the
-# License.
+# Copyright (C) 2015 Glyptodon LLC
 #
-# The Original Code is libguac.
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
 #
-# The Initial Developer of the Original Code is
-# Michael Jumper.
-# Portions created by the Initial Developer are Copyright (C) 2010
-# the Initial Developer. All Rights Reserved.
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
 #
-# Contributor(s):
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
 #
-# Alternatively, the contents of this file may be used under the terms of
-# either the GNU General Public License Version 2 or later (the "GPL"), or
-# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-# in which case the provisions of the GPL or the LGPL are applicable instead
-# of those above. If you wish to allow use of your version of this file only
-# under the terms of either the GPL or the LGPL, and not to allow others to
-# use your version of this file under the terms of the MPL, indicate your
-# decision by deleting the provisions above and replace them with the notice
-# and other provisions required by the GPL or the LGPL. If you do not delete
-# the provisions above, a recipient may use your version of this file under
-# the terms of any one of the MPL, the GPL or the LGPL.
-#
-# ***** END LICENSE BLOCK *****
 
 VPATH = @srcdir@
-am__make_dryrun = \
-  { \
-    am__dry=no; \
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
     case $$MAKEFLAGS in \
       *\\[\ \	]*) \
-        echo 'am--echo: ; @echo "AM"  OK' | $(MAKE) -f - 2>/dev/null \
-          | grep '^AM OK$$' >/dev/null || am__dry=yes;; \
-      *) \
-        for am__flg in $$MAKEFLAGS; do \
-          case $$am__flg in \
-            *=*|--*) ;; \
-            *n*) am__dry=yes; break;; \
-          esac; \
-        done;; \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
     esac; \
-    test $$am__dry = yes; \
-  }
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
 pkgdatadir = $(datadir)/@PACKAGE@
 pkgincludedir = $(includedir)/@PACKAGE@
 pkglibdir = $(libdir)/@PACKAGE@
@@ -90,8 +103,9 @@ host_triplet = @host@
 TESTS = test_libguac$(EXEEXT)
 check_PROGRAMS = test_libguac$(EXEEXT)
 subdir = tests
-DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
-	$(srcdir)/Makefile.in
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(top_srcdir)/depcomp $(noinst_HEADERS) \
+	$(top_srcdir)/test-driver
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
 	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
@@ -100,29 +114,70 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
 CONFIG_CLEAN_FILES =
 CONFIG_CLEAN_VPATH_FILES =
-am_test_libguac_OBJECTS = test_libguac.$(OBJEXT) \
-	client_suite.$(OBJEXT) buffer_pool.$(OBJEXT) \
-	layer_pool.$(OBJEXT) suite.$(OBJEXT) \
-	instruction_read.$(OBJEXT) instruction_write.$(OBJEXT) \
-	nest_write.$(OBJEXT) util_suite.$(OBJEXT) guac_pool.$(OBJEXT) \
-	guac_unicode.$(OBJEXT)
+am__dirstamp = $(am__leading_dot)dirstamp
+am_test_libguac_OBJECTS = test_libguac-test_libguac.$(OBJEXT) \
+	client/test_libguac-client_suite.$(OBJEXT) \
+	client/test_libguac-buffer_pool.$(OBJEXT) \
+	client/test_libguac-layer_pool.$(OBJEXT) \
+	common/test_libguac-common_suite.$(OBJEXT) \
+	common/test_libguac-guac_iconv.$(OBJEXT) \
+	common/test_libguac-guac_string.$(OBJEXT) \
+	common/test_libguac-guac_rect.$(OBJEXT) \
+	protocol/test_libguac-suite.$(OBJEXT) \
+	protocol/test_libguac-base64_decode.$(OBJEXT) \
+	protocol/test_libguac-instruction_parse.$(OBJEXT) \
+	protocol/test_libguac-instruction_read.$(OBJEXT) \
+	protocol/test_libguac-instruction_write.$(OBJEXT) \
+	protocol/test_libguac-nest_write.$(OBJEXT) \
+	util/test_libguac-util_suite.$(OBJEXT) \
+	util/test_libguac-guac_pool.$(OBJEXT) \
+	util/test_libguac-guac_unicode.$(OBJEXT)
 test_libguac_OBJECTS = $(am_test_libguac_OBJECTS)
 test_libguac_DEPENDENCIES =
-DEFAULT_INCLUDES = -I. at am__isrc@
+AM_V_lt = $(am__v_lt_ at AM_V@)
+am__v_lt_ = $(am__v_lt_ at AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+test_libguac_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(test_libguac_CFLAGS) \
+	$(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_ at AM_V@)
+am__v_P_ = $(am__v_P_ at AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_ at AM_V@)
+am__v_GEN_ = $(am__v_GEN_ at AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_ at AM_V@)
+am__v_at_ = $(am__v_at_ at AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I. at am__isrc@ -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/depcomp
 am__depfiles_maybe = depfiles
 am__mv = mv -f
 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
 	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
-	--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
-	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_ at AM_V@)
+am__v_CC_ = $(am__v_CC_ at AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
 CCLD = $(CC)
-LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
-	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
-	$(LDFLAGS) -o $@
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_ at AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_ at AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
 SOURCES = $(test_libguac_SOURCES)
 DIST_SOURCES = $(test_libguac_SOURCES)
 am__can_run_installinfo = \
@@ -131,13 +186,233 @@ am__can_run_installinfo = \
     *) (install-info --version) >/dev/null 2>&1;; \
   esac
 HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
 ETAGS = etags
 CTAGS = ctags
-am__tty_colors = \
-red=; grn=; lgn=; blu=; std=
+am__tty_colors_dummy = \
+  mgn= red= grn= lgn= blu= brg= std=; \
+  am__color_tests=no
+am__tty_colors = { \
+  $(am__tty_colors_dummy); \
+  if test "X$(AM_COLOR_TESTS)" = Xno; then \
+    am__color_tests=no; \
+  elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+    am__color_tests=yes; \
+  elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+    am__color_tests=yes; \
+  fi; \
+  if test $$am__color_tests = yes; then \
+    red=''; \
+    grn=''; \
+    lgn=''; \
+    blu=''; \
+    mgn=''; \
+    brg=''; \
+    std=''; \
+  fi; \
+}
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__recheck_rx = ^[ 	]*:recheck:[ 	]*
+am__global_test_result_rx = ^[ 	]*:global-test-result:[ 	]*
+am__copy_in_global_log_rx = ^[ 	]*:copy-in-global-log:[ 	]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+  recheck = 1; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+        { \
+          if ((getline line2 < ($$0 ".log")) < 0) \
+	    recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+        { \
+          recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+        { \
+          break; \
+        } \
+    }; \
+  if (recheck) \
+    print $$0; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+  print "fatal: making $@: " msg | "cat >&2"; \
+  exit 1; \
+} \
+function rst_section(header) \
+{ \
+  print header; \
+  len = length(header); \
+  for (i = 1; i <= len; i = i + 1) \
+    printf "="; \
+  printf "\n\n"; \
+} \
+{ \
+  copy_in_global_log = 1; \
+  global_test_result = "RUN"; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+         fatal("failed to read from " $$0 ".trs"); \
+      if (line ~ /$(am__global_test_result_rx)/) \
+        { \
+          sub("$(am__global_test_result_rx)", "", line); \
+          sub("[ 	]*$$", "", line); \
+          global_test_result = line; \
+        } \
+      else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+        copy_in_global_log = 0; \
+    }; \
+  if (copy_in_global_log) \
+    { \
+      rst_section(global_test_result ": " $$0); \
+      while ((rc = (getline line < ($$0 ".log"))) != 0) \
+      { \
+        if (rc < 0) \
+          fatal("failed to read from " $$0 ".log"); \
+        print line; \
+      }; \
+      printf "\n"; \
+    }; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/   &   /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this.  Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+  --color-tests "$$am__color_tests" \
+  --enable-hard-errors "$$am__enable_hard_errors" \
+  --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test.  Creates the
+# directory for the log if needed.  Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log.  Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT.  Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup);					\
+$(am__vpath_adj_setup) $(am__vpath_adj)			\
+$(am__tty_colors);					\
+srcdir=$(srcdir); export srcdir;			\
+case "$@" in						\
+  */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;;	\
+    *) am__odir=.;; 					\
+esac;							\
+test "x$$am__odir" = x"." || test -d "$$am__odir" 	\
+  || $(MKDIR_P) "$$am__odir" || exit $$?;		\
+if test -f "./$$f"; then dir=./;			\
+elif test -f "$$f"; then dir=;				\
+else dir="$(srcdir)/"; fi;				\
+tst=$$dir$$f; log='$@'; 				\
+if test -n '$(DISABLE_HARD_ERRORS)'; then		\
+  am__enable_hard_errors=no; 				\
+else							\
+  am__enable_hard_errors=yes; 				\
+fi; 							\
+case " $(XFAIL_TESTS) " in				\
+  *[\ \	]$$f[\ \	]* | *[\ \	]$$dir$$f[\ \	]*) \
+    am__expect_failure=yes;;				\
+  *)							\
+    am__expect_failure=no;;				\
+esac; 							\
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed).  The result is saved in the shell variable
+# '$bases'.  This honors runtime overriding of TESTS and TEST_LOGS.  Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+  bases='$(TEST_LOGS)'; \
+  bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+  bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+AM_RECURSIVE_TARGETS = check recheck
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+  case '$@' in \
+    */*) \
+      case '$*' in \
+        */*) b='$*';; \
+          *) b=`echo '$@' | sed 's/\.log$$//'`; \
+       esac;; \
+    *) \
+      b='$*';; \
+  esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT at .log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+	$(TEST_LOG_FLAGS)
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 ACLOCAL = @ACLOCAL@
 AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
 AR = @AR@
 AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
@@ -147,6 +422,10 @@ CAIRO_LIBS = @CAIRO_LIBS@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+COMMON_INCLUDE = @COMMON_INCLUDE@
+COMMON_LTLIB = @COMMON_LTLIB@
+COMMON_SSH_INCLUDE = @COMMON_SSH_INCLUDE@
+COMMON_SSH_LTLIB = @COMMON_SSH_LTLIB@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
 CUNIT_LIBS = @CUNIT_LIBS@
@@ -154,7 +433,6 @@ CYGPATH_W = @CYGPATH_W@
 DEFS = @DEFS@
 DEPDIR = @DEPDIR@
 DLLTOOL = @DLLTOOL@
-DL_LIBS = @DL_LIBS@
 DSYMUTIL = @DSYMUTIL@
 DUMPBIN = @DUMPBIN@
 ECHO_C = @ECHO_C@
@@ -169,8 +447,10 @@ INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
 INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+JPEG_LIBS = @JPEG_LIBS@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
+LIBADD_DLOPEN = @LIBADD_DLOPEN@
 LIBGUAC_INCLUDE = @LIBGUAC_INCLUDE@
 LIBGUAC_LTLIB = @LIBGUAC_LTLIB@
 LIBOBJS = @LIBOBJS@
@@ -181,6 +461,7 @@ LN_S = @LN_S@
 LTLIBOBJS = @LTLIBOBJS@
 MAKEINFO = @MAKEINFO@
 MANIFEST_TOOL = @MANIFEST_TOOL@
+MATH_LIBS = @MATH_LIBS@
 MKDIR_P = @MKDIR_P@
 NM = @NM@
 NMEDIT = @NMEDIT@
@@ -214,9 +495,14 @@ SHELL = @SHELL@
 SSH_LIBS = @SSH_LIBS@
 SSL_LIBS = @SSL_LIBS@
 STRIP = @STRIP@
+TELNET_LIBS = @TELNET_LIBS@
+TERMINAL_INCLUDE = @TERMINAL_INCLUDE@
+TERMINAL_LTLIB = @TERMINAL_LTLIB@
+UUID_LIBS = @UUID_LIBS@
 VERSION = @VERSION@
 VNC_LIBS = @VNC_LIBS@
 VORBIS_LIBS = @VORBIS_LIBS@
+WEBP_LIBS = @WEBP_LIBS@
 abs_builddir = @abs_builddir@
 abs_srcdir = @abs_srcdir@
 abs_top_builddir = @abs_top_builddir@
@@ -272,30 +558,45 @@ top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
 AUTOMAKE_OPTIONS = foreign 
 ACLOCAL_AMFLAGS = -I m4
-AM_CFLAGS = -Werror -Wall -pedantic @LIBGUAC_INCLUDE@
 noinst_HEADERS = \
-	client/client_suite.h \
-	protocol/suite.h      \
-	util/util_suite.h
+    client/client_suite.h \
+    common/common_suite.h \
+    protocol/suite.h      \
+    util/util_suite.h
 
 test_libguac_SOURCES = \
     test_libguac.c               \
-	client/client_suite.c        \
-	client/buffer_pool.c         \
-	client/layer_pool.c          \
-	protocol/suite.c             \
-	protocol/instruction_read.c  \
-	protocol/instruction_write.c \
-	protocol/nest_write.c        \
-	util/util_suite.c            \
-	util/guac_pool.c             \
-	util/guac_unicode.c
-
-test_libguac_LDADD = @LIBGUAC_LTLIB@ @CUNIT_LIBS@
+    client/client_suite.c        \
+    client/buffer_pool.c         \
+    client/layer_pool.c          \
+    common/common_suite.c        \
+    common/guac_iconv.c          \
+    common/guac_string.c         \
+    common/guac_rect.c           \
+    protocol/suite.c             \
+    protocol/base64_decode.c     \
+    protocol/instruction_parse.c \
+    protocol/instruction_read.c  \
+    protocol/instruction_write.c \
+    protocol/nest_write.c        \
+    util/util_suite.c            \
+    util/guac_pool.c             \
+    util/guac_unicode.c
+
+test_libguac_CFLAGS = \
+    -Werror -Wall -pedantic \
+    @COMMON_INCLUDE@        \
+    @LIBGUAC_INCLUDE@
+
+test_libguac_LDADD = \
+    @COMMON_LTLIB@   \
+    @CUNIT_LIBS@     \
+    @LIBGUAC_LTLIB@
+
 all: all-am
 
 .SUFFIXES:
-.SUFFIXES: .c .lo .o .obj
+.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs
 $(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
 	@for dep in $?; do \
 	  case '$(am__configure_deps)' in \
@@ -335,188 +636,356 @@ clean-checkPROGRAMS:
 	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
 	echo " rm -f" $$list; \
 	rm -f $$list
+client/$(am__dirstamp):
+	@$(MKDIR_P) client
+	@: > client/$(am__dirstamp)
+client/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) client/$(DEPDIR)
+	@: > client/$(DEPDIR)/$(am__dirstamp)
+client/test_libguac-client_suite.$(OBJEXT): client/$(am__dirstamp) \
+	client/$(DEPDIR)/$(am__dirstamp)
+client/test_libguac-buffer_pool.$(OBJEXT): client/$(am__dirstamp) \
+	client/$(DEPDIR)/$(am__dirstamp)
+client/test_libguac-layer_pool.$(OBJEXT): client/$(am__dirstamp) \
+	client/$(DEPDIR)/$(am__dirstamp)
+common/$(am__dirstamp):
+	@$(MKDIR_P) common
+	@: > common/$(am__dirstamp)
+common/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) common/$(DEPDIR)
+	@: > common/$(DEPDIR)/$(am__dirstamp)
+common/test_libguac-common_suite.$(OBJEXT): common/$(am__dirstamp) \
+	common/$(DEPDIR)/$(am__dirstamp)
+common/test_libguac-guac_iconv.$(OBJEXT): common/$(am__dirstamp) \
+	common/$(DEPDIR)/$(am__dirstamp)
+common/test_libguac-guac_string.$(OBJEXT): common/$(am__dirstamp) \
+	common/$(DEPDIR)/$(am__dirstamp)
+common/test_libguac-guac_rect.$(OBJEXT): common/$(am__dirstamp) \
+	common/$(DEPDIR)/$(am__dirstamp)
+protocol/$(am__dirstamp):
+	@$(MKDIR_P) protocol
+	@: > protocol/$(am__dirstamp)
+protocol/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) protocol/$(DEPDIR)
+	@: > protocol/$(DEPDIR)/$(am__dirstamp)
+protocol/test_libguac-suite.$(OBJEXT): protocol/$(am__dirstamp) \
+	protocol/$(DEPDIR)/$(am__dirstamp)
+protocol/test_libguac-base64_decode.$(OBJEXT):  \
+	protocol/$(am__dirstamp) protocol/$(DEPDIR)/$(am__dirstamp)
+protocol/test_libguac-instruction_parse.$(OBJEXT):  \
+	protocol/$(am__dirstamp) protocol/$(DEPDIR)/$(am__dirstamp)
+protocol/test_libguac-instruction_read.$(OBJEXT):  \
+	protocol/$(am__dirstamp) protocol/$(DEPDIR)/$(am__dirstamp)
+protocol/test_libguac-instruction_write.$(OBJEXT):  \
+	protocol/$(am__dirstamp) protocol/$(DEPDIR)/$(am__dirstamp)
+protocol/test_libguac-nest_write.$(OBJEXT): protocol/$(am__dirstamp) \
+	protocol/$(DEPDIR)/$(am__dirstamp)
+util/$(am__dirstamp):
+	@$(MKDIR_P) util
+	@: > util/$(am__dirstamp)
+util/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) util/$(DEPDIR)
+	@: > util/$(DEPDIR)/$(am__dirstamp)
+util/test_libguac-util_suite.$(OBJEXT): util/$(am__dirstamp) \
+	util/$(DEPDIR)/$(am__dirstamp)
+util/test_libguac-guac_pool.$(OBJEXT): util/$(am__dirstamp) \
+	util/$(DEPDIR)/$(am__dirstamp)
+util/test_libguac-guac_unicode.$(OBJEXT): util/$(am__dirstamp) \
+	util/$(DEPDIR)/$(am__dirstamp)
+
 test_libguac$(EXEEXT): $(test_libguac_OBJECTS) $(test_libguac_DEPENDENCIES) $(EXTRA_test_libguac_DEPENDENCIES) 
 	@rm -f test_libguac$(EXEEXT)
-	$(LINK) $(test_libguac_OBJECTS) $(test_libguac_LDADD) $(LIBS)
+	$(AM_V_CCLD)$(test_libguac_LINK) $(test_libguac_OBJECTS) $(test_libguac_LDADD) $(LIBS)
 
 mostlyclean-compile:
 	-rm -f *.$(OBJEXT)
+	-rm -f client/*.$(OBJEXT)
+	-rm -f common/*.$(OBJEXT)
+	-rm -f protocol/*.$(OBJEXT)
+	-rm -f util/*.$(OBJEXT)
 
 distclean-compile:
 	-rm -f *.tab.c
 
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/buffer_pool.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/client_suite.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/guac_pool.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/guac_unicode.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/instruction_read.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/instruction_write.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/layer_pool.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/nest_write.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/suite.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/test_libguac.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/util_suite.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/test_libguac-test_libguac.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at client/$(DEPDIR)/test_libguac-buffer_pool.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at client/$(DEPDIR)/test_libguac-client_suite.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at client/$(DEPDIR)/test_libguac-layer_pool.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at common/$(DEPDIR)/test_libguac-common_suite.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at common/$(DEPDIR)/test_libguac-guac_iconv.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at common/$(DEPDIR)/test_libguac-guac_rect.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at common/$(DEPDIR)/test_libguac-guac_string.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at protocol/$(DEPDIR)/test_libguac-base64_decode.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at protocol/$(DEPDIR)/test_libguac-instruction_parse.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at protocol/$(DEPDIR)/test_libguac-instruction_read.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at protocol/$(DEPDIR)/test_libguac-instruction_write.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at protocol/$(DEPDIR)/test_libguac-nest_write.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at protocol/$(DEPDIR)/test_libguac-suite.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at util/$(DEPDIR)/test_libguac-guac_pool.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at util/$(DEPDIR)/test_libguac-guac_unicode.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at util/$(DEPDIR)/test_libguac-util_suite.Po at am__quote@
 
 .c.o:
- at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+ at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(COMPILE) -c $<
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(COMPILE) -c -o $@ $<
 
 .c.obj:
- at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+ at am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(COMPILE) -c `$(CYGPATH_W) '$<'`
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
 
 .c.lo:
- at am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+ at am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+ at am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+ at am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+test_libguac-test_libguac.o: test_libguac.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT test_libguac-test_libguac.o -MD -MP -MF $(DEPDIR)/test_libguac-test_libguac.Tpo -c -o test_libguac-test_libguac.o `test -f 'test_libguac.c' || echo '$(srcdir)/'`test_libguac.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_libguac-test_libguac.Tpo $(DEPDIR)/test_libguac-test_libguac.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test_libguac.c' object='test_libguac-test_libguac.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o test_libguac-test_libguac.o `test -f 'test_libguac.c' || echo '$(srcdir)/'`test_libguac.c
+
+test_libguac-test_libguac.obj: test_libguac.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT test_libguac-test_libguac.obj -MD -MP -MF $(DEPDIR)/test_libguac-test_libguac.Tpo -c -o test_libguac-test_libguac.obj `if test -f 'test_libguac.c'; then $(CYGPATH_W) 'test_libguac.c'; else $(CYGPATH_W) '$(srcdir)/test_libguac.c'; fi`
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_libguac-test_libguac.Tpo $(DEPDIR)/test_libguac-test_libguac.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test_libguac.c' object='test_libguac-test_libguac.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o test_libguac-test_libguac.obj `if test -f 'test_libguac.c'; then $(CYGPATH_W) 'test_libguac.c'; else $(CYGPATH_W) '$(srcdir)/test_libguac.c'; fi`
+
+client/test_libguac-client_suite.o: client/client_suite.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT client/test_libguac-client_suite.o -MD -MP -MF client/$(DEPDIR)/test_libguac-client_suite.Tpo -c -o client/test_libguac-client_suite.o `test -f 'client/client_suite.c' || echo '$(srcdir)/'`client/client_suite.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) client/$(DEPDIR)/test_libguac-client_suite.Tpo client/$(DEPDIR)/test_libguac-client_suite.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='client/client_suite.c' object='client/test_libguac-client_suite.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o client/test_libguac-client_suite.o `test -f 'client/client_suite.c' || echo '$(srcdir)/'`client/client_suite.c
+
+client/test_libguac-client_suite.obj: client/client_suite.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT client/test_libguac-client_suite.obj -MD -MP -MF client/$(DEPDIR)/test_libguac-client_suite.Tpo -c -o client/test_libguac-client_suite.obj `if test -f 'client/client_suite.c'; then $(CYGPATH_W) 'client/client_suite.c'; else $(CYGPATH_W) '$(srcdir)/client/client_suite.c'; fi`
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) client/$(DEPDIR)/test_libguac-client_suite.Tpo client/$(DEPDIR)/test_libguac-client_suite.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='client/client_suite.c' object='client/test_libguac-client_suite.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o client/test_libguac-client_suite.obj `if test -f 'client/client_suite.c'; then $(CYGPATH_W) 'client/client_suite.c'; else $(CYGPATH_W) '$(srcdir)/client/client_suite.c'; fi`
+
+client/test_libguac-buffer_pool.o: client/buffer_pool.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT client/test_libguac-buffer_pool.o -MD -MP -MF client/$(DEPDIR)/test_libguac-buffer_pool.Tpo -c -o client/test_libguac-buffer_pool.o `test -f 'client/buffer_pool.c' || echo '$(srcdir)/'`client/buffer_pool.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) client/$(DEPDIR)/test_libguac-buffer_pool.Tpo client/$(DEPDIR)/test_libguac-buffer_pool.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='client/buffer_pool.c' object='client/test_libguac-buffer_pool.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o client/test_libguac-buffer_pool.o `test -f 'client/buffer_pool.c' || echo '$(srcdir)/'`client/buffer_pool.c
+
+client/test_libguac-buffer_pool.obj: client/buffer_pool.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT client/test_libguac-buffer_pool.obj -MD -MP -MF client/$(DEPDIR)/test_libguac-buffer_pool.Tpo -c -o client/test_libguac-buffer_pool.obj `if test -f 'client/buffer_pool.c'; then $(CYGPATH_W) 'client/buffer_pool.c'; else $(CYGPATH_W) '$(srcdir)/client/buffer_pool.c'; fi`
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) client/$(DEPDIR)/test_libguac-buffer_pool.Tpo client/$(DEPDIR)/test_libguac-buffer_pool.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='client/buffer_pool.c' object='client/test_libguac-buffer_pool.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o client/test_libguac-buffer_pool.obj `if test -f 'client/buffer_pool.c'; then $(CYGPATH_W) 'client/buffer_pool.c'; else $(CYGPATH_W) '$(srcdir)/client/buffer_pool.c'; fi`
+
+client/test_libguac-layer_pool.o: client/layer_pool.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT client/test_libguac-layer_pool.o -MD -MP -MF client/$(DEPDIR)/test_libguac-layer_pool.Tpo -c -o client/test_libguac-layer_pool.o `test -f 'client/layer_pool.c' || echo '$(srcdir)/'`client/layer_pool.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) client/$(DEPDIR)/test_libguac-layer_pool.Tpo client/$(DEPDIR)/test_libguac-layer_pool.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='client/layer_pool.c' object='client/test_libguac-layer_pool.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o client/test_libguac-layer_pool.o `test -f 'client/layer_pool.c' || echo '$(srcdir)/'`client/layer_pool.c
+
+client/test_libguac-layer_pool.obj: client/layer_pool.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT client/test_libguac-layer_pool.obj -MD -MP -MF client/$(DEPDIR)/test_libguac-layer_pool.Tpo -c -o client/test_libguac-layer_pool.obj `if test -f 'client/layer_pool.c'; then $(CYGPATH_W) 'client/layer_pool.c'; else $(CYGPATH_W) '$(srcdir)/client/layer_pool.c'; fi`
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) client/$(DEPDIR)/test_libguac-layer_pool.Tpo client/$(DEPDIR)/test_libguac-layer_pool.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='client/layer_pool.c' object='client/test_libguac-layer_pool.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o client/test_libguac-layer_pool.obj `if test -f 'client/layer_pool.c'; then $(CYGPATH_W) 'client/layer_pool.c'; else $(CYGPATH_W) '$(srcdir)/client/layer_pool.c'; fi`
+
+common/test_libguac-common_suite.o: common/common_suite.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT common/test_libguac-common_suite.o -MD -MP -MF common/$(DEPDIR)/test_libguac-common_suite.Tpo -c -o common/test_libguac-common_suite.o `test -f 'common/common_suite.c' || echo '$(srcdir)/'`common/common_suite.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) common/$(DEPDIR)/test_libguac-common_suite.Tpo common/$(DEPDIR)/test_libguac-common_suite.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='common/common_suite.c' object='common/test_libguac-common_suite.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o common/test_libguac-common_suite.o `test -f 'common/common_suite.c' || echo '$(srcdir)/'`common/common_suite.c
+
+common/test_libguac-common_suite.obj: common/common_suite.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT common/test_libguac-common_suite.obj -MD -MP -MF common/$(DEPDIR)/test_libguac-common_suite.Tpo -c -o common/test_libguac-common_suite.obj `if test -f 'common/common_suite.c'; then $(CYGPATH_W) 'common/common_suite.c'; else $(CYGPATH_W) '$(srcdir)/common/common_suite.c'; fi`
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) common/$(DEPDIR)/test_libguac-common_suite.Tpo common/$(DEPDIR)/test_libguac-common_suite.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='common/common_suite.c' object='common/test_libguac-common_suite.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o common/test_libguac-common_suite.obj `if test -f 'common/common_suite.c'; then $(CYGPATH_W) 'common/common_suite.c'; else $(CYGPATH_W) '$(srcdir)/common/common_suite.c'; fi`
+
+common/test_libguac-guac_iconv.o: common/guac_iconv.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT common/test_libguac-guac_iconv.o -MD -MP -MF common/$(DEPDIR)/test_libguac-guac_iconv.Tpo -c -o common/test_libguac-guac_iconv.o `test -f 'common/guac_iconv.c' || echo '$(srcdir)/'`common/guac_iconv.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) common/$(DEPDIR)/test_libguac-guac_iconv.Tpo common/$(DEPDIR)/test_libguac-guac_iconv.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='common/guac_iconv.c' object='common/test_libguac-guac_iconv.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o common/test_libguac-guac_iconv.o `test -f 'common/guac_iconv.c' || echo '$(srcdir)/'`common/guac_iconv.c
+
+common/test_libguac-guac_iconv.obj: common/guac_iconv.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT common/test_libguac-guac_iconv.obj -MD -MP -MF common/$(DEPDIR)/test_libguac-guac_iconv.Tpo -c -o common/test_libguac-guac_iconv.obj `if test -f 'common/guac_iconv.c'; then $(CYGPATH_W) 'common/guac_iconv.c'; else $(CYGPATH_W) '$(srcdir)/common/guac_iconv.c'; fi`
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) common/$(DEPDIR)/test_libguac-guac_iconv.Tpo common/$(DEPDIR)/test_libguac-guac_iconv.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='common/guac_iconv.c' object='common/test_libguac-guac_iconv.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o common/test_libguac-guac_iconv.obj `if test -f 'common/guac_iconv.c'; then $(CYGPATH_W) 'common/guac_iconv.c'; else $(CYGPATH_W) '$(srcdir)/common/guac_iconv.c'; fi`
+
+common/test_libguac-guac_string.o: common/guac_string.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT common/test_libguac-guac_string.o -MD -MP -MF common/$(DEPDIR)/test_libguac-guac_string.Tpo -c -o common/test_libguac-guac_string.o `test -f 'common/guac_string.c' || echo '$(srcdir)/'`common/guac_string.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) common/$(DEPDIR)/test_libguac-guac_string.Tpo common/$(DEPDIR)/test_libguac-guac_string.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='common/guac_string.c' object='common/test_libguac-guac_string.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o common/test_libguac-guac_string.o `test -f 'common/guac_string.c' || echo '$(srcdir)/'`common/guac_string.c
+
+common/test_libguac-guac_string.obj: common/guac_string.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT common/test_libguac-guac_string.obj -MD -MP -MF common/$(DEPDIR)/test_libguac-guac_string.Tpo -c -o common/test_libguac-guac_string.obj `if test -f 'common/guac_string.c'; then $(CYGPATH_W) 'common/guac_string.c'; else $(CYGPATH_W) '$(srcdir)/common/guac_string.c'; fi`
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) common/$(DEPDIR)/test_libguac-guac_string.Tpo common/$(DEPDIR)/test_libguac-guac_string.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='common/guac_string.c' object='common/test_libguac-guac_string.obj' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o common/test_libguac-guac_string.obj `if test -f 'common/guac_string.c'; then $(CYGPATH_W) 'common/guac_string.c'; else $(CYGPATH_W) '$(srcdir)/common/guac_string.c'; fi`
 
-client_suite.o: client/client_suite.c
- at am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT client_suite.o -MD -MP -MF $(DEPDIR)/client_suite.Tpo -c -o client_suite.o `test -f 'client/client_suite.c' || echo '$(srcdir)/'`client/client_suite.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/client_suite.Tpo $(DEPDIR)/client_suite.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='client/client_suite.c' object='client_suite.o' libtool=no @AMDEPBACKSLASH@
+common/test_libguac-guac_rect.o: common/guac_rect.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT common/test_libguac-guac_rect.o -MD -MP -MF common/$(DEPDIR)/test_libguac-guac_rect.Tpo -c -o common/test_libguac-guac_rect.o `test -f 'common/guac_rect.c' || echo '$(srcdir)/'`common/guac_rect.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) common/$(DEPDIR)/test_libguac-guac_rect.Tpo common/$(DEPDIR)/test_libguac-guac_rect.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='common/guac_rect.c' object='common/test_libguac-guac_rect.o' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o client_suite.o `test -f 'client/client_suite.c' || echo '$(srcdir)/'`client/client_suite.c
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o common/test_libguac-guac_rect.o `test -f 'common/guac_rect.c' || echo '$(srcdir)/'`common/guac_rect.c
 
-client_suite.obj: client/client_suite.c
- at am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT client_suite.obj -MD -MP -MF $(DEPDIR)/client_suite.Tpo -c -o client_suite.obj `if test -f 'client/client_suite.c'; then $(CYGPATH_W) 'client/client_suite.c'; else $(CYGPATH_W) '$(srcdir)/client/client_suite.c'; fi`
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/client_suite.Tpo $(DEPDIR)/client_suite.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='client/client_suite.c' object='client_suite.obj' libtool=no @AMDEPBACKSLASH@
+common/test_libguac-guac_rect.obj: common/guac_rect.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT common/test_libguac-guac_rect.obj -MD -MP -MF common/$(DEPDIR)/test_libguac-guac_rect.Tpo -c -o common/test_libguac-guac_rect.obj `if test -f 'common/guac_rect.c'; then $(CYGPATH_W) 'common/guac_rect.c'; else $(CYGPATH_W) '$(srcdir)/common/guac_rect.c'; fi`
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) common/$(DEPDIR)/test_libguac-guac_rect.Tpo common/$(DEPDIR)/test_libguac-guac_rect.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='common/guac_rect.c' object='common/test_libguac-guac_rect.obj' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o client_suite.obj `if test -f 'client/client_suite.c'; then $(CYGPATH_W) 'client/client_suite.c'; else $(CYGPATH_W) '$(srcdir)/client/client_suite.c'; fi`
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o common/test_libguac-guac_rect.obj `if test -f 'common/guac_rect.c'; then $(CYGPATH_W) 'common/guac_rect.c'; else $(CYGPATH_W) '$(srcdir)/common/guac_rect.c'; fi`
 
-buffer_pool.o: client/buffer_pool.c
- at am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT buffer_pool.o -MD -MP -MF $(DEPDIR)/buffer_pool.Tpo -c -o buffer_pool.o `test -f 'client/buffer_pool.c' || echo '$(srcdir)/'`client/buffer_pool.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/buffer_pool.Tpo $(DEPDIR)/buffer_pool.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='client/buffer_pool.c' object='buffer_pool.o' libtool=no @AMDEPBACKSLASH@
+protocol/test_libguac-suite.o: protocol/suite.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT protocol/test_libguac-suite.o -MD -MP -MF protocol/$(DEPDIR)/test_libguac-suite.Tpo -c -o protocol/test_libguac-suite.o `test -f 'protocol/suite.c' || echo '$(srcdir)/'`protocol/suite.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) protocol/$(DEPDIR)/test_libguac-suite.Tpo protocol/$(DEPDIR)/test_libguac-suite.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='protocol/suite.c' object='protocol/test_libguac-suite.o' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o buffer_pool.o `test -f 'client/buffer_pool.c' || echo '$(srcdir)/'`client/buffer_pool.c
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o protocol/test_libguac-suite.o `test -f 'protocol/suite.c' || echo '$(srcdir)/'`protocol/suite.c
 
-buffer_pool.obj: client/buffer_pool.c
- at am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT buffer_pool.obj -MD -MP -MF $(DEPDIR)/buffer_pool.Tpo -c -o buffer_pool.obj `if test -f 'client/buffer_pool.c'; then $(CYGPATH_W) 'client/buffer_pool.c'; else $(CYGPATH_W) '$(srcdir)/client/buffer_pool.c'; fi`
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/buffer_pool.Tpo $(DEPDIR)/buffer_pool.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='client/buffer_pool.c' object='buffer_pool.obj' libtool=no @AMDEPBACKSLASH@
+protocol/test_libguac-suite.obj: protocol/suite.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT protocol/test_libguac-suite.obj -MD -MP -MF protocol/$(DEPDIR)/test_libguac-suite.Tpo -c -o protocol/test_libguac-suite.obj `if test -f 'protocol/suite.c'; then $(CYGPATH_W) 'protocol/suite.c'; else $(CYGPATH_W) '$(srcdir)/protocol/suite.c'; fi`
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) protocol/$(DEPDIR)/test_libguac-suite.Tpo protocol/$(DEPDIR)/test_libguac-suite.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='protocol/suite.c' object='protocol/test_libguac-suite.obj' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o buffer_pool.obj `if test -f 'client/buffer_pool.c'; then $(CYGPATH_W) 'client/buffer_pool.c'; else $(CYGPATH_W) '$(srcdir)/client/buffer_pool.c'; fi`
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o protocol/test_libguac-suite.obj `if test -f 'protocol/suite.c'; then $(CYGPATH_W) 'protocol/suite.c'; else $(CYGPATH_W) '$(srcdir)/protocol/suite.c'; fi`
 
-layer_pool.o: client/layer_pool.c
- at am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT layer_pool.o -MD -MP -MF $(DEPDIR)/layer_pool.Tpo -c -o layer_pool.o `test -f 'client/layer_pool.c' || echo '$(srcdir)/'`client/layer_pool.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/layer_pool.Tpo $(DEPDIR)/layer_pool.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='client/layer_pool.c' object='layer_pool.o' libtool=no @AMDEPBACKSLASH@
+protocol/test_libguac-base64_decode.o: protocol/base64_decode.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT protocol/test_libguac-base64_decode.o -MD -MP -MF protocol/$(DEPDIR)/test_libguac-base64_decode.Tpo -c -o protocol/test_libguac-base64_decode.o `test -f 'protocol/base64_decode.c' || echo '$(srcdir)/'`protocol/base64_decode.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) protocol/$(DEPDIR)/test_libguac-base64_decode.Tpo protocol/$(DEPDIR)/test_libguac-base64_decode.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='protocol/base64_decode.c' object='protocol/test_libguac-base64_decode.o' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o layer_pool.o `test -f 'client/layer_pool.c' || echo '$(srcdir)/'`client/layer_pool.c
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o protocol/test_libguac-base64_decode.o `test -f 'protocol/base64_decode.c' || echo '$(srcdir)/'`protocol/base64_decode.c
 
-layer_pool.obj: client/layer_pool.c
- at am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT layer_pool.obj -MD -MP -MF $(DEPDIR)/layer_pool.Tpo -c -o layer_pool.obj `if test -f 'client/layer_pool.c'; then $(CYGPATH_W) 'client/layer_pool.c'; else $(CYGPATH_W) '$(srcdir)/client/layer_pool.c'; fi`
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/layer_pool.Tpo $(DEPDIR)/layer_pool.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='client/layer_pool.c' object='layer_pool.obj' libtool=no @AMDEPBACKSLASH@
+protocol/test_libguac-base64_decode.obj: protocol/base64_decode.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT protocol/test_libguac-base64_decode.obj -MD -MP -MF protocol/$(DEPDIR)/test_libguac-base64_decode.Tpo -c -o protocol/test_libguac-base64_decode.obj `if test -f 'protocol/base64_decode.c'; then $(CYGPATH_W) 'protocol/base64_decode.c'; else $(CYGPATH_W) '$(srcdir)/protocol/base64_decode.c'; fi`
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) protocol/$(DEPDIR)/test_libguac-base64_decode.Tpo protocol/$(DEPDIR)/test_libguac-base64_decode.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='protocol/base64_decode.c' object='protocol/test_libguac-base64_decode.obj' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o layer_pool.obj `if test -f 'client/layer_pool.c'; then $(CYGPATH_W) 'client/layer_pool.c'; else $(CYGPATH_W) '$(srcdir)/client/layer_pool.c'; fi`
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o protocol/test_libguac-base64_decode.obj `if test -f 'protocol/base64_decode.c'; then $(CYGPATH_W) 'protocol/base64_decode.c'; else $(CYGPATH_W) '$(srcdir)/protocol/base64_decode.c'; fi`
 
-suite.o: protocol/suite.c
- at am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT suite.o -MD -MP -MF $(DEPDIR)/suite.Tpo -c -o suite.o `test -f 'protocol/suite.c' || echo '$(srcdir)/'`protocol/suite.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/suite.Tpo $(DEPDIR)/suite.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='protocol/suite.c' object='suite.o' libtool=no @AMDEPBACKSLASH@
+protocol/test_libguac-instruction_parse.o: protocol/instruction_parse.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT protocol/test_libguac-instruction_parse.o -MD -MP -MF protocol/$(DEPDIR)/test_libguac-instruction_parse.Tpo -c -o protocol/test_libguac-instruction_parse.o `test -f 'protocol/instruction_parse.c' || echo '$(srcdir)/'`protocol/instruction_parse.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) protocol/$(DEPDIR)/test_libguac-instruction_parse.Tpo protocol/$(DEPDIR)/test_libguac-instruction_parse.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='protocol/instruction_parse.c' object='protocol/test_libguac-instruction_parse.o' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o suite.o `test -f 'protocol/suite.c' || echo '$(srcdir)/'`protocol/suite.c
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o protocol/test_libguac-instruction_parse.o `test -f 'protocol/instruction_parse.c' || echo '$(srcdir)/'`protocol/instruction_parse.c
 
-suite.obj: protocol/suite.c
- at am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT suite.obj -MD -MP -MF $(DEPDIR)/suite.Tpo -c -o suite.obj `if test -f 'protocol/suite.c'; then $(CYGPATH_W) 'protocol/suite.c'; else $(CYGPATH_W) '$(srcdir)/protocol/suite.c'; fi`
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/suite.Tpo $(DEPDIR)/suite.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='protocol/suite.c' object='suite.obj' libtool=no @AMDEPBACKSLASH@
+protocol/test_libguac-instruction_parse.obj: protocol/instruction_parse.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT protocol/test_libguac-instruction_parse.obj -MD -MP -MF protocol/$(DEPDIR)/test_libguac-instruction_parse.Tpo -c -o protocol/test_libguac-instruction_parse.obj `if test -f 'protocol/instruction_parse.c'; then $(CYGPATH_W) 'protocol/instruction_parse.c'; else $(CYGPATH_W) '$(srcdir)/protocol/instruction_parse.c'; fi`
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) protocol/$(DEPDIR)/test_libguac-instruction_parse.Tpo protocol/$(DEPDIR)/test_libguac-instruction_parse.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='protocol/instruction_parse.c' object='protocol/test_libguac-instruction_parse.obj' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o suite.obj `if test -f 'protocol/suite.c'; then $(CYGPATH_W) 'protocol/suite.c'; else $(CYGPATH_W) '$(srcdir)/protocol/suite.c'; fi`
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o protocol/test_libguac-instruction_parse.obj `if test -f 'protocol/instruction_parse.c'; then $(CYGPATH_W) 'protocol/instruction_parse.c'; else $(CYGPATH_W) '$(srcdir)/protocol/instruction_parse.c'; fi`
 
-instruction_read.o: protocol/instruction_read.c
- at am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT instruction_read.o -MD -MP -MF $(DEPDIR)/instruction_read.Tpo -c -o instruction_read.o `test -f 'protocol/instruction_read.c' || echo '$(srcdir)/'`protocol/instruction_read.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/instruction_read.Tpo $(DEPDIR)/instruction_read.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='protocol/instruction_read.c' object='instruction_read.o' libtool=no @AMDEPBACKSLASH@
+protocol/test_libguac-instruction_read.o: protocol/instruction_read.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT protocol/test_libguac-instruction_read.o -MD -MP -MF protocol/$(DEPDIR)/test_libguac-instruction_read.Tpo -c -o protocol/test_libguac-instruction_read.o `test -f 'protocol/instruction_read.c' || echo '$(srcdir)/'`protocol/instruction_read.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) protocol/$(DEPDIR)/test_libguac-instruction_read.Tpo protocol/$(DEPDIR)/test_libguac-instruction_read.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='protocol/instruction_read.c' object='protocol/test_libguac-instruction_read.o' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o instruction_read.o `test -f 'protocol/instruction_read.c' || echo '$(srcdir)/'`protocol/instruction_read.c
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o protocol/test_libguac-instruction_read.o `test -f 'protocol/instruction_read.c' || echo '$(srcdir)/'`protocol/instruction_read.c
 
-instruction_read.obj: protocol/instruction_read.c
- at am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT instruction_read.obj -MD -MP -MF $(DEPDIR)/instruction_read.Tpo -c -o instruction_read.obj `if test -f 'protocol/instruction_read.c'; then $(CYGPATH_W) 'protocol/instruction_read.c'; else $(CYGPATH_W) '$(srcdir)/protocol/instruction_read.c'; fi`
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/instruction_read.Tpo $(DEPDIR)/instruction_read.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='protocol/instruction_read.c' object='instruction_read.obj' libtool=no @AMDEPBACKSLASH@
+protocol/test_libguac-instruction_read.obj: protocol/instruction_read.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT protocol/test_libguac-instruction_read.obj -MD -MP -MF protocol/$(DEPDIR)/test_libguac-instruction_read.Tpo -c -o protocol/test_libguac-instruction_read.obj `if test -f 'protocol/instruction_read.c'; then $(CYGPATH_W) 'protocol/instruction_read.c'; else $(CYGPATH_W) '$(srcdir)/protocol/instruction_read.c'; fi`
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) protocol/$(DEPDIR)/test_libguac-instruction_read.Tpo protocol/$(DEPDIR)/test_libguac-instruction_read.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='protocol/instruction_read.c' object='protocol/test_libguac-instruction_read.obj' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o instruction_read.obj `if test -f 'protocol/instruction_read.c'; then $(CYGPATH_W) 'protocol/instruction_read.c'; else $(CYGPATH_W) '$(srcdir)/protocol/instruction_read.c'; fi`
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o protocol/test_libguac-instruction_read.obj `if test -f 'protocol/instruction_read.c'; then $(CYGPATH_W) 'protocol/instruction_read.c'; else $(CYGPATH_W) '$(srcdir)/protocol/instruction_read.c'; fi`
 
-instruction_write.o: protocol/instruction_write.c
- at am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT instruction_write.o -MD -MP -MF $(DEPDIR)/instruction_write.Tpo -c -o instruction_write.o `test -f 'protocol/instruction_write.c' || echo '$(srcdir)/'`protocol/instruction_write.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/instruction_write.Tpo $(DEPDIR)/instruction_write.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='protocol/instruction_write.c' object='instruction_write.o' libtool=no @AMDEPBACKSLASH@
+protocol/test_libguac-instruction_write.o: protocol/instruction_write.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT protocol/test_libguac-instruction_write.o -MD -MP -MF protocol/$(DEPDIR)/test_libguac-instruction_write.Tpo -c -o protocol/test_libguac-instruction_write.o `test -f 'protocol/instruction_write.c' || echo '$(srcdir)/'`protocol/instruction_write.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) protocol/$(DEPDIR)/test_libguac-instruction_write.Tpo protocol/$(DEPDIR)/test_libguac-instruction_write.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='protocol/instruction_write.c' object='protocol/test_libguac-instruction_write.o' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o instruction_write.o `test -f 'protocol/instruction_write.c' || echo '$(srcdir)/'`protocol/instruction_write.c
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o protocol/test_libguac-instruction_write.o `test -f 'protocol/instruction_write.c' || echo '$(srcdir)/'`protocol/instruction_write.c
 
-instruction_write.obj: protocol/instruction_write.c
- at am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT instruction_write.obj -MD -MP -MF $(DEPDIR)/instruction_write.Tpo -c -o instruction_write.obj `if test -f 'protocol/instruction_write.c'; then $(CYGPATH_W) 'protocol/instruction_write.c'; else $(CYGPATH_W) '$(srcdir)/protocol/instruction_write.c'; fi`
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/instruction_write.Tpo $(DEPDIR)/instruction_write.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='protocol/instruction_write.c' object='instruction_write.obj' libtool=no @AMDEPBACKSLASH@
+protocol/test_libguac-instruction_write.obj: protocol/instruction_write.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT protocol/test_libguac-instruction_write.obj -MD -MP -MF protocol/$(DEPDIR)/test_libguac-instruction_write.Tpo -c -o protocol/test_libguac-instruction_write.obj `if test -f 'protocol/instruction_write.c'; then $(CYGPATH_W) 'protocol/instruction_write.c'; else $(CYGPATH_W) '$(srcdir)/protocol/instruction_write.c'; fi`
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) protocol/$(DEPDIR)/test_libguac-instruction_write.Tpo protocol/$(DEPDIR)/test_libguac-instruction_write.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='protocol/instruction_write.c' object='protocol/test_libguac-instruction_write.obj' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o instruction_write.obj `if test -f 'protocol/instruction_write.c'; then $(CYGPATH_W) 'protocol/instruction_write.c'; else $(CYGPATH_W) '$(srcdir)/protocol/instruction_write.c'; fi`
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o protocol/test_libguac-instruction_write.obj `if test -f 'protocol/instruction_write.c'; then $(CYGPATH_W) 'protocol/instruction_write.c'; else $(CYGPATH_W) '$(srcdir)/protocol/instruction_write.c'; fi`
 
-nest_write.o: protocol/nest_write.c
- at am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nest_write.o -MD -MP -MF $(DEPDIR)/nest_write.Tpo -c -o nest_write.o `test -f 'protocol/nest_write.c' || echo '$(srcdir)/'`protocol/nest_write.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/nest_write.Tpo $(DEPDIR)/nest_write.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='protocol/nest_write.c' object='nest_write.o' libtool=no @AMDEPBACKSLASH@
+protocol/test_libguac-nest_write.o: protocol/nest_write.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT protocol/test_libguac-nest_write.o -MD -MP -MF protocol/$(DEPDIR)/test_libguac-nest_write.Tpo -c -o protocol/test_libguac-nest_write.o `test -f 'protocol/nest_write.c' || echo '$(srcdir)/'`protocol/nest_write.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) protocol/$(DEPDIR)/test_libguac-nest_write.Tpo protocol/$(DEPDIR)/test_libguac-nest_write.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='protocol/nest_write.c' object='protocol/test_libguac-nest_write.o' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nest_write.o `test -f 'protocol/nest_write.c' || echo '$(srcdir)/'`protocol/nest_write.c
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o protocol/test_libguac-nest_write.o `test -f 'protocol/nest_write.c' || echo '$(srcdir)/'`protocol/nest_write.c
 
-nest_write.obj: protocol/nest_write.c
- at am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nest_write.obj -MD -MP -MF $(DEPDIR)/nest_write.Tpo -c -o nest_write.obj `if test -f 'protocol/nest_write.c'; then $(CYGPATH_W) 'protocol/nest_write.c'; else $(CYGPATH_W) '$(srcdir)/protocol/nest_write.c'; fi`
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/nest_write.Tpo $(DEPDIR)/nest_write.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='protocol/nest_write.c' object='nest_write.obj' libtool=no @AMDEPBACKSLASH@
+protocol/test_libguac-nest_write.obj: protocol/nest_write.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT protocol/test_libguac-nest_write.obj -MD -MP -MF protocol/$(DEPDIR)/test_libguac-nest_write.Tpo -c -o protocol/test_libguac-nest_write.obj `if test -f 'protocol/nest_write.c'; then $(CYGPATH_W) 'protocol/nest_write.c'; else $(CYGPATH_W) '$(srcdir)/protocol/nest_write.c'; fi`
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) protocol/$(DEPDIR)/test_libguac-nest_write.Tpo protocol/$(DEPDIR)/test_libguac-nest_write.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='protocol/nest_write.c' object='protocol/test_libguac-nest_write.obj' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nest_write.obj `if test -f 'protocol/nest_write.c'; then $(CYGPATH_W) 'protocol/nest_write.c'; else $(CYGPATH_W) '$(srcdir)/protocol/nest_write.c'; fi`
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o protocol/test_libguac-nest_write.obj `if test -f 'protocol/nest_write.c'; then $(CYGPATH_W) 'protocol/nest_write.c'; else $(CYGPATH_W) '$(srcdir)/protocol/nest_write.c'; fi`
 
-util_suite.o: util/util_suite.c
- at am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT util_suite.o -MD -MP -MF $(DEPDIR)/util_suite.Tpo -c -o util_suite.o `test -f 'util/util_suite.c' || echo '$(srcdir)/'`util/util_suite.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/util_suite.Tpo $(DEPDIR)/util_suite.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='util/util_suite.c' object='util_suite.o' libtool=no @AMDEPBACKSLASH@
+util/test_libguac-util_suite.o: util/util_suite.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT util/test_libguac-util_suite.o -MD -MP -MF util/$(DEPDIR)/test_libguac-util_suite.Tpo -c -o util/test_libguac-util_suite.o `test -f 'util/util_suite.c' || echo '$(srcdir)/'`util/util_suite.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) util/$(DEPDIR)/test_libguac-util_suite.Tpo util/$(DEPDIR)/test_libguac-util_suite.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='util/util_suite.c' object='util/test_libguac-util_suite.o' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o util_suite.o `test -f 'util/util_suite.c' || echo '$(srcdir)/'`util/util_suite.c
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o util/test_libguac-util_suite.o `test -f 'util/util_suite.c' || echo '$(srcdir)/'`util/util_suite.c
 
-util_suite.obj: util/util_suite.c
- at am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT util_suite.obj -MD -MP -MF $(DEPDIR)/util_suite.Tpo -c -o util_suite.obj `if test -f 'util/util_suite.c'; then $(CYGPATH_W) 'util/util_suite.c'; else $(CYGPATH_W) '$(srcdir)/util/util_suite.c'; fi`
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/util_suite.Tpo $(DEPDIR)/util_suite.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='util/util_suite.c' object='util_suite.obj' libtool=no @AMDEPBACKSLASH@
+util/test_libguac-util_suite.obj: util/util_suite.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT util/test_libguac-util_suite.obj -MD -MP -MF util/$(DEPDIR)/test_libguac-util_suite.Tpo -c -o util/test_libguac-util_suite.obj `if test -f 'util/util_suite.c'; then $(CYGPATH_W) 'util/util_suite.c'; else $(CYGPATH_W) '$(srcdir)/util/util_suite.c'; fi`
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) util/$(DEPDIR)/test_libguac-util_suite.Tpo util/$(DEPDIR)/test_libguac-util_suite.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='util/util_suite.c' object='util/test_libguac-util_suite.obj' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o util_suite.obj `if test -f 'util/util_suite.c'; then $(CYGPATH_W) 'util/util_suite.c'; else $(CYGPATH_W) '$(srcdir)/util/util_suite.c'; fi`
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o util/test_libguac-util_suite.obj `if test -f 'util/util_suite.c'; then $(CYGPATH_W) 'util/util_suite.c'; else $(CYGPATH_W) '$(srcdir)/util/util_suite.c'; fi`
 
-guac_pool.o: util/guac_pool.c
- at am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT guac_pool.o -MD -MP -MF $(DEPDIR)/guac_pool.Tpo -c -o guac_pool.o `test -f 'util/guac_pool.c' || echo '$(srcdir)/'`util/guac_pool.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/guac_pool.Tpo $(DEPDIR)/guac_pool.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='util/guac_pool.c' object='guac_pool.o' libtool=no @AMDEPBACKSLASH@
+util/test_libguac-guac_pool.o: util/guac_pool.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT util/test_libguac-guac_pool.o -MD -MP -MF util/$(DEPDIR)/test_libguac-guac_pool.Tpo -c -o util/test_libguac-guac_pool.o `test -f 'util/guac_pool.c' || echo '$(srcdir)/'`util/guac_pool.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) util/$(DEPDIR)/test_libguac-guac_pool.Tpo util/$(DEPDIR)/test_libguac-guac_pool.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='util/guac_pool.c' object='util/test_libguac-guac_pool.o' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o guac_pool.o `test -f 'util/guac_pool.c' || echo '$(srcdir)/'`util/guac_pool.c
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o util/test_libguac-guac_pool.o `test -f 'util/guac_pool.c' || echo '$(srcdir)/'`util/guac_pool.c
 
-guac_pool.obj: util/guac_pool.c
- at am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT guac_pool.obj -MD -MP -MF $(DEPDIR)/guac_pool.Tpo -c -o guac_pool.obj `if test -f 'util/guac_pool.c'; then $(CYGPATH_W) 'util/guac_pool.c'; else $(CYGPATH_W) '$(srcdir)/util/guac_pool.c'; fi`
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/guac_pool.Tpo $(DEPDIR)/guac_pool.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='util/guac_pool.c' object='guac_pool.obj' libtool=no @AMDEPBACKSLASH@
+util/test_libguac-guac_pool.obj: util/guac_pool.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT util/test_libguac-guac_pool.obj -MD -MP -MF util/$(DEPDIR)/test_libguac-guac_pool.Tpo -c -o util/test_libguac-guac_pool.obj `if test -f 'util/guac_pool.c'; then $(CYGPATH_W) 'util/guac_pool.c'; else $(CYGPATH_W) '$(srcdir)/util/guac_pool.c'; fi`
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) util/$(DEPDIR)/test_libguac-guac_pool.Tpo util/$(DEPDIR)/test_libguac-guac_pool.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='util/guac_pool.c' object='util/test_libguac-guac_pool.obj' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o guac_pool.obj `if test -f 'util/guac_pool.c'; then $(CYGPATH_W) 'util/guac_pool.c'; else $(CYGPATH_W) '$(srcdir)/util/guac_pool.c'; fi`
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o util/test_libguac-guac_pool.obj `if test -f 'util/guac_pool.c'; then $(CYGPATH_W) 'util/guac_pool.c'; else $(CYGPATH_W) '$(srcdir)/util/guac_pool.c'; fi`
 
-guac_unicode.o: util/guac_unicode.c
- at am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT guac_unicode.o -MD -MP -MF $(DEPDIR)/guac_unicode.Tpo -c -o guac_unicode.o `test -f 'util/guac_unicode.c' || echo '$(srcdir)/'`util/guac_unicode.c
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/guac_unicode.Tpo $(DEPDIR)/guac_unicode.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='util/guac_unicode.c' object='guac_unicode.o' libtool=no @AMDEPBACKSLASH@
+util/test_libguac-guac_unicode.o: util/guac_unicode.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT util/test_libguac-guac_unicode.o -MD -MP -MF util/$(DEPDIR)/test_libguac-guac_unicode.Tpo -c -o util/test_libguac-guac_unicode.o `test -f 'util/guac_unicode.c' || echo '$(srcdir)/'`util/guac_unicode.c
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) util/$(DEPDIR)/test_libguac-guac_unicode.Tpo util/$(DEPDIR)/test_libguac-guac_unicode.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='util/guac_unicode.c' object='util/test_libguac-guac_unicode.o' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o guac_unicode.o `test -f 'util/guac_unicode.c' || echo '$(srcdir)/'`util/guac_unicode.c
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o util/test_libguac-guac_unicode.o `test -f 'util/guac_unicode.c' || echo '$(srcdir)/'`util/guac_unicode.c
 
-guac_unicode.obj: util/guac_unicode.c
- at am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT guac_unicode.obj -MD -MP -MF $(DEPDIR)/guac_unicode.Tpo -c -o guac_unicode.obj `if test -f 'util/guac_unicode.c'; then $(CYGPATH_W) 'util/guac_unicode.c'; else $(CYGPATH_W) '$(srcdir)/util/guac_unicode.c'; fi`
- at am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/guac_unicode.Tpo $(DEPDIR)/guac_unicode.Po
- at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='util/guac_unicode.c' object='guac_unicode.obj' libtool=no @AMDEPBACKSLASH@
+util/test_libguac-guac_unicode.obj: util/guac_unicode.c
+ at am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -MT util/test_libguac-guac_unicode.obj -MD -MP -MF util/$(DEPDIR)/test_libguac-guac_unicode.Tpo -c -o util/test_libguac-guac_unicode.obj `if test -f 'util/guac_unicode.c'; then $(CYGPATH_W) 'util/guac_unicode.c'; else $(CYGPATH_W) '$(srcdir)/util/guac_unicode.c'; fi`
+ at am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) util/$(DEPDIR)/test_libguac-guac_unicode.Tpo util/$(DEPDIR)/test_libguac-guac_unicode.Po
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='util/guac_unicode.c' object='util/test_libguac-guac_unicode.obj' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
- at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o guac_unicode.obj `if test -f 'util/guac_unicode.c'; then $(CYGPATH_W) 'util/guac_unicode.c'; else $(CYGPATH_W) '$(srcdir)/util/guac_unicode.c'; fi`
+ at am__fastdepCC_FALSE@	$(AM_V_CC at am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_libguac_CFLAGS) $(CFLAGS) -c -o util/test_libguac-guac_unicode.obj `if test -f 'util/guac_unicode.c'; then $(CYGPATH_W) 'util/guac_unicode.c'; else $(CYGPATH_W) '$(srcdir)/util/guac_unicode.c'; fi`
 
 mostlyclean-libtool:
 	-rm -f *.lo
@@ -524,26 +993,15 @@ mostlyclean-libtool:
 clean-libtool:
 	-rm -rf .libs _libs
 
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
-	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
-	unique=`for i in $$list; do \
-	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-	  done | \
-	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
-	      END { if (nonempty) { for (i in files) print i; }; }'`; \
-	mkid -fID $$unique
-tags: TAGS
-
-TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
-		$(TAGS_FILES) $(LISP)
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
 	set x; \
 	here=`pwd`; \
-	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
-	unique=`for i in $$list; do \
-	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-	  done | \
-	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
-	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	$(am__define_uniq_tagged_files); \
 	shift; \
 	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
 	  test -n "$$unique" || unique=$$empty_fix; \
@@ -555,15 +1013,11 @@ TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
 	      $$unique; \
 	  fi; \
 	fi
-ctags: CTAGS
-CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
-		$(TAGS_FILES) $(LISP)
-	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
-	unique=`for i in $$list; do \
-	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-	  done | \
-	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
-	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
 	test -z "$(CTAGS_ARGS)$$unique" \
 	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
 	     $$unique
@@ -572,102 +1026,187 @@ GTAGS:
 	here=`$(am__cd) $(top_builddir) && pwd` \
 	  && $(am__cd) $(top_srcdir) \
 	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
 
 distclean-tags:
 	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
 
-check-TESTS: $(TESTS)
-	@failed=0; all=0; xfail=0; xpass=0; skip=0; \
-	srcdir=$(srcdir); export srcdir; \
-	list=' $(TESTS) '; \
-	$(am__tty_colors); \
-	if test -n "$$list"; then \
-	  for tst in $$list; do \
-	    if test -f ./$$tst; then dir=./; \
-	    elif test -f $$tst; then dir=; \
-	    else dir="$(srcdir)/"; fi; \
-	    if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
-	      all=`expr $$all + 1`; \
-	      case " $(XFAIL_TESTS) " in \
-	      *[\ \	]$$tst[\ \	]*) \
-		xpass=`expr $$xpass + 1`; \
-		failed=`expr $$failed + 1`; \
-		col=$$red; res=XPASS; \
-	      ;; \
-	      *) \
-		col=$$grn; res=PASS; \
-	      ;; \
-	      esac; \
-	    elif test $$? -ne 77; then \
-	      all=`expr $$all + 1`; \
-	      case " $(XFAIL_TESTS) " in \
-	      *[\ \	]$$tst[\ \	]*) \
-		xfail=`expr $$xfail + 1`; \
-		col=$$lgn; res=XFAIL; \
-	      ;; \
-	      *) \
-		failed=`expr $$failed + 1`; \
-		col=$$red; res=FAIL; \
-	      ;; \
-	      esac; \
-	    else \
-	      skip=`expr $$skip + 1`; \
-	      col=$$blu; res=SKIP; \
-	    fi; \
-	    echo "$${col}$$res$${std}: $$tst"; \
-	  done; \
-	  if test "$$all" -eq 1; then \
-	    tests="test"; \
-	    All=""; \
-	  else \
-	    tests="tests"; \
-	    All="All "; \
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'.  Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+	rm -f $< $@
+	$(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+	@:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+	@$(am__set_TESTS_bases); \
+	am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+	redo_bases=`for i in $$bases; do \
+	              am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+	            done`; \
+	if test -n "$$redo_bases"; then \
+	  redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+	  redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+	  if $(am__make_dryrun); then :; else \
+	    rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
 	  fi; \
-	  if test "$$failed" -eq 0; then \
-	    if test "$$xfail" -eq 0; then \
-	      banner="$$All$$all $$tests passed"; \
-	    else \
-	      if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
-	      banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
-	    fi; \
-	  else \
-	    if test "$$xpass" -eq 0; then \
-	      banner="$$failed of $$all $$tests failed"; \
+	fi; \
+	if test -n "$$am__remaking_logs"; then \
+	  echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+	       "recursion detected" >&2; \
+	else \
+	  am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+	fi; \
+	if $(am__make_dryrun); then :; else \
+	  st=0;  \
+	  errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+	  for i in $$redo_bases; do \
+	    test -f $$i.trs && test -r $$i.trs \
+	      || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+	    test -f $$i.log && test -r $$i.log \
+	      || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+	  done; \
+	  test $$st -eq 0 || exit 1; \
+	fi
+	@$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+	ws='[ 	]'; \
+	results=`for b in $$bases; do echo $$b.trs; done`; \
+	test -n "$$results" || results=/dev/null; \
+	all=`  grep "^$$ws*:test-result:"           $$results | wc -l`; \
+	pass=` grep "^$$ws*:test-result:$$ws*PASS"  $$results | wc -l`; \
+	fail=` grep "^$$ws*:test-result:$$ws*FAIL"  $$results | wc -l`; \
+	skip=` grep "^$$ws*:test-result:$$ws*SKIP"  $$results | wc -l`; \
+	xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+	xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+	error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+	if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+	  success=true; \
+	else \
+	  success=false; \
+	fi; \
+	br='==================='; br=$$br$$br$$br$$br; \
+	result_count () \
+	{ \
+	    if test x"$$1" = x"--maybe-color"; then \
+	      maybe_colorize=yes; \
+	    elif test x"$$1" = x"--no-color"; then \
+	      maybe_colorize=no; \
 	    else \
-	      if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
-	      banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+	      echo "$@: invalid 'result_count' usage" >&2; exit 4; \
 	    fi; \
-	  fi; \
-	  dashes="$$banner"; \
-	  skipped=""; \
-	  if test "$$skip" -ne 0; then \
-	    if test "$$skip" -eq 1; then \
-	      skipped="($$skip test was not run)"; \
+	    shift; \
+	    desc=$$1 count=$$2; \
+	    if test $$maybe_colorize = yes && test $$count -gt 0; then \
+	      color_start=$$3 color_end=$$std; \
 	    else \
-	      skipped="($$skip tests were not run)"; \
+	      color_start= color_end=; \
 	    fi; \
-	    test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
-	      dashes="$$skipped"; \
-	  fi; \
-	  report=""; \
-	  if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
-	    report="Please report to $(PACKAGE_BUGREPORT)"; \
-	    test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
-	      dashes="$$report"; \
-	  fi; \
-	  dashes=`echo "$$dashes" | sed s/./=/g`; \
-	  if test "$$failed" -eq 0; then \
-	    col="$$grn"; \
-	  else \
-	    col="$$red"; \
-	  fi; \
-	  echo "$${col}$$dashes$${std}"; \
-	  echo "$${col}$$banner$${std}"; \
-	  test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \
-	  test -z "$$report" || echo "$${col}$$report$${std}"; \
-	  echo "$${col}$$dashes$${std}"; \
-	  test "$$failed" -eq 0; \
-	else :; fi
+	    echo "$${color_start}# $$desc $$count$${color_end}"; \
+	}; \
+	create_testsuite_report () \
+	{ \
+	  result_count $$1 "TOTAL:" $$all   "$$brg"; \
+	  result_count $$1 "PASS: " $$pass  "$$grn"; \
+	  result_count $$1 "SKIP: " $$skip  "$$blu"; \
+	  result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+	  result_count $$1 "FAIL: " $$fail  "$$red"; \
+	  result_count $$1 "XPASS:" $$xpass "$$red"; \
+	  result_count $$1 "ERROR:" $$error "$$mgn"; \
+	}; \
+	{								\
+	  echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" |	\
+	    $(am__rst_title);						\
+	  create_testsuite_report --no-color;				\
+	  echo;								\
+	  echo ".. contents:: :depth: 2";				\
+	  echo;								\
+	  for b in $$bases; do echo $$b; done				\
+	    | $(am__create_global_log);					\
+	} >$(TEST_SUITE_LOG).tmp || exit 1;				\
+	mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG);			\
+	if $$success; then						\
+	  col="$$grn";							\
+	 else								\
+	  col="$$red";							\
+	  test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG);		\
+	fi;								\
+	echo "$${col}$$br$${std}"; 					\
+	echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}";	\
+	echo "$${col}$$br$${std}"; 					\
+	create_testsuite_report --maybe-color;				\
+	echo "$$col$$br$$std";						\
+	if $$success; then :; else					\
+	  echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}";		\
+	  if test -n "$(PACKAGE_BUGREPORT)"; then			\
+	    echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}";	\
+	  fi;								\
+	  echo "$$col$$br$$std";					\
+	fi;								\
+	$$success || exit 1
+
+check-TESTS:
+	@list='$(RECHECK_LOGS)';           test -z "$$list" || rm -f $$list
+	@list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+	log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+	exit $$?;
+recheck: all $(check_PROGRAMS)
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	bases=`for i in $$bases; do echo $$i; done \
+	         | $(am__list_recheck_tests)` || exit 1; \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	log_list=`echo $$log_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+	        am__force_recheck=am--force-recheck \
+	        TEST_LOGS="$$log_list"; \
+	exit $$?
+test_libguac.log: test_libguac$(EXEEXT)
+	@p='test_libguac$(EXEEXT)'; \
+	b='test_libguac'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+	@p='$<'; \
+	$(am__set_b); \
+	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+ at am__EXEEXT_TRUE@.test$(EXEEXT).log:
+ at am__EXEEXT_TRUE@	@p='$<'; \
+ at am__EXEEXT_TRUE@	$(am__set_b); \
+ at am__EXEEXT_TRUE@	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+ at am__EXEEXT_TRUE@	--log-file $$b.log --trs-file $$b.trs \
+ at am__EXEEXT_TRUE@	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+ at am__EXEEXT_TRUE@	"$$tst" $(AM_TESTS_FD_REDIRECT)
 
 distdir: $(DISTFILES)
 	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
@@ -725,12 +1264,23 @@ install-strip:
 	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
 	fi
 mostlyclean-generic:
+	-test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+	-test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+	-test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
 
 clean-generic:
 
 distclean-generic:
 	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
 	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+	-rm -f client/$(DEPDIR)/$(am__dirstamp)
+	-rm -f client/$(am__dirstamp)
+	-rm -f common/$(DEPDIR)/$(am__dirstamp)
+	-rm -f common/$(am__dirstamp)
+	-rm -f protocol/$(DEPDIR)/$(am__dirstamp)
+	-rm -f protocol/$(am__dirstamp)
+	-rm -f util/$(DEPDIR)/$(am__dirstamp)
+	-rm -f util/$(am__dirstamp)
 
 maintainer-clean-generic:
 	@echo "This command is intended for maintainers to use"
@@ -741,7 +1291,7 @@ clean-am: clean-checkPROGRAMS clean-generic clean-libtool \
 	mostlyclean-am
 
 distclean: distclean-am
-	-rm -rf ./$(DEPDIR)
+	-rm -rf ./$(DEPDIR) client/$(DEPDIR) common/$(DEPDIR) protocol/$(DEPDIR) util/$(DEPDIR)
 	-rm -f Makefile
 distclean-am: clean-am distclean-compile distclean-generic \
 	distclean-tags
@@ -787,7 +1337,7 @@ install-ps-am:
 installcheck-am:
 
 maintainer-clean: maintainer-clean-am
-	-rm -rf ./$(DEPDIR)
+	-rm -rf ./$(DEPDIR) client/$(DEPDIR) common/$(DEPDIR) protocol/$(DEPDIR) util/$(DEPDIR)
 	-rm -f Makefile
 maintainer-clean-am: distclean-am maintainer-clean-generic
 
@@ -808,9 +1358,9 @@ uninstall-am:
 
 .MAKE: check-am install-am install-strip
 
-.PHONY: CTAGS GTAGS all all-am check check-TESTS check-am clean \
-	clean-checkPROGRAMS clean-generic clean-libtool ctags \
-	distclean distclean-compile distclean-generic \
+.PHONY: CTAGS GTAGS TAGS all all-am check check-TESTS check-am clean \
+	clean-checkPROGRAMS clean-generic clean-libtool cscopelist-am \
+	ctags ctags-am distclean distclean-compile distclean-generic \
 	distclean-libtool distclean-tags distdir dvi dvi-am html \
 	html-am info info-am install install-am install-data \
 	install-data-am install-dvi install-dvi-am install-exec \
@@ -820,7 +1370,7 @@ uninstall-am:
 	installcheck-am installdirs maintainer-clean \
 	maintainer-clean-generic mostlyclean mostlyclean-compile \
 	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
-	tags uninstall uninstall-am
+	recheck tags tags-am uninstall uninstall-am
 
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
diff --git a/tests/client/buffer_pool.c b/tests/client/buffer_pool.c
index 9757f99..72079d8 100644
--- a/tests/client/buffer_pool.c
+++ b/tests/client/buffer_pool.c
@@ -1,45 +1,33 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <CUnit/Basic.h>
+#include "config.h"
 
-#include <guacamole/client.h>
 #include "client_suite.h"
 
+#include <CUnit/Basic.h>
+#include <guacamole/client.h>
+#include <guacamole/layer.h>
+
 void test_buffer_pool() {
 
     guac_client* client;
diff --git a/tests/client/client_suite.c b/tests/client/client_suite.c
index b2c83c7..9cfaea1 100644
--- a/tests/client/client_suite.c
+++ b/tests/client/client_suite.c
@@ -1,44 +1,31 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <CUnit/Basic.h>
+#include "config.h"
 
 #include "client_suite.h"
 
+#include <CUnit/Basic.h>
+
 int client_suite_init() {
     return 0;
 }
diff --git a/tests/client/client_suite.h b/tests/client/client_suite.h
index 804c85d..d89f071 100644
--- a/tests/client/client_suite.h
+++ b/tests/client/client_suite.h
@@ -1,43 +1,31 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef _GUAC_TEST_CLIENT_SUITE_H
 #define _GUAC_TEST_CLIENT_SUITE_H
 
+#include "config.h"
+
 int register_client_suite();
 
 void test_layer_pool();
diff --git a/tests/client/layer_pool.c b/tests/client/layer_pool.c
index 85a1af7..3d49414 100644
--- a/tests/client/layer_pool.c
+++ b/tests/client/layer_pool.c
@@ -1,45 +1,33 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <CUnit/Basic.h>
+#include "config.h"
 
-#include <guacamole/client.h>
 #include "client_suite.h"
 
+#include <CUnit/Basic.h>
+#include <guacamole/client.h>
+#include <guacamole/layer.h>
+
 void test_layer_pool() {
 
     guac_client* client;
diff --git a/tests/common/common_suite.c b/tests/common/common_suite.c
new file mode 100644
index 0000000..5494aa8
--- /dev/null
+++ b/tests/common/common_suite.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "common_suite.h"
+
+#include <CUnit/Basic.h>
+
+int common_suite_init() {
+    return 0;
+}
+
+int common_suite_cleanup() {
+    return 0;
+}
+
+int register_common_suite() {
+
+    /* Add common test suite */
+    CU_pSuite suite = CU_add_suite("common",
+            common_suite_init, common_suite_cleanup);
+    if (suite == NULL) {
+        CU_cleanup_registry();
+        return CU_get_error();
+    }
+
+    /* Add tests */
+    if (
+        CU_add_test(suite, "guac-iconv", test_guac_iconv)  == NULL
+     || CU_add_test(suite, "guac-string", test_guac_string) == NULL
+     || CU_add_test(suite, "guac-rect", test_guac_rect) == NULL
+       ) {
+        CU_cleanup_registry();
+        return CU_get_error();
+    }
+
+    return 0;
+
+}
+
diff --git a/tests/common/common_suite.h b/tests/common/common_suite.h
new file mode 100644
index 0000000..6ed50d9
--- /dev/null
+++ b/tests/common/common_suite.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef _GUAC_TEST_COMMON_SUITE_H
+#define _GUAC_TEST_COMMON_SUITE_H
+
+/**
+ * Test suite containing unit tests for the "common" utility library included
+ * for the sake of simplifying guacamole-server development, but not included
+ * as part of libguac.
+ *
+ * @file common_suite.h
+ */
+
+#include "config.h"
+
+/**
+ * Registers the common test suite with CUnit.
+ */
+int register_common_suite();
+
+/**
+ * Unit test for string utility functions.
+ */
+void test_guac_string();
+
+/**
+ * Unit test for character conversion functions.
+ */
+void test_guac_iconv();
+
+/**
+ * Unit test for rectangle calculation functions.
+ */
+void test_guac_rect();
+
+#endif
+
diff --git a/tests/common/guac_iconv.c b/tests/common/guac_iconv.c
new file mode 100644
index 0000000..99c07db
--- /dev/null
+++ b/tests/common/guac_iconv.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "common_suite.h"
+#include "guac_iconv.h"
+
+#include <stdlib.h>
+#include <CUnit/Basic.h>
+
+static void test_conversion(
+        guac_iconv_read* reader,  unsigned char* in_string,  int in_length,
+        guac_iconv_write* writer, unsigned char* out_string, int out_length) {
+
+    char output[4096];
+    char input[4096];
+
+    const char* current_input = input;
+    char* current_output = output;
+
+    memcpy(input, in_string, in_length);
+    guac_iconv(reader, &current_input,  sizeof(input),
+               writer, &current_output, sizeof(output));
+
+    /* Verify output length */
+    CU_ASSERT_EQUAL(out_length, current_output - output);
+
+    /* Verify entire input read */
+    CU_ASSERT_EQUAL(in_length, current_input - input);
+
+    /* Verify output content */
+    CU_ASSERT_EQUAL(0, memcmp(output, out_string, out_length));
+
+}
+
+void test_guac_iconv() {
+
+    /* UTF8 for "papà è bello" */
+    unsigned char test_string_utf8[] = {
+        'p',  'a',  'p', 0xC3, 0xA0, ' ',
+        0xC3, 0xA8, ' ',
+        'b',  'e',  'l', 'l',  'o',
+        0x00
+    };
+
+    /* UTF16 for "papà è bello" */
+    unsigned char test_string_utf16[] = {
+        'p',  0x00, 'a', 0x00, 'p', 0x00, 0xE0, 0x00, ' ', 0x00,
+        0xE8, 0x00, ' ', 0x00,
+        'b',  0x00, 'e', 0x00, 'l', 0x00, 'l',  0x00, 'o', 0x00,
+        0x00, 0x00
+    };
+
+    /* ISO-8859-1 for "papà è bello" */
+    unsigned char test_string_iso8859_1[] = {
+        'p',  'a',  'p', 0xE0, ' ',
+        0xE8, ' ',
+        'b',  'e',  'l', 'l',  'o',
+        0x00
+    };
+
+    /* CP1252 for "papà è bello" */
+    unsigned char test_string_cp1252[] = {
+        'p',  'a',  'p', 0xE0, ' ',
+        0xE8, ' ',
+        'b',  'e',  'l', 'l',  'o',
+        0x00
+    };
+
+    /* UTF8 identity */
+    test_conversion(
+            GUAC_READ_UTF8,  test_string_utf8, sizeof(test_string_utf8),
+            GUAC_WRITE_UTF8, test_string_utf8, sizeof(test_string_utf8));
+
+    /* UTF16 identity */
+    test_conversion(
+            GUAC_READ_UTF16,  test_string_utf16, sizeof(test_string_utf16),
+            GUAC_WRITE_UTF16, test_string_utf16, sizeof(test_string_utf16));
+
+    /* UTF8 to UTF16 */
+    test_conversion(
+            GUAC_READ_UTF8,   test_string_utf8,  sizeof(test_string_utf8),
+            GUAC_WRITE_UTF16, test_string_utf16, sizeof(test_string_utf16));
+
+    /* UTF16 to UTF8 */
+    test_conversion(
+            GUAC_READ_UTF16, test_string_utf16, sizeof(test_string_utf16),
+            GUAC_WRITE_UTF8, test_string_utf8,  sizeof(test_string_utf8));
+
+    /* UTF16 to ISO-8859-1 */
+    test_conversion(
+            GUAC_READ_UTF16,      test_string_utf16,      sizeof(test_string_utf16),
+            GUAC_WRITE_ISO8859_1, test_string_iso8859_1,  sizeof(test_string_iso8859_1));
+
+    /* UTF16 to CP1252 */
+    test_conversion(
+            GUAC_READ_UTF16,   test_string_utf16,  sizeof(test_string_utf16),
+            GUAC_WRITE_CP1252, test_string_cp1252, sizeof(test_string_cp1252));
+
+    /* CP1252 to UTF8 */
+    test_conversion(
+            GUAC_READ_CP1252, test_string_cp1252, sizeof(test_string_cp1252),
+            GUAC_WRITE_UTF8,  test_string_utf8,   sizeof(test_string_utf8));
+
+    /* ISO-8859-1 to UTF8 */
+    test_conversion(
+            GUAC_READ_ISO8859_1, test_string_iso8859_1, sizeof(test_string_iso8859_1),
+            GUAC_WRITE_UTF8,     test_string_utf8,      sizeof(test_string_utf8));
+
+}
+
diff --git a/tests/common/guac_rect.c b/tests/common/guac_rect.c
new file mode 100644
index 0000000..df41dda
--- /dev/null
+++ b/tests/common/guac_rect.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "common_suite.h"
+#include "guac_rect.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <CUnit/Basic.h>
+
+void test_guac_rect() {
+
+    guac_common_rect max;
+
+    /*
+     *  Test init method
+     */
+    guac_common_rect_init(&max, 0, 0, 100, 100);
+    CU_ASSERT_EQUAL(0, max.x);
+    CU_ASSERT_EQUAL(0, max.y);
+    CU_ASSERT_EQUAL(100, max.width);
+    CU_ASSERT_EQUAL(100, max.height);
+
+    /*
+     * Test constrain method
+     */
+    guac_common_rect rect;
+    guac_common_rect_init(&rect, -10, -10, 110, 110);
+    guac_common_rect_init(&max, 0, 0, 100, 100);
+    guac_common_rect_constrain(&rect, &max);
+    CU_ASSERT_EQUAL(0, rect.x);
+    CU_ASSERT_EQUAL(0, rect.y);
+    CU_ASSERT_EQUAL(100, rect.width);
+    CU_ASSERT_EQUAL(100, rect.height);
+
+    /*
+     * Test extend method
+     */
+    guac_common_rect_init(&rect, 10, 10, 90, 90);
+    guac_common_rect_init(&max, 0, 0, 100, 100);
+    guac_common_rect_extend(&rect, &max);
+    CU_ASSERT_EQUAL(0, rect.x);
+    CU_ASSERT_EQUAL(0, rect.y);
+    CU_ASSERT_EQUAL(100, rect.width);
+    CU_ASSERT_EQUAL(100, rect.height);
+
+    /*
+     * Test adjust method
+     */
+    int cell_size = 16;
+
+    /* Simple adjustment */
+    guac_common_rect_init(&rect, 0, 0, 25, 25);
+    guac_common_rect_init(&max, 0, 0, 100, 100);
+    guac_common_rect_expand_to_grid(cell_size, &rect, &max);
+    CU_ASSERT_EQUAL(0, rect.x);
+    CU_ASSERT_EQUAL(0, rect.y);
+    CU_ASSERT_EQUAL(32, rect.width);
+    CU_ASSERT_EQUAL(32, rect.height);
+
+    /* Adjustment with moving of rect */
+    guac_common_rect_init(&rect, 75, 75, 25, 25);
+    guac_common_rect_init(&max, 0, 0, 100, 100);
+    guac_common_rect_expand_to_grid(cell_size, &rect, &max);
+    CU_ASSERT_EQUAL(max.width - 32, rect.x);
+    CU_ASSERT_EQUAL(max.height - 32, rect.y);
+    CU_ASSERT_EQUAL(32, rect.width);
+    CU_ASSERT_EQUAL(32, rect.height);
+
+    guac_common_rect_init(&rect, -5, -5, 25, 25);
+    guac_common_rect_init(&max, 0, 0, 100, 100);
+    guac_common_rect_expand_to_grid(cell_size, &rect, &max);
+    CU_ASSERT_EQUAL(0, rect.x);
+    CU_ASSERT_EQUAL(0, rect.y);
+    CU_ASSERT_EQUAL(32, rect.width);
+    CU_ASSERT_EQUAL(32, rect.height);
+
+    /* Adjustment with moving and clamping of rect */
+    guac_common_rect_init(&rect, 0, 0, 25, 15);
+    guac_common_rect_init(&max, 0, 5, 32, 15);
+    guac_common_rect_expand_to_grid(cell_size, &rect, &max);
+    CU_ASSERT_EQUAL(max.x, rect.x);
+    CU_ASSERT_EQUAL(max.y, rect.y);
+    CU_ASSERT_EQUAL(max.width, rect.width);
+    CU_ASSERT_EQUAL(max.height, rect.height);
+
+    /*
+     *  Rectangle intersection tests
+     */
+    guac_common_rect min;
+    guac_common_rect_init(&min, 10, 10, 10, 10);
+
+    /* Rectangle intersection - empty
+     * rectangle is outside */
+    guac_common_rect_init(&rect, 25, 25, 5, 5);
+    int res = guac_common_rect_intersects(&rect, &min);
+    CU_ASSERT_EQUAL(0, res);
+
+    /* Rectangle intersection - complete
+     * rectangle is completely inside */
+    guac_common_rect_init(&rect, 11, 11, 5, 5);
+    res = guac_common_rect_intersects(&rect, &min);
+    CU_ASSERT_EQUAL(2, res);
+
+    /* Rectangle intersection - partial
+     * rectangle intersects UL */
+    guac_common_rect_init(&rect, 8, 8, 5, 5);
+    res = guac_common_rect_intersects(&rect, &min);
+    CU_ASSERT_EQUAL(1, res);
+
+    /* Rectangle intersection - partial
+     * rectangle intersects LR */
+    guac_common_rect_init(&rect, 18, 18, 5, 5);
+    res = guac_common_rect_intersects(&rect, &min);
+    CU_ASSERT_EQUAL(1, res);
+
+    /* Rectangle intersection - complete
+     * rect intersects along UL but inside */
+    guac_common_rect_init(&rect, 10, 10, 5, 5);
+    res = guac_common_rect_intersects(&rect, &min);
+    CU_ASSERT_EQUAL(2, res);
+
+    /* Rectangle intersection - partial
+     * rectangle intersects along L but outside */
+    guac_common_rect_init(&rect, 5, 10, 5, 5);
+    res = guac_common_rect_intersects(&rect, &min);
+    CU_ASSERT_EQUAL(1, res);
+
+    /* Rectangle intersection - complete
+     * rectangle intersects along LR but rest is inside */
+    guac_common_rect_init(&rect, 15, 15, 5, 5);
+    res = guac_common_rect_intersects(&rect, &min);
+    CU_ASSERT_EQUAL(2, res);
+
+    /* Rectangle intersection - partial
+     * rectangle intersects along R but rest is outside */
+    guac_common_rect_init(&rect, 20, 10, 5, 5);
+    res = guac_common_rect_intersects(&rect, &min);
+    CU_ASSERT_EQUAL(1, res);
+
+    /* Rectangle intersection - partial
+     * rectangle encloses min; which is a partial intersection */
+    guac_common_rect_init(&rect, 5, 5, 20, 20);
+    res = guac_common_rect_intersects(&rect, &min);
+    CU_ASSERT_EQUAL(1, res);
+
+    /*
+     * Basic test of clip and split method
+     */
+    guac_common_rect_init(&min, 10, 10, 10, 10);
+    guac_common_rect cut;
+
+    /* Clip top */
+    guac_common_rect_init(&rect, 10, 5, 10, 10);
+    res = guac_common_rect_clip_and_split(&rect, &min, &cut);
+    CU_ASSERT_EQUAL(1, res);
+    CU_ASSERT_EQUAL(10, cut.x);
+    CU_ASSERT_EQUAL(5, cut.y);
+    CU_ASSERT_EQUAL(10, cut.width);
+    CU_ASSERT_EQUAL(5, cut.height);
+
+    CU_ASSERT_EQUAL(10, rect.x);
+    CU_ASSERT_EQUAL(10, rect.y);
+    CU_ASSERT_EQUAL(10, rect.width);
+    CU_ASSERT_EQUAL(5, rect.height);
+
+    /* Clip bottom */
+    guac_common_rect_init(&rect, 10, 15, 10, 10);
+    res = guac_common_rect_clip_and_split(&rect, &min, &cut);
+    CU_ASSERT_EQUAL(1, res);
+    CU_ASSERT_EQUAL(10, cut.x);
+    CU_ASSERT_EQUAL(20, cut.y);
+    CU_ASSERT_EQUAL(10, cut.width);
+    CU_ASSERT_EQUAL(5, cut.height);
+
+    CU_ASSERT_EQUAL(10, rect.x);
+    CU_ASSERT_EQUAL(15, rect.y);
+    CU_ASSERT_EQUAL(10, rect.width);
+    CU_ASSERT_EQUAL(5, rect.height);
+
+    /* Clip left */
+    guac_common_rect_init(&rect, 5, 10, 10, 10);
+    res = guac_common_rect_clip_and_split(&rect, &min, &cut);
+    CU_ASSERT_EQUAL(1, res);
+    CU_ASSERT_EQUAL(5, cut.x);
+    CU_ASSERT_EQUAL(10, cut.y);
+    CU_ASSERT_EQUAL(5, cut.width);
+    CU_ASSERT_EQUAL(10, cut.height);
+
+    CU_ASSERT_EQUAL(10, rect.x);
+    CU_ASSERT_EQUAL(10, rect.y);
+    CU_ASSERT_EQUAL(5, rect.width);
+    CU_ASSERT_EQUAL(10, rect.height);
+
+    /* Clip right */
+    guac_common_rect_init(&rect, 15, 10, 10, 10);
+    res = guac_common_rect_clip_and_split(&rect, &min, &cut);
+    CU_ASSERT_EQUAL(1, res);
+    CU_ASSERT_EQUAL(20, cut.x);
+    CU_ASSERT_EQUAL(10, cut.y);
+    CU_ASSERT_EQUAL(5, cut.width);
+    CU_ASSERT_EQUAL(10, cut.height);
+
+    CU_ASSERT_EQUAL(15, rect.x);
+    CU_ASSERT_EQUAL(10, rect.y);
+    CU_ASSERT_EQUAL(5, rect.width);
+    CU_ASSERT_EQUAL(10, rect.height);
+
+    /*
+     * Test a rectangle which completely covers the hole.
+     * Clip and split until done.
+     */
+    guac_common_rect_init(&rect, 5, 5, 20, 20);
+
+    /* Clip top */
+    res = guac_common_rect_clip_and_split(&rect, &min, &cut);
+    CU_ASSERT_EQUAL(1, res);
+    CU_ASSERT_EQUAL(5, cut.x);
+    CU_ASSERT_EQUAL(5, cut.y);
+    CU_ASSERT_EQUAL(20, cut.width);
+    CU_ASSERT_EQUAL(5, cut.height);
+
+    CU_ASSERT_EQUAL(5, rect.x);
+    CU_ASSERT_EQUAL(10, rect.y);
+    CU_ASSERT_EQUAL(20, rect.width);
+    CU_ASSERT_EQUAL(15, rect.height);
+
+    /* Clip left */
+    res = guac_common_rect_clip_and_split(&rect, &min, &cut);
+    CU_ASSERT_EQUAL(1, res);
+    CU_ASSERT_EQUAL(5, cut.x);
+    CU_ASSERT_EQUAL(10, cut.y);
+    CU_ASSERT_EQUAL(5, cut.width);
+    CU_ASSERT_EQUAL(15, cut.height);
+
+    CU_ASSERT_EQUAL(10, rect.x);
+    CU_ASSERT_EQUAL(10, rect.y);
+    CU_ASSERT_EQUAL(15, rect.width);
+    CU_ASSERT_EQUAL(15, rect.height);
+
+    /* Clip bottom */
+    res = guac_common_rect_clip_and_split(&rect, &min, &cut);
+    CU_ASSERT_EQUAL(1, res);
+    CU_ASSERT_EQUAL(10, cut.x);
+    CU_ASSERT_EQUAL(20, cut.y);
+    CU_ASSERT_EQUAL(15, cut.width);
+    CU_ASSERT_EQUAL(5, cut.height);
+
+    CU_ASSERT_EQUAL(10, rect.x);
+    CU_ASSERT_EQUAL(10, rect.y);
+    CU_ASSERT_EQUAL(15, rect.width);
+    CU_ASSERT_EQUAL(10, rect.height);
+
+    /* Clip right */
+    res = guac_common_rect_clip_and_split(&rect, &min, &cut);
+    CU_ASSERT_EQUAL(20, cut.x);
+    CU_ASSERT_EQUAL(10, cut.y);
+    CU_ASSERT_EQUAL(5, cut.width);
+    CU_ASSERT_EQUAL(10, cut.height);
+
+    CU_ASSERT_EQUAL(10, rect.x);
+    CU_ASSERT_EQUAL(10, rect.y);
+    CU_ASSERT_EQUAL(10, rect.width);
+    CU_ASSERT_EQUAL(10, rect.height);
+
+    /* Make sure nothing is left to do */
+    res = guac_common_rect_clip_and_split(&rect, &min, &cut);
+    CU_ASSERT_EQUAL(0, res);
+
+}
+
diff --git a/tests/common/guac_string.c b/tests/common/guac_string.c
new file mode 100644
index 0000000..3deb1ab
--- /dev/null
+++ b/tests/common/guac_string.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "common_suite.h"
+#include "guac_string.h"
+
+#include <stdlib.h>
+#include <CUnit/Basic.h>
+
+void test_guac_string() {
+
+    char** tokens;
+
+    /* Test occurrence counting */
+    CU_ASSERT_EQUAL(4, guac_count_occurrences("this is a test string", 's'));
+    CU_ASSERT_EQUAL(3, guac_count_occurrences("this is a test string", 'i'));
+    CU_ASSERT_EQUAL(0, guac_count_occurrences("", 's'));
+
+    /* Split test string */
+    tokens = guac_split("this is a test string", ' ');
+
+    CU_ASSERT_PTR_NOT_NULL(tokens);
+
+    /* Check resulting tokens */
+    CU_ASSERT_PTR_NOT_NULL_FATAL(tokens[0]);
+    CU_ASSERT_STRING_EQUAL("this", tokens[0]);
+
+    CU_ASSERT_PTR_NOT_NULL_FATAL(tokens[1]);
+    CU_ASSERT_STRING_EQUAL("is", tokens[1]);
+
+    CU_ASSERT_PTR_NOT_NULL_FATAL(tokens[2]);
+    CU_ASSERT_STRING_EQUAL("a", tokens[2]);
+
+    CU_ASSERT_PTR_NOT_NULL_FATAL(tokens[3]);
+    CU_ASSERT_STRING_EQUAL("test", tokens[3]);
+
+    CU_ASSERT_PTR_NOT_NULL_FATAL(tokens[4]);
+    CU_ASSERT_STRING_EQUAL("string", tokens[4]);
+
+    CU_ASSERT_PTR_NULL(tokens[5]);
+
+
+    /* Clean up */
+    free(tokens[0]);
+    free(tokens[1]);
+    free(tokens[2]);
+    free(tokens[3]);
+    free(tokens[4]);
+    free(tokens);
+
+}
+
diff --git a/tests/protocol/base64_decode.c b/tests/protocol/base64_decode.c
new file mode 100644
index 0000000..5673f70
--- /dev/null
+++ b/tests/protocol/base64_decode.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "suite.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <CUnit/Basic.h>
+#include <guacamole/protocol.h>
+
+void test_base64_decode() {
+
+    /* Test strings */
+    char test_HELLO[]     = "SEVMTE8=";
+    char test_AVOCADO[]   = "QVZPQ0FETw==";
+    char test_GUACAMOLE[] = "R1VBQ0FNT0xF";
+
+    /* Invalid strings */
+    char invalid1[] = "====";
+    char invalid2[] = "";
+
+    /* Test one character of padding */
+    CU_ASSERT_EQUAL(guac_protocol_decode_base64(test_HELLO), 5);
+    CU_ASSERT_NSTRING_EQUAL(test_HELLO, "HELLO", 5);
+
+    /* Test two characters of padding */
+    CU_ASSERT_EQUAL(guac_protocol_decode_base64(test_AVOCADO), 7);
+    CU_ASSERT_NSTRING_EQUAL(test_AVOCADO, "AVOCADO", 7);
+
+    /* Test three characters of padding */
+    CU_ASSERT_EQUAL(guac_protocol_decode_base64(test_GUACAMOLE), 9);
+    CU_ASSERT_NSTRING_EQUAL(test_GUACAMOLE, "GUACAMOLE", 9);
+
+    /* Verify invalid strings stop early as expected */
+    CU_ASSERT_EQUAL(guac_protocol_decode_base64(invalid1), 0);
+    CU_ASSERT_EQUAL(guac_protocol_decode_base64(invalid2), 0);
+
+}
+
diff --git a/tests/protocol/instruction_parse.c b/tests/protocol/instruction_parse.c
new file mode 100644
index 0000000..63eaf25
--- /dev/null
+++ b/tests/protocol/instruction_parse.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2013 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "suite.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <CUnit/Basic.h>
+#include <guacamole/instruction.h>
+
+void test_instruction_parse() {
+
+    /* Allocate instruction space */
+    guac_instruction* instruction = guac_instruction_alloc();
+    CU_ASSERT_PTR_NOT_NULL_FATAL(instruction);
+
+    /* Instruction input */
+    char buffer[] = "4.test,8.testdata,5.zxcvb,13.guacamoletest;XXXXXXXXXXXXXXXXXX";
+    char* current = buffer;
+
+    /* While data remains */
+    int remaining = sizeof(buffer)-1;
+    while (remaining > 18) {
+
+        /* Parse more data */
+        int parsed = guac_instruction_append(instruction, current, remaining);
+        if (parsed == 0)
+            break;
+
+        current += parsed;
+        remaining -= parsed;
+
+    }
+
+    CU_ASSERT_EQUAL(remaining, 18);
+    CU_ASSERT_EQUAL(instruction->state, GUAC_INSTRUCTION_PARSE_COMPLETE);
+
+    /* Parse is complete - no more data should be read */
+    CU_ASSERT_EQUAL(guac_instruction_append(instruction, current, 18), 0);
+    CU_ASSERT_EQUAL(instruction->state, GUAC_INSTRUCTION_PARSE_COMPLETE);
+
+    /* Validate resulting structure */
+    CU_ASSERT_EQUAL(instruction->argc, 3);
+    CU_ASSERT_PTR_NOT_NULL_FATAL(instruction->opcode);
+    CU_ASSERT_PTR_NOT_NULL_FATAL(instruction->argv[0]);
+    CU_ASSERT_PTR_NOT_NULL_FATAL(instruction->argv[1]);
+    CU_ASSERT_PTR_NOT_NULL_FATAL(instruction->argv[2]);
+
+    /* Validate resulting content */
+    CU_ASSERT_STRING_EQUAL(instruction->opcode,  "test");
+    CU_ASSERT_STRING_EQUAL(instruction->argv[0], "testdata");
+    CU_ASSERT_STRING_EQUAL(instruction->argv[1], "zxcvb");
+    CU_ASSERT_STRING_EQUAL(instruction->argv[2], "guacamoletest");
+
+}
+
diff --git a/tests/protocol/instruction_read.c b/tests/protocol/instruction_read.c
index 7645e0f..886cffe 100644
--- a/tests/protocol/instruction_read.c
+++ b/tests/protocol/instruction_read.c
@@ -1,52 +1,39 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "suite.h"
 
-#include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <CUnit/Basic.h>
+#include <unistd.h>
 
+#include <CUnit/Basic.h>
 #include <guacamole/error.h>
 #include <guacamole/instruction.h>
 #include <guacamole/protocol.h>
 #include <guacamole/socket.h>
 
-#include "suite.h"
-
 void test_instruction_read() {
 
     int rfd, wfd;
diff --git a/tests/protocol/instruction_write.c b/tests/protocol/instruction_write.c
index c7880e9..8334bd4 100644
--- a/tests/protocol/instruction_write.c
+++ b/tests/protocol/instruction_write.c
@@ -1,52 +1,39 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "suite.h"
 
-#include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <CUnit/Basic.h>
+#include <unistd.h>
 
+#include <CUnit/Basic.h>
 #include <guacamole/error.h>
 #include <guacamole/instruction.h>
 #include <guacamole/protocol.h>
 #include <guacamole/socket.h>
 
-#include "suite.h"
-
 void test_instruction_write() {
 
     int rfd, wfd;
@@ -77,7 +64,7 @@ void test_instruction_write() {
         socket = guac_socket_open(wfd);
 
         /* Write instruction */
-        guac_protocol_send_clipboard(socket, "a" UTF8_4 "b" UTF8_4 "c");
+        guac_protocol_send_name(socket, "a" UTF8_4 "b" UTF8_4 "c");
         guac_protocol_send_sync(socket, 12345);
         guac_socket_flush(socket);
 
@@ -89,7 +76,7 @@ void test_instruction_write() {
     else {
 
         char expected[] =
-            "9.clipboard,11.a" UTF8_4 "b" UTF8_4 "c;"
+            "4.name,11.a" UTF8_4 "b" UTF8_4 "c;"
             "4.sync,5.12345;";
 
         int numread;
diff --git a/tests/protocol/nest_write.c b/tests/protocol/nest_write.c
index 36249d3..ebe701c 100644
--- a/tests/protocol/nest_write.c
+++ b/tests/protocol/nest_write.c
@@ -1,52 +1,39 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "suite.h"
 
-#include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <CUnit/Basic.h>
+#include <unistd.h>
 
+#include <CUnit/Basic.h>
 #include <guacamole/error.h>
 #include <guacamole/instruction.h>
 #include <guacamole/protocol.h>
 #include <guacamole/socket.h>
 
-#include "suite.h"
-
 void test_nest_write() {
 
     int rfd, wfd;
@@ -81,7 +68,7 @@ void test_nest_write() {
         nested_socket = guac_socket_nest(socket, 0);
 
         /* Write instruction */
-        guac_protocol_send_clipboard(nested_socket, "a" UTF8_4 "b" UTF8_4 "c");
+        guac_protocol_send_name(nested_socket, "a" UTF8_4 "b" UTF8_4 "c");
         guac_protocol_send_sync(nested_socket, 12345);
         guac_socket_flush(nested_socket);
         guac_socket_flush(socket);
@@ -95,8 +82,8 @@ void test_nest_write() {
     else {
 
         char expected[] =
-            "4.nest,1.0,42."
-                "9.clipboard,11.a" UTF8_4 "b" UTF8_4 "c;"
+            "4.nest,1.0,37."
+                "4.name,11.a" UTF8_4 "b" UTF8_4 "c;"
                 "4.sync,5.12345;"
             ";";
 
diff --git a/tests/protocol/suite.c b/tests/protocol/suite.c
index 925433c..00c744c 100644
--- a/tests/protocol/suite.c
+++ b/tests/protocol/suite.c
@@ -1,44 +1,31 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <CUnit/Basic.h>
+#include "config.h"
 
 #include "suite.h"
 
+#include <CUnit/Basic.h>
+
 int protocol_suite_init() {
     return 0;
 }
@@ -59,7 +46,9 @@ int register_protocol_suite() {
 
     /* Add tests */
     if (
-        CU_add_test(suite, "instruction-read", test_instruction_read) == NULL
+        CU_add_test(suite, "base64-decode", test_base64_decode) == NULL
+     || CU_add_test(suite, "instruction-parse", test_instruction_parse) == NULL
+     || CU_add_test(suite, "instruction-read", test_instruction_read) == NULL
      || CU_add_test(suite, "instruction-write", test_instruction_write) == NULL
      || CU_add_test(suite, "nest-write", test_nest_write) == NULL
        ) {
diff --git a/tests/protocol/suite.h b/tests/protocol/suite.h
index f05d2c1..5c1e2cb 100644
--- a/tests/protocol/suite.h
+++ b/tests/protocol/suite.h
@@ -1,43 +1,31 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef _GUAC_TEST_PROTOCOL_SUITE_H
 #define _GUAC_TEST_PROTOCOL_SUITE_H
 
+#include "config.h"
+
 /* Unicode (UTF-8) strings */
 
 #define UTF8_1 "\xe7\x8a\xac"            /* One character    */
@@ -48,6 +36,8 @@
 
 int register_protocol_suite();
 
+void test_base64_decode();
+void test_instruction_parse();
 void test_instruction_read();
 void test_instruction_write();
 void test_nest_write();
diff --git a/tests/test_libguac.c b/tests/test_libguac.c
index 8648b9e..b1cfd1a 100644
--- a/tests/test_libguac.c
+++ b/tests/test_libguac.c
@@ -1,46 +1,34 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <CUnit/Basic.h>
+#include "config.h"
 
-#include "protocol/suite.h"
 #include "client/client_suite.h"
+#include "common/common_suite.h"
+#include "protocol/suite.h"
 #include "util/util_suite.h"
 
+#include <CUnit/Basic.h>
+
 int main() {
 
     /* Init registry */
@@ -51,6 +39,7 @@ int main() {
     register_protocol_suite();
     register_client_suite();
     register_util_suite();
+    register_common_suite();
 
     /* Run tests */
     CU_basic_set_mode(CU_BRM_VERBOSE);
diff --git a/tests/util/guac_pool.c b/tests/util/guac_pool.c
index a276431..ba98bb1 100644
--- a/tests/util/guac_pool.c
+++ b/tests/util/guac_pool.c
@@ -1,45 +1,32 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <CUnit/Basic.h>
+#include "config.h"
 
-#include <guacamole/pool.h>
 #include "util_suite.h"
 
+#include <CUnit/Basic.h>
+#include <guacamole/pool.h>
+
 #define UNSEEN          0 
 #define SEEN_PHASE_1    1
 #define SEEN_PHASE_2    2
diff --git a/tests/util/guac_unicode.c b/tests/util/guac_unicode.c
index 162813e..5162862 100644
--- a/tests/util/guac_unicode.c
+++ b/tests/util/guac_unicode.c
@@ -1,45 +1,32 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
- *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * Contributor(s):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <CUnit/Basic.h>
+#include "config.h"
 
-#include <guacamole/unicode.h>
 #include "util_suite.h"
 
+#include <CUnit/Basic.h>
+#include <guacamole/unicode.h>
+
 void test_guac_unicode() {
 
     int codepoint;
diff --git a/tests/util/util_suite.c b/tests/util/util_suite.c
index afcaf0d..a9fcc47 100644
--- a/tests/util/util_suite.c
+++ b/tests/util/util_suite.c
@@ -1,44 +1,31 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
-#include <CUnit/Basic.h>
+#include "config.h"
 
 #include "util_suite.h"
 
+#include <CUnit/Basic.h>
+
 int util_suite_init() {
     return 0;
 }
diff --git a/tests/util/util_suite.h b/tests/util/util_suite.h
index 02c4f88..88687e1 100644
--- a/tests/util/util_suite.h
+++ b/tests/util/util_suite.h
@@ -1,39 +1,25 @@
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is libguac.
+/*
+ * Copyright (C) 2013 Glyptodon LLC
  *
- * The Initial Developer of the Original Code is
- * Michael Jumper.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- * Contributor(s):
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 
 #ifndef _GUAC_TEST_UTIL_SUITE_H
 #define _GUAC_TEST_UTIL_SUITE_H
@@ -46,6 +32,7 @@
  * @file util_suite.h
  */
 
+#include "config.h"
 
 /**
  * A single Unicode character encoded as one byte with UTF-8.

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-edu/pkg-team/guacamole-server.git



More information about the debian-edu-commits mailing list