Compare commits

...

124 Commits

Author SHA1 Message Date
Virtually Nick
47b9360d46
GUACAMOLE-1714: Merge update guacenc for const parameters/values introduced in FFmpeg 5.0. 2023-02-02 20:36:32 -05:00
Mike Jumper
98c2a6adcb
GUACAMOLE-377: Merge correction ensuring users receive a proper frame boundary when joining. 2023-01-24 14:25:09 -08:00
Alex Leitner
3b0a9bac75 GUACAMOLE-377: Send a sync instruction to users when synchronizing surfaces. 2023-01-23 20:55:01 +00:00
Mike Jumper
f6893ed319 Merge 1.5.0 changes back to master. 2023-01-10 21:54:05 -08:00
James Muehlner
a5214c971a
GUACAMOLE-1604: Merge version number bumps for 1.5.0. 2023-01-10 17:16:43 -08:00
Mike Jumper
ccfcef8c0f GUACAMOLE-1604: Add explicit libtool version info for libguac-terminal. 2023-01-10 17:08:15 -08:00
Mike Jumper
1a7a57ed19 GUACAMOLE-1604: Update libtool version info for libguac (interfaces added and changed).
The only changed interface here is the guac_user_info struct, which now
has a "name" member.
2023-01-10 17:07:16 -08:00
Mike Jumper
eac064bde9 GUACAMOLE-1604: Bump version number to 1.5.0. 2023-01-10 17:02:06 -08:00
Virtually Nick
4afc1d85ce Merge 1.5.0 changes back to master. 2023-01-04 15:53:11 -05:00
Virtually Nick
818b5f79df
GUACAMOLE-1538: Merge add missing documentation for libguac-terminal. 2023-01-04 15:51:05 -05:00
Mike Jumper
8ef60bfa9d GUACAMOLE-1538: Document parameters of libguac-terminal handlers. 2023-01-04 12:40:21 -08:00
Mike Jumper
d90e0e97fe GUACAMOLE-1538: Add missing documentation for libguac-terminal functions. 2023-01-04 12:22:02 -08:00
Mike Jumper
ec7964e8fb GUACAMOLE-1538: Return number of bytes written for guac_terminal_write() and guac_terminal_printf(). 2023-01-04 12:05:02 -08:00
James Muehlner
add7ce361b Merge 1.5.0 changes back to master. 2022-11-29 03:46:57 +00:00
James Muehlner
7d16f67d6d
GUACAMOLE-1293: Merge fix for double acquisition/release of rwlock. 2022-11-28 14:31:44 -08:00
Mike Jumper
e3adb97085 GUACAMOLE-1293: Do not re-acquire __users_lock while already held for writing.
Per POSIX spec, the behavior of acquiring a read lock on a rwlock that's
already acquired for writing is undefined. From the documentation for
pthread_rwlock_rdlock():

"... Results are undefined if the calling thread holds a write lock on
rwlock at the time the call is made."
2022-11-28 13:37:41 -08:00
Mike Jumper
55941823ec Merge 1.5.0 changes back to master. 2022-11-25 23:24:08 -08:00
Mike Jumper
07acce8a76
GUACAMOLE-1293: Merge support for notifying when a user has joined/left a connection. 2022-11-25 23:23:13 -08:00
Virtually Nick
5b1677f21a GUACAMOLE-1293: Fix copy-pasta and style issues; add user ID to information passed to client. 2022-11-25 21:57:44 -05:00
Virtually Nick
623c398005 GUACAMOLE-1293: Change new user info member to simply "name" to clarify its purpose. 2022-11-24 18:13:06 -05:00
Virtually Nick
aa92239edd GUACAMOLE-1293: Rename new enum values to be more consistent with existing code. 2022-11-08 07:45:38 -05:00
Virtually Nick
897712c743 GUACAMOLE-1293: Update and add debug logging. 2022-11-08 07:45:38 -05:00
Virtually Nick
02b24d0101 GUACAMOLE-1293: Simplify the assignment of strings/constants. 2022-11-08 07:45:38 -05:00
Virtually Nick
26eadc37a3 GUACAMOLE-1293: Move to status code plus arguments for msg instruction. 2022-11-08 07:45:38 -05:00
Virtually Nick
6d7156bc70 GUACAMOLE-1293: Update struct member that stores human-readable name. 2022-11-08 07:45:38 -05:00
Virtually Nick
6312e1720d GUACAMOLE-1293: Add support for notifying owner of users joining and leaving. 2022-11-08 07:45:38 -05:00
Virtually Nick
cb7ae25177 GUACAMOLE-1293: Add support for the name handshake instruction. 2022-11-08 07:45:38 -05:00
Virtually Nick
a4adb3f5c0 GUACAMOLE-1293: Add protocol support for msg instruction. 2022-11-08 07:45:38 -05:00
Dan Fandrich
5cf408ebbb GUACAMOLE-1714: Adapt to const parameters of ffmpeg 5.0. 2022-11-07 12:16:35 -08:00
Mike Jumper
3ca6bb0a61
GUACAMOLE-1708: Merge correction to missing Czech keyboard character mapping. 2022-11-06 09:05:52 -08:00
Max
457a169c49 GUACAMOLE-1708: Added Czech keyboard keymap fix missing char 2022-11-06 12:17:21 +01:00
Mike Jumper
bad381cebe
GUACAMOLE-1708: Merge RDP support for Czech keyboard layout. 2022-11-05 08:56:56 -07:00
Max
6171da6d0b GUACAMOLE-1708: Added Czech keyboard keymap for RDP 2022-11-01 21:56:23 +01:00
Mike Jumper
067f2a91a0
GUACAMOLE-1682: Merge automatic newline normalization of terminal clipboard. 2022-10-18 12:41:48 -07:00
Alex Leitner
bc52485570 GUACAMOLE-1682: Normalize conflicting newline encodings in clipboards between Linux and Windows systems for ssh sessions. 2022-10-18 19:38:56 +00:00
Mike Jumper
b20afa275a
GUACAMOLE-1669: Merge fix for RSA key upgrade failure if FIPS mode is enabled. 2022-09-13 14:45:55 -07:00
James Muehlner
b096e47f57 GUACAMOLE-1669: Include ext-info-c in preferred KEX algorithms to ensure RSA key upgrades can happen. 2022-09-13 21:39:38 +00:00
Mike Jumper
4d211e0c9e
GUACAMOLE-1674: Merge changes removing NLA from negotiation if FIPS is enabled. 2022-09-08 09:44:47 -07:00
James Muehlner
dffbeac57a GUACAMOLE-1674: Warn about NLA mode if FIPS mode is enabled, or disable if possible. 2022-08-30 23:23:56 +00:00
Mike Jumper
0361adc01f
GUACAMOLE-1669: Merge FIPS support for SSH connections. 2022-08-24 15:29:46 -07:00
James Muehlner
1971a9dad2 GUACAMOLE-1669: Prefer FIPS compliant ciphers and algorithms when FIPS mode is enabled. 2022-08-24 22:23:46 +00:00
Virtually Nick
5dbf4820ab Merge 1.5.0 changes back to master. 2022-08-19 15:48:51 -04:00
Virtually Nick
b2eb13a178
GUACAMOLE-1540: Merge correct automated retrieval of Docker build dependencies. 2022-08-19 15:30:31 -04:00
Michael Jumper
2bc9d5ff01 GUACAMOLE-1540: Correct regex stripping of package version (major number may have multiple digits). 2022-08-19 12:12:29 -07:00
Michael Jumper
5918cc9f7c GUACAMOLE-1540: Ignore failures to find packages associated with libraries we build ourselves. 2022-08-19 12:12:29 -07:00
James Muehlner
15f6e9f678 Merge 1.5.0 changes back to master. 2022-08-16 18:48:31 +00:00
James Muehlner
b5addfe3da
GUACAMOLE-1540: Merge Alpine Linux docker base image with manual library builds. 2022-08-16 09:40:45 -07:00
Michael Jumper
7f4246b6d5 GUACAMOLE-1540: Manual build all core protocol libraries for Docker image using Alpine Linux base. 2022-08-16 08:39:54 -07:00
Virtually Nick
6ab82446bb
GUACAMOLE-1652: Merge only call SSL init functions when the library version requires it. 2022-07-30 07:36:37 -04:00
James Muehlner
9c93337d97 GUACAMOLE-1652: Migrate OpenSSL initialization to modern methods for OpenSSL >= 1.1.0. 2022-07-30 02:24:31 +00:00
James Muehlner
cdee93ae25 GUACAMOLE-1652: Only call SSL init functions when the library version requires it. 2022-07-30 02:22:36 +00:00
Mike Jumper
eee3ac092c
GUACAMOLE-1622: Merge correction to terminal resize regression. 2022-07-13 16:20:19 -07:00
Alex Leitner
5bb56ed5ba GUACAMOLE-1622: Restructured code to resolve scrollbar resizing bug where the scrollbar would clip off the side of the terminal. This fix also resolves the issue where the text would blur at certain intervals of resizing the window. 2022-07-13 21:57:47 +00:00
Mike Jumper
0aae5eeadb
GUACAMOLE-1636: Merge corrections to typos within RDP comments/documentation. 2022-07-13 13:55:05 -07:00
Jimmy
6d994db9d2 GUACAMOLE-1636: Fix a typo mistake invokved. 2022-07-13 23:47:13 +03:00
Jimmy
cba5484be0 GUACAMOLE-1636: Fix a typo mistake recieved. 2022-07-13 23:41:42 +03:00
Jimmy
4048dd4900 GUACAMOLE-1636: Fix a typo mistake assicated. 2022-07-13 23:32:12 +03:00
Jimmy
98556fbe2e GUACAMOLE-1636: Fix a typo mistake coordinare. 2022-07-13 23:24:06 +03:00
Jimmy
f438a36612 GUACAMOLE-1636: Fix a typo mistake synchonize. 2022-07-13 23:17:50 +03:00
Jimmy
e8d966aec6 GUACAMOLE-1636: Fix a typo mistake Versoin. 2022-07-13 23:10:36 +03:00
Jimmy
523532a52d GUACAMOLE-1636: Fix a typo mistake offscren. 2022-07-13 23:02:37 +03:00
Mike Jumper
51c640fdbd
GUACAMOLE-1436: Merge addition of missing FreeRDP winpr headers. 2022-07-05 12:09:00 -07:00
Virtually Nick
4cf1bfae0e
GUACAMOLE-377: Merge update unit tests for new prototype of guac_protocol_send_sync(). 2022-07-05 14:30:57 -04:00
Michael Jumper
9642afc468 GUACAMOLE-377: Update unit tests for new prototype of guac_protocol_send_sync().
The new guac_protocol_send_sync() requires an additional parameter: the
number of logical frames associated with the sync.
2022-07-05 10:58:38 -07:00
James Muehlner
ffb6c809be
GUACAMOLE-1622: Merge addition of margins to ssh sessions. 2022-06-22 09:32:00 -07:00
Alex Leitner
64ea9c4d1f GUACAMOLE-1622: Clarified comments to describe if param is a pointer. 2022-06-21 16:16:52 +00:00
Alex Leitner
a5834fd319 GUACAMOLE-1622: Separated logic into single responsibility functions. 2022-06-16 17:09:41 +00:00
Alex Leitner
1e9cd9137b GUACAMOLE-1622: Added margins to ssh sessions. 2022-06-15 16:59:20 +00:00
James Muehlner
d4cd9b3e3a
GUACAMOLE-377: Merge support for RemoteFX. 2022-06-09 17:41:23 -07:00
Michael Jumper
31f1b2c7c4 GUACAMOLE-377: Rename single-letter "e" event arguments variable to "args" for readability. 2022-06-09 09:02:11 -07:00
Michael Jumper
ce27936ed5 GUACAMOLE-377: Add frame boundaries around cursor set operations if otherwise absent. 2022-06-09 09:02:11 -07:00
Michael Jumper
b7f05b9e4f GUACAMOLE-377: Ensure backing surface of underlying FreeRDP GDI implementation is resized when desktop is resized. 2022-06-09 09:02:11 -07:00
Michael Jumper
d5761ad625 GUACAMOLE-377: Warn about required color depth only if actually overridden. 2022-06-09 09:02:11 -07:00
Michael Jumper
b26f9d64d6 GUACAMOLE-377: Clarify usage of EndPaint to detect frames. 2022-06-09 09:02:11 -07:00
Michael Jumper
da80163e24 GUACAMOLE-377: Enable graphics pipeline extension by default. 2022-06-09 09:02:11 -07:00
Michael Jumper
28396ae345 GUACAMOLE-377: Expect explicit RDP frame boundaries only after at least one frame boundary has been received. 2022-05-18 15:43:54 -07:00
Michael Jumper
a0e9f6ed9b GUACAMOLE-377: Leverage client timestamp tracking for RDP frame duration. 2022-05-18 15:43:54 -07:00
Michael Jumper
bde8cdee46 GUACAMOLE-377: Add general RDP support for frame markers. 2022-05-18 15:43:54 -07:00
Michael Jumper
669e02b4dc GUACAMOLE-377: Leverage RDPGFX to report remote frame statistics to the client. 2022-05-12 13:50:20 -07:00
Michael Jumper
52c8683bcf GUACAMOLE-377: Add protocol-level support for reporting remote frame statistics. 2022-05-12 13:50:20 -07:00
Michael Jumper
c19eab9691 GUACAMOLE-377: Revise processing lag calculations to consider cumulative processing lag. 2022-05-12 13:50:20 -07:00
Michael Jumper
dd85c54961 GUACAMOLE-377: Add handling for EndPaint required by software GDI implementation of RDPGFX. 2022-05-12 13:50:20 -07:00
Michael Jumper
c795bf9e4a GUACAMOLE-377: Control RemoteFX / GFX support with "enable-gfx" parameter. 2022-05-12 13:50:20 -07:00
Michael Jumper
c469300941 GUACAMOLE-377: Support for RDPGFX. 2022-05-12 13:50:20 -07:00
James Muehlner
81300052e0
GUACAMOLE-1595: Merge mouse mask initialization fix. 2022-05-02 17:24:58 -07:00
Michael Jumper
df4e5c6fdf GUACAMOLE-1595: Ensure all mouse buttons are initially released when terminal starts. 2022-05-03 00:20:08 +00:00
Virtually Nick
a175a3d902
GUACAMOLE-1312: Merge add Canadian French RDP keymap 2022-03-23 09:58:58 -04:00
Kaven Rousseau
9cbd768210 GUACAMOLE-1312: Added fr_ca keymap 2022-03-17 16:13:42 -04:00
Virtually Nick
c716a07abc Merge 1.5.0 changes back to master. 2022-03-17 15:24:48 -04:00
Virtually Nick
c880f02fe8
GUACAMOLE-1115: Merge ensure RDP print process does not block itself from completing. 2022-03-17 15:20:51 -04:00
Michael Jumper
ce88fa4d4a GUACAMOLE-1115: Forcibly kill any outstanding PDF filter job when cleaning up resources. 2022-03-17 18:35:38 +00:00
Michael Jumper
d734bac590 GUACAMOLE-1115: Do not hold general RDP message lock while waiting for print operations.
Holding the message lock will block handling of things like mouse and
keyboard events, as the message lock must be acquired before sending the
corresponding messages to the RDP server. If mouse and keyboard events
are blocked, then handling of further Guacamole instructions like "ack"
is also blocked. If the print job is blocked until an "ack" is received,
this results in deadlock.
2022-03-17 18:35:20 +00:00
Mike Jumper
75a11b05b2
GUACAMOLE-1543: Merge changes moving recording structures/functions to the public API. 2022-03-01 09:58:50 -08:00
James Muehlner
854b5ecbb8 GUACAMOLE-1543: Move recording functionality from common to libguac. 2022-03-01 04:01:44 +00:00
Mike Jumper
d8073f9b17
GUACAMOLE-1538: Merge corrections to libguac-terminal build and scope. 2022-02-28 16:56:04 -08:00
James Muehlner
46e813343e GUACAMOLE-1538: Only the core functionality of the terminal lib should be public. 2022-03-01 00:33:55 +00:00
James Muehlner
ad0155b5f5 GUACAMOLE-1538: Make it clear which functions are getters by adding _get_ to the name of each. 2022-02-24 12:02:36 -08:00
James Muehlner
0856e94ece GUACAMOLE-1538 Use dashes instead of underscores in filenames for consistency with libguac public API. 2022-02-24 11:12:05 -08:00
James Muehlner
1c97cdb115 GUACAMOLE-1538: Autogenerate docs for new library. 2022-02-23 13:49:32 -08:00
James Muehlner
ce2ffdf75f GUACAMOLE-1538: Improve code style and cleanliness. 2022-02-22 20:37:42 -08:00
James Muehlner
6dd33a8d90 GUACAMOLE-1538: Do not use terminal internals outside of terminal code. 2022-02-22 16:06:48 -08:00
James Muehlner
589e0ecd03 GUACAMOLE-1538 - Consolidate clipboard handling; opaque clipboard struct to avoid exposing internal guac_common_clipboard. 2022-02-22 14:04:47 -08:00
James Muehlner
037045a054 GUACAMOLE-1538: Explicitly include the common lib; ensure no undefined symbols. 2022-02-22 11:07:30 -08:00
James Muehlner
63bf5b329c GUACAMOLE-1538: Rename library to match conventions. 2022-02-22 09:41:08 -08:00
Mike Jumper
dc9dfe562f
GUACAMOLE-1540: Merge changes correcting Docker-specific search for FreeRDP install location. 2022-02-21 17:32:23 -08:00
James Muehlner
757928dfa1 GUACAMOLE-1540: Search for libfreerdp2 installation directly instead of checking links. 2022-02-21 16:57:48 -08:00
Virtually Nick
a0faa02616
GUACAMOLE-1538: Merge refactor libguac_terminal for easier extensibility, and migrate to shared library. 2022-02-21 14:30:40 -05:00
James Muehlner
44d76da21a GUACAMOLE-1538: Use an options struct instead of hardcoding params in constructor. 2022-02-21 11:27:20 -08:00
James Muehlner
05c6131cf3 GUACAMOLE-1538: Update libguac_terminal to be a shared library. 2022-02-21 11:27:13 -08:00
Mike Jumper
001347b4e8
GUACAMOLE-1540: Merge migration of guacd Docker image to Ubuntu 21.10. 2022-02-18 16:20:17 -08:00
James Muehlner
edce11fcb4 GUACAMOLE-1540: Build using Ubuntu 21.10 as a base instead of buster-slim. 2022-02-18 15:48:18 -08:00
James Muehlner
e78d589e25
GUACAMOLE-876: Merge null-check fix for RDP open file check. 2022-02-18 13:50:03 -08:00
Michael Jumper
06461cac53 GUACAMOLE-876: Test for open files only if filesystem has been allocated. 2022-02-18 13:40:15 -08:00
Virtually Nick
8e94b00587
GUACAMOLE-1495: Merge add keymap for Polish keyboard layout for RDP 2022-02-17 12:46:01 -05:00
Virtually Nick
9245d02bc5
GUACAMOLE-462: Merge create recordings/typescripts with group read permission. 2022-02-17 12:43:51 -05:00
Michael Jumper
4d41b38a24 GUACAMOLE-462: Create recordings/typescripts with group read permission.
Previously, all recordings/typescripts were strictly readable by the
service user that created them (guacd). This prevents reading by other
services like the Guacamole web application. Instead,
recordings/typescripts should at least be group-readable.
2022-02-17 09:25:28 -08:00
Mike Jumper
29535e6cb8
GUACAMOLE-876: Merge changes deferring reconnect-to-resize until active transfers are complete. 2022-02-02 09:56:41 -08:00
Virtually Nick
10126444bf GUACAMOLE-876: Avoid disrupting open files and active print jobs to update display. 2022-02-01 21:45:10 -05:00
Mike Jumper
23612720ce
GUACAMOLE-745: Merge support for OpenSSH-format private keys / Ed25519. 2022-01-12 11:38:12 -08:00
Joshua Roys
f84db7d166 GUACAMOLE-745: Support OpenSSH private keys & ED25519
Let libssh2 parse PEM and ssh-native keys. Requires libssh2 1.9.0+
compiled against a crypto backend supporting ed25519.
2022-01-12 09:02:11 -05:00
ClassicGOD
a1d0d0aea4
GUACAMOLE-1495: add entry for pl_pl_qwerty
Add pl_pl_qwerty.keymap to rdp_keymaps
2022-01-10 20:31:41 +01:00
ClassicGOD
534158c1cb
GUACAMOLE-1495: add pl_pl_qwerty keymap
Add keymap file for Polish keyboard layout
2022-01-10 20:28:11 +01:00
Mike Jumper
56dca9880d
GUACAMOLE-1435: Merge correction to FreeRDP plugin entrypoint return type. 2022-01-03 21:59:57 -08:00
Virtually Nick
703f258b06 GUACAMOLE-1435: Correctly return UINT for DVCPluginEntry 2022-01-03 20:15:11 -05:00
144 changed files with 3594 additions and 2004 deletions

View File

@ -56,5 +56,5 @@ tests/test_*
!tests/test_*.[ch]
# Generated docs
doc/doxygen-output
doc/*/doxygen-output

2
.gitignore vendored
View File

@ -44,7 +44,7 @@ configure
stamp-h1
# Generated docs
doc/doxygen-output
doc/*/doxygen-output
# IDE metadata
nbproject/

View File

@ -21,25 +21,32 @@
# Dockerfile for guacamole-server
#
# The Debian image that should be used as the basis for the guacd image
ARG DEBIAN_BASE_IMAGE=buster-slim
# The Alpine Linux image that should be used as the basis for the guacd image
ARG ALPINE_BASE_IMAGE=latest
FROM alpine:${ALPINE_BASE_IMAGE} AS builder
# Use Debian as base for the build
FROM debian:${DEBIAN_BASE_IMAGE} AS builder
# Install build dependencies
RUN apk add --no-cache \
autoconf \
automake \
build-base \
cairo-dev \
cmake \
git \
grep \
libjpeg-turbo-dev \
libpng-dev \
libtool \
libwebp-dev \
make \
openssl-dev \
pango-dev \
pulseaudio-dev \
util-linux-dev
#
# The Debian repository that should be preferred for dependencies (this will be
# added to /etc/apt/sources.list if not already present)
#
# NOTE: Due to limitations of the Docker image build process, this value is
# duplicated in an ARG in the second stage of the build.
#
ARG DEBIAN_RELEASE=buster-backports
# Add repository for specified Debian release if not already present in
# sources.list
RUN grep " ${DEBIAN_RELEASE} " /etc/apt/sources.list || echo >> /etc/apt/sources.list \
"deb http://deb.debian.org/debian ${DEBIAN_RELEASE} main contrib non-free"
# Copy source to container for sake of build
ARG BUILD_DIR=/tmp/guacamole-server
COPY . ${BUILD_DIR}
#
# Base directory for installed build artifacts.
@ -47,70 +54,99 @@ RUN grep " ${DEBIAN_RELEASE} " /etc/apt/sources.list || echo >> /etc/apt/sources
# NOTE: Due to limitations of the Docker image build process, this value is
# duplicated in an ARG in the second stage of the build.
#
ARG PREFIX_DIR=/usr/local/guacamole
ARG PREFIX_DIR=/opt/guacamole
# Build arguments
ARG BUILD_DIR=/tmp/guacd-docker-BUILD
ARG BUILD_DEPENDENCIES=" \
autoconf \
automake \
freerdp2-dev \
gcc \
libcairo2-dev \
libgcrypt-dev \
libjpeg62-turbo-dev \
libossp-uuid-dev \
libpango1.0-dev \
libpulse-dev \
libssh2-1-dev \
libssl-dev \
libtelnet-dev \
libtool \
libvncserver-dev \
libwebsockets-dev \
libwebp-dev \
make"
#
# Automatically select the latest versions of each core protocol support
# library (these can be overridden at build time if a specific version is
# needed)
#
ARG WITH_FREERDP='2(\.\d+)+'
ARG WITH_LIBSSH2='libssh2-\d+(\.\d+)+'
ARG WITH_LIBTELNET='\d+(\.\d+)+'
ARG WITH_LIBVNCCLIENT='LibVNCServer-\d+(\.\d+)+'
ARG WITH_LIBWEBSOCKETS='v\d+(\.\d+)+'
# Do not require interaction during build
ARG DEBIAN_FRONTEND=noninteractive
#
# Default build options for each core protocol support library, as well as
# guacamole-server itself (these can be overridden at build time if different
# options are needed)
#
# Bring build environment up to date and install build dependencies
RUN apt-get update && \
apt-get install -t ${DEBIAN_RELEASE} -y $BUILD_DEPENDENCIES && \
rm -rf /var/lib/apt/lists/*
ARG FREERDP_OPTS="\
-DBUILTIN_CHANNELS=OFF \
-DCHANNEL_URBDRC=OFF \
-DWITH_ALSA=OFF \
-DWITH_CAIRO=ON \
-DWITH_CHANNELS=ON \
-DWITH_CLIENT=ON \
-DWITH_CUPS=OFF \
-DWITH_DIRECTFB=OFF \
-DWITH_FFMPEG=OFF \
-DWITH_GSM=OFF \
-DWITH_GSSAPI=OFF \
-DWITH_IPP=OFF \
-DWITH_JPEG=ON \
-DWITH_LIBSYSTEMD=OFF \
-DWITH_MANPAGES=OFF \
-DWITH_OPENH264=OFF \
-DWITH_OPENSSL=ON \
-DWITH_OSS=OFF \
-DWITH_PCSC=OFF \
-DWITH_PULSE=OFF \
-DWITH_SERVER=OFF \
-DWITH_SERVER_INTERFACE=OFF \
-DWITH_SHADOW_MAC=OFF \
-DWITH_SHADOW_X11=OFF \
-DWITH_SSE2=ON \
-DWITH_WAYLAND=OFF \
-DWITH_X11=OFF \
-DWITH_X264=OFF \
-DWITH_XCURSOR=ON \
-DWITH_XEXT=ON \
-DWITH_XI=OFF \
-DWITH_XINERAMA=OFF \
-DWITH_XKBFILE=ON \
-DWITH_XRENDER=OFF \
-DWITH_XTEST=OFF \
-DWITH_XV=OFF \
-DWITH_ZLIB=ON"
# Add configuration scripts
COPY src/guacd-docker/bin "${PREFIX_DIR}/bin/"
ARG GUACAMOLE_SERVER_OPTS="\
--disable-guaclog"
# Copy source to container for sake of build
COPY . "$BUILD_DIR"
ARG LIBSSH2_OPTS="\
-DBUILD_EXAMPLES=OFF \
-DBUILD_SHARED_LIBS=ON"
# Build guacamole-server from local source
RUN ${PREFIX_DIR}/bin/build-guacd.sh "$BUILD_DIR" "$PREFIX_DIR"
ARG LIBTELNET_OPTS="\
--disable-static \
--disable-util"
ARG LIBVNCCLIENT_OPTS=""
ARG LIBWEBSOCKETS_OPTS="\
-DDISABLE_WERROR=ON \
-DLWS_WITHOUT_SERVER=ON \
-DLWS_WITHOUT_TESTAPPS=ON \
-DLWS_WITHOUT_TEST_CLIENT=ON \
-DLWS_WITHOUT_TEST_PING=ON \
-DLWS_WITHOUT_TEST_SERVER=ON \
-DLWS_WITHOUT_TEST_SERVER_EXTPOLL=ON \
-DLWS_WITH_STATIC=OFF"
# Build guacamole-server and its core protocol library dependencies
RUN ${BUILD_DIR}/src/guacd-docker/bin/build-all.sh
# Record the packages of all runtime library dependencies
RUN ${PREFIX_DIR}/bin/list-dependencies.sh \
RUN ${BUILD_DIR}/src/guacd-docker/bin/list-dependencies.sh \
${PREFIX_DIR}/sbin/guacd \
${PREFIX_DIR}/lib/libguac-client-*.so \
${PREFIX_DIR}/lib/freerdp2/*guac*.so \
> ${PREFIX_DIR}/DEPENDENCIES
# Use same Debian as the base for the runtime image
FROM debian:${DEBIAN_BASE_IMAGE}
#
# The Debian repository that should be preferred for dependencies (this will be
# added to /etc/apt/sources.list if not already present)
#
# NOTE: Due to limitations of the Docker image build process, this value is
# duplicated in an ARG in the first stage of the build.
#
ARG DEBIAN_RELEASE=buster-backports
# Add repository for specified Debian release if not already present in
# sources.list
RUN grep " ${DEBIAN_RELEASE} " /etc/apt/sources.list || echo >> /etc/apt/sources.list \
"deb http://deb.debian.org/debian ${DEBIAN_RELEASE} main contrib non-free"
# Use same Alpine version as the base for the runtime image
FROM alpine:${ALPINE_BASE_IMAGE}
#
# Base directory for installed build artifacts. See also the
@ -119,36 +155,27 @@ RUN grep " ${DEBIAN_RELEASE} " /etc/apt/sources.list || echo >> /etc/apt/sources
# NOTE: Due to limitations of the Docker image build process, this value is
# duplicated in an ARG in the first stage of the build.
#
ARG PREFIX_DIR=/usr/local/guacamole
ARG PREFIX_DIR=/opt/guacamole
# Runtime environment
ENV LC_ALL=C.UTF-8
ENV LD_LIBRARY_PATH=${PREFIX_DIR}/lib
ENV GUACD_LOG_LEVEL=info
ARG RUNTIME_DEPENDENCIES=" \
netcat-openbsd \
ca-certificates \
ghostscript \
fonts-liberation \
fonts-dejavu \
xfonts-terminus"
# Do not require interaction during build
ARG DEBIAN_FRONTEND=noninteractive
# Copy build artifacts into this stage
COPY --from=builder ${PREFIX_DIR} ${PREFIX_DIR}
# Bring runtime environment up to date and install runtime dependencies
RUN apt-get update && \
apt-get install -t ${DEBIAN_RELEASE} -y --no-install-recommends $RUNTIME_DEPENDENCIES && \
apt-get install -t ${DEBIAN_RELEASE} -y --no-install-recommends $(cat "${PREFIX_DIR}"/DEPENDENCIES) && \
rm -rf /var/lib/apt/lists/*
# Link FreeRDP plugins into proper path
RUN ${PREFIX_DIR}/bin/link-freerdp-plugins.sh \
${PREFIX_DIR}/lib/freerdp2/libguac*.so
RUN apk add --no-cache \
ca-certificates \
ghostscript \
netcat-openbsd \
shadow \
terminus-font \
ttf-dejavu \
ttf-liberation \
util-linux-login && \
xargs apk add --no-cache < ${PREFIX_DIR}/DEPENDENCIES
# Checks the operating status every 5 minutes with a timeout of 5 seconds
HEALTHCHECK --interval=5m --timeout=5s CMD nc -z 127.0.0.1 4822 || exit 1
@ -157,7 +184,7 @@ HEALTHCHECK --interval=5m --timeout=5s CMD nc -z 127.0.0.1 4822 || exit 1
ARG UID=1000
ARG GID=1000
RUN groupadd --gid $GID guacd
RUN useradd --system --create-home --shell /usr/sbin/nologin --uid $UID --gid $GID guacd
RUN useradd --system --create-home --shell /sbin/nologin --uid $UID --gid $GID guacd
# Run with user guacd
USER guacd
@ -170,5 +197,5 @@ EXPOSE 4822
# Note the path here MUST correspond to the value specified in the
# PREFIX_DIR build argument.
#
CMD /usr/local/guacamole/sbin/guacd -b 0.0.0.0 -L $GUACD_LOG_LEVEL -f
CMD /opt/guacamole/sbin/guacd -b 0.0.0.0 -L $GUACD_LOG_LEVEL -f

View File

@ -89,14 +89,15 @@ if ENABLE_GUACLOG
SUBDIRS += src/guaclog
endif
EXTRA_DIST = \
.dockerignore \
CONTRIBUTING \
Dockerfile \
LICENSE \
NOTICE \
bin/guacctl \
doc/Doxyfile.in \
src/guacd-docker \
EXTRA_DIST = \
.dockerignore \
CONTRIBUTING \
Dockerfile \
LICENSE \
NOTICE \
bin/guacctl \
doc/libguac/Doxyfile.in \
doc/libguac-terminal/Doxyfile.in \
src/guacd-docker \
util/generate-test-runner.pl

View File

@ -117,7 +117,7 @@ error() {
##
usage() {
cat >&2 <<END
guacctl 1.4.0, Apache Guacamole terminal session control utility.
guacctl 1.5.0, Apache Guacamole terminal session control utility.
Usage: guacctl [OPTION] [FILE or NAME]...
-d, --download download each of the files listed.

View File

@ -18,7 +18,7 @@
#
AC_PREREQ([2.61])
AC_INIT([guacamole-server], [1.4.0])
AC_INIT([guacamole-server], [1.5.0])
AC_CONFIG_AUX_DIR([build-aux])
AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects])
AM_SILENT_RULES([yes])
@ -160,6 +160,11 @@ AC_CHECK_DECL([strlcat],
[Whether strlcat() is defined])],,
[#include <string.h>])
AC_CHECK_DECL([strnstr],
[AC_DEFINE([HAVE_STRNSTR],,
[Whether strnstr() is defined])],,
[#include <string.h>])
# Typedefs
AC_TYPE_SIZE_T
AC_TYPE_SSIZE_T
@ -189,7 +194,7 @@ AC_SUBST([LIBGUAC_CLIENT_RDP_LTLIB], '$(top_builddir)/src/protocols/rdp/libgua
AC_SUBST([LIBGUAC_CLIENT_RDP_INCLUDE], '-I$(top_srcdir)/src/protocols/rdp')
# Terminal emulator
AC_SUBST([TERMINAL_LTLIB], '$(top_builddir)/src/terminal/libguac_terminal.la')
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
@ -320,30 +325,6 @@ then
else
AC_DEFINE([ENABLE_SSL],, [Whether SSL-related support is enabled])
# OpenSSL 1.1 accessor function for DSA signature values
AC_CHECK_DECL([DSA_SIG_get0],
[AC_DEFINE([HAVE_DSA_SIG_GET0],,
[Whether libssl provides DSA_SIG_get0()])],,
[#include <openssl/dsa.h>])
# OpenSSL 1.1 accessor function for DSA public key parameters
AC_CHECK_DECL([DSA_get0_pqg],
[AC_DEFINE([HAVE_DSA_GET0_PQG],,
[Whether libssl provides DSA_get0_pqg()])],,
[#include <openssl/dsa.h>])
# OpenSSL 1.1 accessor function for DSA public/private key values
AC_CHECK_DECL([DSA_get0_key],
[AC_DEFINE([HAVE_DSA_GET0_KEY],,
[Whether libssl provides DSA_get0_key()])],,
[#include <openssl/dsa.h>])
# OpenSSL 1.1 accessor function for RSA public/private key values
AC_CHECK_DECL([RSA_get0_key],
[AC_DEFINE([HAVE_RSA_GET0_KEY],,
[Whether libssl provides RSA_get0_key()])],,
[#include <openssl/rsa.h>])
# OpenSSL 1.1 does away with explicit threading callbacks
AC_MSG_CHECKING([whether libssl requires threading callbacks])
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
@ -932,7 +913,7 @@ if test "x$with_ssh" != "xno"
then
have_libssh2=yes
AC_CHECK_LIB([ssh2], [libssh2_session_init_ex],
AC_CHECK_LIB([ssh2], [libssh2_userauth_publickey_frommemory],
[SSH_LIBS="$SSH_LIBS -lssh2"],
[have_libssh2=no])
fi
@ -1186,7 +1167,8 @@ AM_CONDITIONAL([ENABLE_GUACLOG], [test "x${enable_guaclog}" = "xyes"])
#
AC_CONFIG_FILES([Makefile
doc/Doxyfile
doc/libguac/Doxyfile
doc/libguac-terminal/Doxyfile
src/common/Makefile
src/common/tests/Makefile
src/common-ssh/Makefile

View File

@ -1,4 +1,3 @@
#!/bin/sh -e
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
@ -18,32 +17,42 @@
# under the License.
#
##
## @fn build-guacd.sh
##
## Builds the source of guacamole-server, automatically creating any required
## symbolic links for the proper loading of FreeRDP plugins.
##
## @param BUILD_DIR
## The directory which currently contains the guacamole-server source and
## in which the build should be performed.
##
## @param PREFIX_DIR
## The directory prefix into which the build artifacts should be installed
## in which the build should be performed. This is passed to the --prefix
## option of `configure`.
##
BUILD_DIR="$1"
PREFIX_DIR="$2"
#
# Build guacamole-server
# Project name / version
#
cd "$BUILD_DIR"
autoreconf -fi
./configure --prefix="$PREFIX_DIR" --disable-guaclog --with-freerdp-plugin-dir="$PREFIX_DIR/lib/freerdp2"
make
make install
ldconfig
PROJECT_NAME = libguac-terminal
PROJECT_NUMBER = @PACKAGE_VERSION@
#
# Warn about undocumented parameters and return values, but do not fill output
# with verbose progress info.
#
QUIET = YES
WARN_NO_PARAMDOC = YES
#
# Output format
#
ALPHABETICAL_INDEX = YES
GENERATE_HTML = YES
GENERATE_LATEX = NO
OPTIMIZE_OUTPUT_FOR_C = YES
OUTPUT_DIRECTORY = doxygen-output
RECURSIVE = YES
SHOW_INCLUDE_FILES = NO
#
# Input format
#
CASE_SENSE_NAMES = YES
FILE_PATTERNS = *.h
STRIP_FROM_PATH = ../../src/terminal
INPUT = ../../src/terminal/terminal/terminal.h
JAVADOC_AUTOBRIEF = YES
TAB_SIZE = 4
TYPEDEF_HIDES_STRUCT = YES

View File

@ -52,9 +52,9 @@ SHOW_INCLUDE_FILES = NO
CASE_SENSE_NAMES = YES
EXCLUDE_SYMBOLS = __* guac_palette*
FILE_PATTERNS = *.h
INPUT = ../src/libguac/guacamole
INPUT = ../../src/libguac/guacamole
JAVADOC_AUTOBRIEF = YES
STRIP_FROM_PATH = ../src/libguac
STRIP_FROM_PATH = ../../src/libguac
TAB_SIZE = 4
TYPEDEF_HIDES_STRUCT = YES

View File

@ -31,8 +31,6 @@ SUBDIRS = . tests
libguac_common_ssh_la_SOURCES = \
buffer.c \
dsa-compat.c \
rsa-compat.c \
sftp.c \
ssh.c \
key.c \
@ -40,8 +38,6 @@ libguac_common_ssh_la_SOURCES = \
noinst_HEADERS = \
common-ssh/buffer.h \
common-ssh/dsa-compat.h \
common-ssh/rsa-compat.h \
common-ssh/key.h \
common-ssh/sftp.h \
common-ssh/ssh.h \

View File

@ -1,61 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef GUAC_COMMON_SSH_DSA_COMPAT_H
#define GUAC_COMMON_SSH_DSA_COMPAT_H
#include "config.h"
#include <openssl/bn.h>
#include <openssl/dsa.h>
#ifndef HAVE_DSA_GET0_PQG
/**
* DSA_get0_pqg() implementation for versions of OpenSSL which lack this
* function (pre 1.1).
*
* See: https://www.openssl.org/docs/man1.1.0/crypto/DSA_get0_pqg.html
*/
void DSA_get0_pqg(const DSA* dsa_key, const BIGNUM** p,
const BIGNUM** q, const BIGNUM** g);
#endif
#ifndef HAVE_DSA_GET0_KEY
/**
* DSA_get0_key() implementation for versions of OpenSSL which lack this
* function (pre 1.1).
*
* See: https://www.openssl.org/docs/man1.1.0/crypto/DSA_get0_key.html
*/
void DSA_get0_key(const DSA* dsa_key, const BIGNUM** pub_key,
const BIGNUM** priv_key);
#endif
#ifndef HAVE_DSA_SIG_GET0
/**
* DSA_SIG_get0() implementation for versions of OpenSSL which lack this
* function (pre 1.1).
*
* See: https://www.openssl.org/docs/man1.1.0/crypto/DSA_SIG_get0.html
*/
void DSA_SIG_get0(const DSA_SIG* dsa_sig, const BIGNUM** r, const BIGNUM** s);
#endif
#endif

View File

@ -25,75 +25,27 @@
#include <guacamole/client.h>
#include <libssh2.h>
#include <openssl/ossl_typ.h>
/**
* OpenSSH v1 private keys are PEM-wrapped base64-encoded blobs. The encoded data begins with:
* "openssh-key-v1\0"
*/
#define OPENSSH_V1_KEY_HEADER "-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktdjEA"
/**
* The expected header of RSA private keys.
* The base64-encoded prefix indicating an OpenSSH v1 private key is NOT protected by a
* passphrase. Specifically, it is the following data fields and values:
* pascal string: cipher name ("none")
* pascal string: kdf name ("none")
* pascal string: kdf params (NULL)
* 32-bit int: number of keys (1)
*/
#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;
#define OPENSSH_V1_UNENCRYPTED_KEY "AAAABG5vbmUAAAAEbm9uZQAAAAAAAAAB"
/**
* 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.
*/
@ -104,6 +56,11 @@ typedef struct guac_common_ssh_key {
*/
int private_key_length;
/**
* The private key's passphrase, if any.
*/
char *passphrase;
} guac_common_ssh_key;
/**
@ -144,31 +101,6 @@ const char* guac_common_ssh_key_error();
*/
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);
/**
* Verifies the host key for the given hostname/port combination against
* one or more known_hosts entries. The known_host entries can either be a

View File

@ -1,40 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef GUAC_COMMON_SSH_RSA_COMPAT_H
#define GUAC_COMMON_SSH_RSA_COMPAT_H
#include "config.h"
#include <openssl/bn.h>
#include <openssl/rsa.h>
#ifndef HAVE_RSA_GET0_KEY
/**
* RSA_get0_key() implementation for versions of OpenSSL which lack this
* function (pre 1.1).
*
* See: https://www.openssl.org/docs/man1.1.0/crypto/RSA_get0_key.html
*/
void RSA_get0_key(const RSA* rsa_key, const BIGNUM** n,
const BIGNUM** e, const BIGNUM**d);
#endif
#endif

View File

@ -1,59 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "config.h"
#include <openssl/bn.h>
#include <openssl/dsa.h>
#include <stdlib.h>
#ifndef HAVE_DSA_GET0_PQG
void DSA_get0_pqg(const DSA* dsa_key, const BIGNUM** p,
const BIGNUM** q, const BIGNUM** g) {
/* Retrieve all requested internal values */
if (p != NULL) *p = dsa_key->p;
if (q != NULL) *q = dsa_key->q;
if (g != NULL) *g = dsa_key->g;
}
#endif
#ifndef HAVE_DSA_GET0_KEY
void DSA_get0_key(const DSA* dsa_key, const BIGNUM** pub_key,
const BIGNUM** priv_key) {
/* Retrieve all requested internal values */
if (pub_key != NULL) *pub_key = dsa_key->pub_key;
if (priv_key != NULL) *priv_key = dsa_key->priv_key;
}
#endif
#ifndef HAVE_DSA_SIG_GET0
void DSA_SIG_get0(const DSA_SIG* dsa_sig, const BIGNUM** r, const BIGNUM** s) {
/* Retrieve all requested internal values */
if (r != NULL) *r = dsa_sig->r;
if (s != NULL) *s = dsa_sig->s;
}
#endif

View File

@ -20,9 +20,9 @@
#include "config.h"
#include "common-ssh/buffer.h"
#include "common-ssh/dsa-compat.h"
#include "common-ssh/key.h"
#include "common-ssh/rsa-compat.h"
#include <guacamole/string.h>
#include <openssl/bio.h>
#include <openssl/bn.h>
@ -33,119 +33,118 @@
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/**
* Check for a PKCS#1/PKCS#8 ENCRYPTED marker.
*
* @param data
* The buffer to scan.
* @param length
* The length of the buffer.
*
* @return
* True if the buffer contains the marker, false otherwise.
*/
static bool is_pkcs_encrypted_key(char* data, int length) {
return guac_strnstr(data, "ENCRYPTED", length) != NULL;
}
/**
* Check for a PEM header & initial base64-encoded data indicating this is an
* OpenSSH v1 key.
*
* @param data
* The buffer to scan.
* @param length
* The length of the buffer.
*
* @return
* True if the buffer contains a private key, false otherwise.
*/
static bool is_ssh_private_key(char* data, int length) {
if (length < sizeof(OPENSSH_V1_KEY_HEADER) - 1) {
return false;
}
return !strncmp(data, OPENSSH_V1_KEY_HEADER, sizeof(OPENSSH_V1_KEY_HEADER) - 1);
}
/**
* Assuming an offset into a key past the header, check for the base64-encoded
* data indicating this key is not protected by a passphrase.
*
* @param data
* The buffer to scan.
* @param length
* The length of the buffer.
*
* @return
* True if the buffer contains an unencrypted key, false otherwise.
*/
static bool is_ssh_key_unencrypted(char* data, int length) {
if (length < sizeof(OPENSSH_V1_UNENCRYPTED_KEY) - 1) {
return false;
}
return !strncmp(data, OPENSSH_V1_UNENCRYPTED_KEY, sizeof(OPENSSH_V1_UNENCRYPTED_KEY) - 1);
}
/**
* A passphrase is needed if the key is an encrypted PKCS#1/PKCS#8 key OR if
* the key is both an OpenSSH v1 key AND there isn't a marker indicating the
* key is unprotected.
*
* @param data
* The buffer to scan.
* @param length
* The length of the buffer.
*
* @return
* True if the buffer contains a key needing a passphrase, false otherwise.
*/
static bool is_passphrase_needed(char* data, int length) {
/* Is this an encrypted PKCS#1/PKCS#8 key? */
if (is_pkcs_encrypted_key(data, length)) {
return true;
}
/* Is this an OpenSSH v1 key? */
if (is_ssh_private_key(data, length)) {
/* This is safe due to the check in is_ssh_private_key. */
data += sizeof(OPENSSH_V1_KEY_HEADER) - 1;
length -= sizeof(OPENSSH_V1_KEY_HEADER) - 1;
/* If this is NOT unprotected, we need a passphrase. */
if (!is_ssh_key_unencrypted(data, length)) {
return true;
}
}
return false;
}
guac_common_ssh_key* guac_common_ssh_key_alloc(char* data, int length,
char* passphrase) {
guac_common_ssh_key* key;
BIO* key_bio;
/* Because libssh2 will do the actual key parsing (to let it deal with
* different key algorithms) we need to perform a heuristic here to check
* if a passphrase is needed. This could allow junk keys through that
* would never be able to auth. libssh2 should display errors to help
* admins track down malformed keys and delete or replace them.
*/
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;
const BIGNUM* key_e;
const BIGNUM* key_n;
/* 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;
/* Retrieve public key */
RSA_get0_key(rsa_key, &key_n, &key_e, NULL);
/* Send public key formatted for SSH */
guac_common_ssh_buffer_write_string(&pos, "ssh-rsa", sizeof("ssh-rsa")-1);
guac_common_ssh_buffer_write_bignum(&pos, key_e);
guac_common_ssh_buffer_write_bignum(&pos, 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;
const BIGNUM* key_p;
const BIGNUM* key_q;
const BIGNUM* key_g;
const BIGNUM* pub_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;
/* Retrieve public key */
DSA_get0_pqg(dsa_key, &key_p, &key_q, &key_g);
DSA_get0_key(dsa_key, &pub_key, NULL);
/* Send public key formatted for SSH */
guac_common_ssh_buffer_write_string(&pos, "ssh-dss", sizeof("ssh-dss")-1);
guac_common_ssh_buffer_write_bignum(&pos, key_p);
guac_common_ssh_buffer_write_bignum(&pos, key_q);
guac_common_ssh_buffer_write_bignum(&pos, key_g);
guac_common_ssh_buffer_write_bignum(&pos, 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);
if (is_passphrase_needed(data, length) && (passphrase == NULL || *passphrase == '\0'))
return NULL;
}
guac_common_ssh_key* key = malloc(sizeof(guac_common_ssh_key));
/* Copy private key to structure */
key->private_key_length = length;
key->private_key = malloc(length);
memcpy(key->private_key, data, length);
key->passphrase = strdup(passphrase);
BIO_free(key_bio);
return key;
}
@ -159,93 +158,11 @@ const char* guac_common_ssh_key_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->private_key);
free(key->public_key);
free(key->passphrase);
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;
unsigned char digest[EVP_MAX_MD_SIZE];
unsigned int dlen, len;
/* Get SHA1 digest */
if ((md = EVP_get_digestbynid(NID_sha1)) == NULL)
return -1;
/* Allocate digest context */
EVP_MD_CTX* md_ctx = EVP_MD_CTX_create();
if (md_ctx == NULL)
return -1;
/* Digest data */
EVP_DigestInit(md_ctx, md);
EVP_DigestUpdate(md_ctx, data, length);
EVP_DigestFinal(md_ctx, digest, &dlen);
/* Digest context no longer needed */
EVP_MD_CTX_destroy(md_ctx);
/* 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) {
const BIGNUM* sig_r;
const BIGNUM* sig_s;
/* Retrieve DSA signature values */
DSA_SIG_get0(dsa_sig, &sig_r, &sig_s);
/* Compute size of each half of signature */
int rlen = BN_num_bytes(sig_r);
int slen = BN_num_bytes(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(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(sig_s, sig + DSA_SIG_SIZE - slen);
/* Done */
DSA_SIG_free(dsa_sig);
return DSA_SIG_SIZE;
}
}
}
return -1;
}
int guac_common_ssh_verify_host_key(LIBSSH2_SESSION* session, guac_client* client,
const char* host_key, const char* hostname, int port, const char* remote_hostkey,
const size_t remote_hostkey_len) {

View File

@ -22,6 +22,7 @@
#include "common-ssh/user.h"
#include <guacamole/client.h>
#include <guacamole/fips.h>
#include <libssh2.h>
#ifdef LIBSSH2_USES_GCRYPT
@ -46,6 +47,20 @@
GCRY_THREAD_OPTION_PTHREAD_IMPL;
#endif
/**
* A list of all key exchange algorithms that are both FIPS-compliant, and
* OpenSSL-supported. Note that "ext-info-c" is also included. While not a key
* exchange algorithm per se, it must be in the list to ensure that the server
* will send a SSH_MSG_EXT_INFO response, which is required to perform RSA key
* upgrades.
*/
#define FIPS_COMPLIANT_KEX_ALGORITHMS "diffie-hellman-group-exchange-sha256,ext-info-c"
/**
* A list of ciphers that are both FIPS-compliant, and OpenSSL-supported.
*/
#define FIPS_COMPLIANT_CIPHERS "aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,aes192-cbc,aes256-cbc"
#ifdef OPENSSL_REQUIRES_THREADING_CALLBACKS
/**
* Array of mutexes, used by OpenSSL.
@ -165,9 +180,11 @@ int guac_common_ssh_init(guac_client* client) {
CRYPTO_set_locking_callback(guac_common_ssh_openssl_locking_callback);
#endif
/* Init OpenSSL */
#if OPENSSL_VERSION_NUMBER < 0x10100000L
/* Init OpenSSL - only required for OpenSSL Versions < 1.1.0 */
SSL_library_init();
ERR_load_crypto_strings();
#endif
/* Init libssh2 */
libssh2_init(0);
@ -183,55 +200,6 @@ void guac_common_ssh_uninit() {
#endif
}
/**
* 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
@ -324,8 +292,9 @@ static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session)
}
/* Get list of supported authentication methods */
size_t username_len = strlen(user->username);
char* user_authlist = libssh2_userauth_list(session, user->username,
strlen(user->username));
username_len);
/* If auth list is NULL, then authentication has succeeded with NONE */
if (user_authlist == NULL) {
@ -349,9 +318,9 @@ static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session)
}
/* Attempt public key auth */
if (libssh2_userauth_publickey(session, user->username,
(unsigned char*) key->public_key, key->public_key_length,
guac_common_ssh_sign_callback, (void**) key)) {
if (libssh2_userauth_publickey_frommemory(session, user->username,
username_len, NULL, 0, key->private_key,
key->private_key_length, key->passphrase)) {
/* Abort on failure */
char* error_message;
@ -532,6 +501,17 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
return NULL;
}
/*
* If FIPS mode is enabled, prefer only FIPS-compatible algorithms and
* ciphers that are also supported by libssh2. For more info, see:
* https://csrc.nist.gov/CSRC/media/projects/cryptographic-module-validation-program/documents/security-policies/140sp2906.pdf
*/
if (guac_fips_enabled()) {
libssh2_session_method_pref(session, LIBSSH2_METHOD_KEX, FIPS_COMPLIANT_KEX_ALGORITHMS);
libssh2_session_method_pref(session, LIBSSH2_METHOD_CRYPT_CS, FIPS_COMPLIANT_CIPHERS);
libssh2_session_method_pref(session, LIBSSH2_METHOD_CRYPT_SC, FIPS_COMPLIANT_CIPHERS);
}
/* Perform handshake */
if (libssh2_session_handshake(session, fd)) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,

View File

@ -42,7 +42,6 @@ noinst_HEADERS = \
common/json.h \
common/list.h \
common/pointer_cursor.h \
common/recording.h \
common/rect.h \
common/string.h \
common/surface.h
@ -59,7 +58,6 @@ libguac_common_la_SOURCES = \
json.c \
list.c \
pointer_cursor.c \
recording.c \
rect.c \
string.c \
surface.c

View File

@ -29,15 +29,15 @@
#include <string.h>
#include <stdlib.h>
guac_common_clipboard* guac_common_clipboard_alloc(int size) {
guac_common_clipboard* guac_common_clipboard_alloc() {
guac_common_clipboard* clipboard = malloc(sizeof(guac_common_clipboard));
/* Init clipboard */
clipboard->mimetype[0] = '\0';
clipboard->buffer = malloc(size);
clipboard->buffer = malloc(GUAC_COMMON_CLIPBOARD_MAX_LENGTH);
clipboard->available = GUAC_COMMON_CLIPBOARD_MAX_LENGTH;
clipboard->length = 0;
clipboard->available = size;
pthread_mutex_init(&(clipboard->lock), NULL);

View File

@ -31,6 +31,11 @@
*/
#define GUAC_COMMON_CLIPBOARD_BLOCK_SIZE 4096
/**
* The maximum number of bytes to allow within the clipboard.
*/
#define GUAC_COMMON_CLIPBOARD_MAX_LENGTH 262144
/**
* Generic clipboard structure.
*/
@ -66,12 +71,9 @@ typedef struct guac_common_clipboard {
} 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.
* Creates a new clipboard.
*/
guac_common_clipboard* guac_common_clipboard_alloc(int size);
guac_common_clipboard* guac_common_clipboard_alloc();
/**
* Frees the given clipboard.

View File

@ -166,6 +166,8 @@ void guac_common_display_free(guac_common_display* display) {
void guac_common_display_dup(guac_common_display* display, guac_user* user,
guac_socket* socket) {
guac_client* client = user->client;
pthread_mutex_lock(&display->_lock);
/* Sunchronize shared cursor */
@ -178,6 +180,9 @@ void guac_common_display_dup(guac_common_display* display, guac_user* user,
guac_common_display_dup_layers(display->layers, user, socket);
guac_common_display_dup_layers(display->buffers, user, socket);
/* Sends a sync instruction to mark the boundary of the first frame */
guac_protocol_send_sync(socket, client->last_sent_timestamp, 1);
pthread_mutex_unlock(&display->_lock);
}
@ -384,4 +389,3 @@ void guac_common_display_free_buffer(guac_common_display* display,
pthread_mutex_unlock(&display->_lock);
}

115
src/guacd-docker/bin/build-all.sh Executable file
View File

@ -0,0 +1,115 @@
#!/bin/sh -e
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
##
## @fn build-all.sh
##
## Builds the source of guacamole-server and its various core protocol library
## dependencies.
##
# Pre-populate build control variables such that the custom build prefix is
# used for C headers, locating libraries, etc.
export CFLAGS="-I${PREFIX_DIR}/include"
export LDFLAGS="-L${PREFIX_DIR}/lib"
export PKG_CONFIG_PATH="${PREFIX_DIR}/lib/pkgconfig"
# Ensure thread stack size will be 8 MB (glibc's default on Linux) rather than
# 128 KB (musl's default)
export LDFLAGS="$LDFLAGS -Wl,-z,stack-size=8388608"
##
## Builds and installs the source at the given git repository, automatically
## switching to the version of the source at the tag/commit that matches the
## given pattern.
##
## @param URL
## The URL of the git repository that the source should be downloaded from.
##
## @param PATTERN
## The Perl-compatible regular expression that the tag must match. If no
## tag matches the regular expression, the pattern is assumed to be an
## exact reference to a commit, branch, etc. acceptable by git checkout.
##
## @param ...
## Any additional command-line options that should be provided to CMake or
## the configure script.
##
install_from_git() {
URL="$1"
PATTERN="$2"
shift 2
# Calculate top-level directory name of resulting repository from the
# provided URL
REPO_DIR="$(basename "$URL" .git)"
# Allow dependencies to be manually omitted with the tag/commit pattern "NO"
if [ "$PATTERN" = "NO" ]; then
echo "NOT building $REPO_DIR (explicitly skipped)"
return
fi
# Clone repository and change to top-level directory of source
cd /tmp
git clone "$URL"
cd $REPO_DIR/
# Locate tag/commit based on provided pattern
VERSION="$(git tag -l --sort=-v:refname | grep -Px -m1 "$PATTERN" \
|| echo "$PATTERN")"
# Switch to desired version of source
echo "Building $REPO_DIR @ $VERSION ..."
git -c advice.detachedHead=false checkout "$VERSION"
# Configure build using CMake or GNU Autotools, whichever happens to be
# used by the library being built
if [ -e CMakeLists.txt ]; then
cmake -DCMAKE_INSTALL_PREFIX:PATH="$PREFIX_DIR" "$@" .
else
[ -e configure ] || autoreconf -fi
./configure --prefix="$PREFIX_DIR" "$@"
fi
# Build and install
make && make install
}
#
# Build and install core protocol library dependencies
#
install_from_git "https://github.com/FreeRDP/FreeRDP" "$WITH_FREERDP" $FREERDP_OPTS
install_from_git "https://github.com/libssh2/libssh2" "$WITH_LIBSSH2" $LIBSSH2_OPTS
install_from_git "https://github.com/seanmiddleditch/libtelnet" "$WITH_LIBTELNET" $LIBTELNET_OPTS
install_from_git "https://github.com/LibVNC/libvncserver" "$WITH_LIBVNCCLIENT" $LIBVNCCLIENT_OPTS
install_from_git "https://libwebsockets.org/repo/libwebsockets" "$WITH_LIBWEBSOCKETS" $LIBWEBSOCKETS_OPTS
#
# Build guacamole-server
#
cd "$BUILD_DIR"
autoreconf -fi && ./configure --prefix="$PREFIX_DIR" $GUACAMOLE_SERVER_OPTS
make && make install

View File

@ -1,86 +0,0 @@
#!/bin/sh -e
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
##
## @fn link-freerdp-plugins.sh
##
## Automatically creates any required symbolic links for the proper loading of
## the given FreeRDP plugins. If a given plugin is already in the correct
## directory, no link is created for that plugin.
##
## @param ...
## The FreeRDP plugins to add links for.
##
##
## Given the full path to a FreeRDP plugin, locates the base directory of the
## associated FreeRDP installation (where the FreeRDP library .so files are
## located), printing the result to STDOUT. If the directory cannot be
## determined, an error is printed.
##
## @param PLUGIN_FILE
## The full path to the FreeRDP plugin to check.
##
where_is_freerdp() {
PLUGIN_FILE="$1"
# Determine the location of all libfreerdp* libraries explicitly linked
# to given file
PATHS="$(ldd "$PLUGIN_FILE" \
| awk '/=>/{print $(NF-1)}' \
| grep 'libfreerdp' \
| xargs -r dirname \
| xargs -r realpath \
| sort -u)"
# Verify that exactly one location was found
if [ "$(echo "$PATHS" | wc -l)" != 1 ]; then
echo "$1: Unable to locate FreeRDP install location." >&2
return 1
fi
echo "$PATHS"
}
#
# Create symbolic links as necessary to include all given plugins within the
# search path of FreeRDP
#
while [ -n "$1" ]; do
# Determine correct install location for FreeRDP plugins
FREERDP_DIR="$(where_is_freerdp "$1")"
FREERDP_PLUGIN_DIR="${FREERDP_DIR}/freerdp2"
# Add symbolic link if necessary
if [ ! -e "$FREERDP_PLUGIN_DIR/$(basename "$1")" ]; then
mkdir -p "$FREERDP_PLUGIN_DIR"
ln -s "$1" "$FREERDP_PLUGIN_DIR"
else
echo "$1: Already in correct directory." >&2
fi
shift
done

View File

@ -21,7 +21,7 @@
##
## @fn list-dependencies.sh
##
## Lists the Debian/Ubuntu package names for all library dependencies of the
## Lists the Alpine Linux package names for all library dependencies of the
## given binaries. Each package is only listed once, even if multiple binaries
## provided by the same package are given.
##
@ -35,14 +35,17 @@ while [ -n "$1" ]; do
ldd "$1" | grep -v 'libguac' | awk '/=>/{print $(NF-1)}' \
| while read LIBRARY; do
# Determine the Debian package which is associated with that
# library, if any
dpkg-query -S "$LIBRARY" 2> /dev/null || true
# List the package providing that library, if any
apk info -W "$LIBRARY" 2> /dev/null \
| grep 'is owned by' | grep -o '[^ ]*$' || true
done
# Next binary
shift
done | cut -f1 -d: | sort -u
# Strip the "-VERSION" suffix from each package name, listing each resulting
# package uniquely ("apk add" cannot handle package names that include the
# version number)
done | sed 's/\(.*\)-[0-9]\+\..*$/\1/' | sort -u

View File

@ -381,10 +381,15 @@ int main(int argc, char* argv[]) {
CRYPTO_set_locking_callback(guacd_openssl_locking_callback);
#endif
/* Init SSL */
#if OPENSSL_VERSION_NUMBER < 0x10100000L
/* Init OpenSSL for OpenSSL Versions < 1.1.0 */
SSL_library_init();
SSL_load_error_strings();
ssl_context = SSL_CTX_new(SSLv23_server_method());
#else
/* Set up OpenSSL for OpenSSL Versions >= 1.1.0 */
ssl_context = SSL_CTX_new(TLS_server_method());
#endif
/* Load key */
if (config->key_file != NULL) {

View File

@ -213,7 +213,7 @@ int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) {
#endif
}
AVCodecContext* guacenc_build_avcodeccontext(AVStream* stream, AVCodec* codec,
AVCodecContext* guacenc_build_avcodeccontext(AVStream* stream, const AVCodec* codec,
int bitrate, int width, int height, int gop_size, int qmax, int qmin,
int pix_fmt, AVRational time_base) {
@ -249,7 +249,7 @@ AVCodecContext* guacenc_build_avcodeccontext(AVStream* stream, AVCodec* codec,
}
int guacenc_open_avcodec(AVCodecContext *avcodec_context,
AVCodec *codec, AVDictionary **options,
const AVCodec *codec, AVDictionary **options,
AVStream* stream) {
int ret = avcodec_open2(avcodec_context, codec, options);

View File

@ -128,7 +128,7 @@ int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame);
* The pointer to the configured AVCodecContext.
*
*/
AVCodecContext* guacenc_build_avcodeccontext(AVStream* stream, AVCodec* codec,
AVCodecContext* guacenc_build_avcodeccontext(AVStream* stream, const AVCodec* codec,
int bitrate, int width, int height, int gop_size, int qmax, int qmin,
int pix_fmt, AVRational time_base);
@ -158,7 +158,7 @@ AVCodecContext* guacenc_build_avcodeccontext(AVStream* stream, AVCodec* codec,
* Zero on success, a negative value on error.
*/
int guacenc_open_avcodec(AVCodecContext *avcodec_context,
AVCodec *codec, AVDictionary **options,
const AVCodec *codec, AVDictionary **options,
AVStream* stream);
#endif

View File

@ -47,7 +47,7 @@
guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
int width, int height, int bitrate) {
AVOutputFormat *container_format;
const AVOutputFormat *container_format;
AVFormatContext *container_format_context;
AVStream *video_stream;
int ret;
@ -63,7 +63,7 @@ guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
container_format = container_format_context->oformat;
/* Pull codec based on name */
AVCodec* codec = avcodec_find_encoder_by_name(codec_name);
const AVCodec* codec = avcodec_find_encoder_by_name(codec_name);
if (codec == NULL) {
guacenc_log(GUAC_LOG_ERROR, "Failed to locate codec \"%s\".",
codec_name);

View File

@ -44,6 +44,7 @@ libguacinc_HEADERS = \
guacamole/client-types.h \
guacamole/error.h \
guacamole/error-types.h \
guacamole/fips.h \
guacamole/hash.h \
guacamole/layer.h \
guacamole/layer-types.h \
@ -59,6 +60,7 @@ libguacinc_HEADERS = \
guacamole/protocol.h \
guacamole/protocol-constants.h \
guacamole/protocol-types.h \
guacamole/recording.h \
guacamole/socket-constants.h \
guacamole/socket.h \
guacamole/socket-fntypes.h \
@ -92,6 +94,7 @@ libguac_la_SOURCES = \
encode-jpeg.c \
encode-png.c \
error.c \
fips.c \
hash.c \
id.c \
palette.c \
@ -99,6 +102,7 @@ libguac_la_SOURCES = \
pool.c \
protocol.c \
raw_encoder.c \
recording.c \
socket.c \
socket-broadcast.c \
socket-fd.c \
@ -135,7 +139,7 @@ libguac_la_CFLAGS = \
-Werror -Wall -pedantic
libguac_la_LDFLAGS = \
-version-info 20:0:0 \
-version-info 21:0:0 \
-no-undefined \
@CAIRO_LIBS@ \
@DL_LIBS@ \

View File

@ -307,6 +307,10 @@ int guac_client_add_user(guac_client* client, guac_user* user, int argc, char**
pthread_rwlock_unlock(&(client->__users_lock));
/* Notify owner of user joining connection. */
if (retval == 0 && !user->owner)
guac_client_owner_notify_join(client, user);
return retval;
}
@ -333,6 +337,10 @@ void guac_client_remove_user(guac_client* client, guac_user* user) {
pthread_rwlock_unlock(&(client->__users_lock));
/* Update owner of user having left the connection. */
if (!user->owner)
guac_client_owner_notify_leave(client, user);
/* Call handler, if defined */
if (user->leave_handler)
user->leave_handler(user);
@ -413,15 +421,19 @@ void* guac_client_for_user(guac_client* client, guac_user* user,
}
int guac_client_end_frame(guac_client* client) {
return guac_client_end_multiple_frames(client, 0);
}
int guac_client_end_multiple_frames(guac_client* client, int frames) {
/* Update and send timestamp */
client->last_sent_timestamp = guac_timestamp_current();
/* Log received timestamp and calculated lag (at TRACE level only) */
guac_client_log(client, GUAC_LOG_TRACE, "Server completed "
"frame %" PRIu64 "ms.", client->last_sent_timestamp);
"frame %" PRIu64 "ms (%i logical frames)", client->last_sent_timestamp, frames);
return guac_protocol_send_sync(client->socket, client->last_sent_timestamp);
return guac_protocol_send_sync(client->socket, client->last_sent_timestamp, frames);
}
@ -671,6 +683,36 @@ static void* __webp_support_callback(guac_user* user, void* data) {
}
#endif
/**
* A callback function which is invoked by guac_client_owner_supports_msg()
* to determine if the owner of a client supports the "msg" instruction,
* returning zero if the user does not support the instruction or non-zero if
* the user supports it.
*
* @param user
* The guac_user that will be checked for "msg" instruction support.
*
* @param data
* Data provided to the callback. This value is never used within this
* callback.
*
* @return
* A non-zero integer if the provided user who owns the connection supports
* the "msg" instruction, or zero if the user does not. The integer is cast
* as a void*.
*/
static void* guac_owner_supports_msg_callback(guac_user* user, void* data) {
return (void*) ((intptr_t) guac_user_supports_msg(user));
}
int guac_client_owner_supports_msg(guac_client* client) {
return (int) ((intptr_t) guac_client_for_owner(client, guac_owner_supports_msg_callback, NULL));
}
/**
* A callback function which is invoked by guac_client_owner_supports_required()
* to determine if the owner of a client supports the "required" instruction,
@ -701,6 +743,124 @@ int guac_client_owner_supports_required(guac_client* client) {
}
/**
* A callback function that is invokved by guac_client_owner_notify_join() to
* notify the owner of a connection that another user has joined the
* connection, returning zero if the message is sent successfully, or non-zero
* if an error occurs.
*
* @param user
* The user to send the notification to, which will be the owner of the
* connection.
*
* @param data
* The data provided to the callback, which is the user that is joining the
* connection.
*
* @return
* Zero if the message is sent successfully to the owner, otherwise
* non-zero, cast as a void*.
*/
static void* guac_client_owner_notify_join_callback(guac_user* user, void* data) {
const guac_user* joiner = (const guac_user *) data;
if (user == NULL)
return (void*) ((intptr_t) -1);
char* log_owner = "owner";
if (user->info.name != NULL)
log_owner = (char *) user->info.name;
char* log_joiner = "anonymous";
char* send_joiner = "";
if (joiner->info.name != NULL) {
log_joiner = (char *) joiner->info.name;
send_joiner = (char *) joiner->info.name;
}
guac_user_log(user, GUAC_LOG_DEBUG, "Notifying owner \"%s\" of \"%s\" joining.",
log_owner, log_joiner);
/* Send user joined notification to owner. */
const char* args[] = { (const char*)joiner->user_id, (const char*)send_joiner, NULL };
return (void*) ((intptr_t) guac_protocol_send_msg(user->socket, GUAC_MESSAGE_USER_JOINED, args));
}
int guac_client_owner_notify_join(guac_client* client, guac_user* joiner) {
/* Don't send msg instruction if client does not support it. */
if (!guac_client_owner_supports_msg(client)) {
guac_client_log(client, GUAC_LOG_DEBUG,
"Client does not support the \"msg\" instruction and "
"will not be notified of the user joining the connection.");
return -1;
}
return (int) ((intptr_t) guac_client_for_owner(client, guac_client_owner_notify_join_callback, joiner));
}
/**
* A callback function that is invokved by guac_client_owner_notify_leave() to
* notify the owner of a connection that another user has left the connection,
* returning zero if the message is sent successfully, or non-zero
* if an error occurs.
*
* @param user
* The user to send the notification to, which will be the owner of the
* connection.
*
* @param data
* The data provided to the callback, which is the user that is leaving the
* connection.
*
* @return
* Zero if the message is sent successfully to the owner, otherwise
* non-zero, cast as a void*.
*/
static void* guac_client_owner_notify_leave_callback(guac_user* user, void* data) {
const guac_user* quitter = (const guac_user *) data;
if (user == NULL)
return (void*) ((intptr_t) -1);
char* log_owner = "owner";
if (user->info.name != NULL)
log_owner = (char *) user->info.name;
char* log_quitter = "anonymous";
char* send_quitter = "";
if (quitter->info.name != NULL) {
log_quitter = (char *) quitter->info.name;
send_quitter = (char *) quitter->info.name;
}
guac_user_log(user, GUAC_LOG_DEBUG, "Notifying owner \"%s\" of \"%s\" leaving.",
log_owner, log_quitter);
/* Send user left notification to owner. */
const char* args[] = { (const char*)quitter->user_id, (const char*)send_quitter, NULL };
return (void*) ((intptr_t) guac_protocol_send_msg(user->socket, GUAC_MESSAGE_USER_LEFT, args));
}
int guac_client_owner_notify_leave(guac_client* client, guac_user* quitter) {
/* Don't send msg instruction if client does not support it. */
if (!guac_client_owner_supports_msg(client)) {
guac_client_log(client, GUAC_LOG_DEBUG,
"Client does not support the \"msg\" instruction and "
"will not be notified of the user leaving the connection.");
return -1;
}
return (int) ((intptr_t) guac_client_for_owner(client, guac_client_owner_notify_leave_callback, quitter));
}
int guac_client_supports_webp(guac_client* client) {
#ifdef ENABLE_WEBP

51
src/libguac/fips.c Normal file
View File

@ -0,0 +1,51 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "config.h"
#include "guacamole/fips.h"
/* If OpenSSL is available, include header for version numbers */
#ifdef ENABLE_SSL
#include <openssl/opensslv.h>
/* OpenSSL versions prior to 0.9.7e did not have FIPS support */
#if !defined(OPENSSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER < 0x00090705f)
#define GUAC_FIPS_ENABLED 0
/* OpenSSL 3+ uses EVP_default_properties_is_fips_enabled() */
#elif defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
#include <openssl/evp.h>
#define GUAC_FIPS_ENABLED EVP_default_properties_is_fips_enabled(NULL)
/* For OpenSSL versions between 0.9.7e and 3.0, use FIPS_mode() */
#else
#include <openssl/crypto.h>
#define GUAC_FIPS_ENABLED FIPS_mode()
#endif
/* FIPS support does not exist if OpenSSL is not available. */
#else
#define GUAC_FIPS_ENABLED 0
#endif
int guac_fips_enabled() {
return GUAC_FIPS_ENABLED;
}

View File

@ -509,18 +509,47 @@ void* guac_client_for_user(guac_client* client, guac_user* user,
guac_user_callback* callback, void* data);
/**
* Marks the end of the current frame by sending a "sync" instruction to
* all connected users. This instruction will contain the current timestamp.
* The last_sent_timestamp member of guac_client will be updated accordingly.
* Marks the end of the current frame by sending a "sync" instruction to all
* connected users, where the number of input frames that were considered in
* creating this frame is either unknown or inapplicable. This instruction will
* contain the current timestamp. The last_sent_timestamp member of guac_client
* will be updated accordingly.
*
* If an error occurs sending the instruction, a non-zero value is
* returned, and guac_error is set appropriately.
*
* @param client The guac_client which has finished a frame.
* @return Zero on success, non-zero on error.
* @param client
* The guac_client which has finished a frame.
*
* @return
* Zero on success, non-zero on error.
*/
int guac_client_end_frame(guac_client* client);
/**
* Marks the end of the current frame by sending a "sync" instruction to all
* connected users, where that frame may combine or otherwise represent the
* changes of an arbitrary number of input frames. This instruction will
* contain the current timestamp, as well as the number of frames that were
* considered in creating that frame. The last_sent_timestamp member of
* guac_client will be updated accordingly.
*
* If an error occurs sending the instruction, a non-zero value is
* returned, and guac_error is set appropriately.
*
* @param client
* The guac_client which has finished a frame.
*
* @param frames
* The number of distinct frames that were considered or combined when
* generating the current frame, or zero if the boundaries of relevant
* frames are unknown.
*
* @return
* Zero on success, non-zero on error.
*/
int guac_client_end_multiple_frames(guac_client* client, int frames);
/**
* Initializes the given guac_client using the initialization routine provided
* by the plugin corresponding to the named protocol. This will automatically
@ -708,6 +737,21 @@ 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 owner of the given client supports the "msg"
* instruction, returning non-zero if the client owner does support the
* instruction, or zero if the owner does not.
*
* @param client
* The Guacamole client whose owner should be checked for supporting
* the "msg" instruction.
*
* @return
* Non-zero if the owner of the given client supports the "msg"
* instruction, zero otherwise.
*/
int guac_client_owner_supports_msg(guac_client* client);
/**
* Returns whether the owner of the given client supports the "required"
* instruction, returning non-zero if the client owner does support the
@ -723,6 +767,42 @@ void guac_client_stream_webp(guac_client* client, guac_socket* socket,
*/
int guac_client_owner_supports_required(guac_client* client);
/**
* Notifies the owner of the given client that a user has joined the connection,
* and returns zero if the message was sent successfully, or non-zero if the
* notification failed.
*
* @param client
* The Guacamole Client whose owner should be notified of a user joining
* the connection.
*
* @param joiner
* The Guacamole User who joined the connection.
*
* @return
* Zero if the notification to the owner was sent successfully, or non-zero
* if an error occurred.
*/
int guac_client_owner_notify_join(guac_client* client, guac_user* joiner);
/**
* Notifies the owner of the given client that a user has left the connection,
* and returns zero if the message was sent successfully, or non-zero if the
* notification failed.
*
* @param client
* The Guacamole Client whose owner should be notified of a user leaving
* the connection.
*
* @param quitter
* The Guacamole User who left the connection.
*
* @return
* Zero if the notification to the owner was sent successfully, or non-zero
* if an error occurred.
*/
int guac_client_owner_notify_leave(guac_client* client, guac_user* quitter);
/**
* Returns whether all users of the given client support WebP. If any user does
* not support WebP, or the server cannot encode WebP images, zero is returned.

View File

@ -17,22 +17,17 @@
* under the License.
*/
#include "config.h"
#ifndef GUAC_FIPS_H
#define GUAC_FIPS_H
#include <openssl/bn.h>
#include <openssl/rsa.h>
#include <stdlib.h>
#ifndef HAVE_RSA_GET0_KEY
void RSA_get0_key(const RSA* rsa_key, const BIGNUM** n,
const BIGNUM** e, const BIGNUM**d) {
/* Retrieve all requested internal values */
if (n != NULL) *n = rsa_key->n;
if (e != NULL) *e = rsa_key->e;
if (d != NULL) *d = rsa_key->d;
}
#endif
/**
* Returns a non-zero value if FIPS mode is enabled, or zero if FIPS mode
* is not enabled.
*
* @return
* A non-zero value if FIPS mode is enabled, or zero if FIPS mode is
* not enabled.
*/
int guac_fips_enabled();
#endif

View File

@ -38,7 +38,7 @@
* This version is passed by the __guac_protocol_send_args() function from the
* server to the client during the client/server handshake.
*/
#define GUACAMOLE_PROTOCOL_VERSION "VERSION_1_3_0"
#define GUACAMOLE_PROTOCOL_VERSION "VERSION_1_5_0"
/**
* The maximum number of bytes that should be sent in any one blob instruction

View File

@ -306,9 +306,40 @@ typedef enum guac_protocol_version {
* allowing connections in guacd to request information from the client and
* await a response.
*/
GUAC_PROTOCOL_VERSION_1_3_0 = 0x010300
GUAC_PROTOCOL_VERSION_1_3_0 = 0x010300,
/**
* Protocol version 1.5.0, which supports the "msg" instruction, allowing
* messages to be sent to the client, and adds support for the "name"
* handshake instruction.
*/
GUAC_PROTOCOL_VERSION_1_5_0 = 0x010500
} guac_protocol_version;
/**
* A type that represents codes for human-readable messages sent by the "msg"
* instruction to the Client, that will be displayed in the client's browser.
* The codes will be interpreted by the client into translatable messages, and
* make take arguments, as noted below.
*/
typedef enum guac_message_type {
/**
* A message that notifies the owner of a connection that another user has
* joined their connection. There should be a single argument provided, the
* name of the user who has joined.
*/
GUAC_MESSAGE_USER_JOINED = 0x0001,
/**
* A message that notifies the owner of a connection that another user has
* left their connection. There should be a single argument provided, the
* name of the user who has left.
*/
GUAC_MESSAGE_USER_LEFT = 0x0002
} guac_message_type;
#endif

View File

@ -171,6 +171,27 @@ int guac_protocol_send_log(guac_socket* socket, const char* format, ...);
int vguac_protocol_send_log(guac_socket* socket, const char* format,
va_list args);
/**
* Sends the given string over the socket to be displayed on the client. Returns
* zero if the message was sent successfully or non-zero if an error occurs.
*
* @param socket
* The guac_socket connection to send the message to.
*
* @param msg
* The message code to send to the client.
*
* @param args
* A null-terminated array of strings that will be provided to the client
* as part of the message, that the client may then place in the message,
* or null if the message requires no arguments.
*
* @return
* Zero if the message is sent successfully; otherwise non-zero.
*/
int guac_protocol_send_msg(guac_socket* socket, guac_message_type msg,
const char** args);
/**
* Sends a mouse instruction over the given guac_socket connection.
*
@ -363,11 +384,22 @@ int guac_protocol_send_select(guac_socket* socket, const char* protocol);
* 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 timestamp The current timestamp (in milliseconds).
* @return Zero on success, non-zero on error.
* @param socket
* The guac_socket connection to use.
*
* @param timestamp
* The current timestamp (in milliseconds).
*
* @param frames
* The number of distinct frames that were considered or combined when
* generating the frame terminated by this instruction, or zero if the
* boundaries of relevant frames are unknown.
*
* @return
* Zero on success, non-zero on error.
*/
int guac_protocol_send_sync(guac_socket* socket, guac_timestamp timestamp);
int guac_protocol_send_sync(guac_socket* socket, guac_timestamp timestamp,
int frames);
/* OBJECT INSTRUCTIONS */

View File

@ -17,11 +17,17 @@
* under the License.
*/
#ifndef GUAC_COMMON_RECORDING_H
#define GUAC_COMMON_RECORDING_H
#ifndef GUAC_RECORDING_H
#define GUAC_RECORDING_H
#include <guacamole/client.h>
/**
* Provides functions and structures to be use for session recording.
*
* @file recording.h
*/
/**
* The maximum numeric value allowed for the .1, .2, .3, etc. suffix appended
* to the end of the session recording filename if a recording having the
@ -47,7 +53,7 @@
* that output Guacamole instructions may be dynamically intercepted and
* written to a file.
*/
typedef struct guac_common_recording {
typedef struct guac_recording {
/**
* The guac_socket which writes directly to the recording file, rather than
@ -89,7 +95,7 @@ typedef struct guac_common_recording {
*/
int include_keys;
} guac_common_recording;
} guac_recording;
/**
* Replaces the socket of the given client such that all further Guacamole
@ -143,11 +149,11 @@ typedef struct guac_common_recording {
* passwords, credit card numbers, etc.
*
* @return
* A new guac_common_recording structure representing the in-progress
* A new guac_recording structure representing the in-progress
* recording if the recording file has been successfully created and a
* recording will be written, NULL otherwise.
*/
guac_common_recording* guac_common_recording_create(guac_client* client,
guac_recording* guac_recording_create(guac_client* client,
const char* path, const char* name, int create_path,
int include_output, int include_mouse, int include_touch,
int include_keys);
@ -159,15 +165,15 @@ guac_common_recording* guac_common_recording_create(guac_client* client,
* freed when the guac_client is freed.
*
* @param recording
* The guac_common_recording to free.
* The guac_recording to free.
*/
void guac_common_recording_free(guac_common_recording* recording);
void guac_recording_free(guac_recording* recording);
/**
* Reports the current mouse position and button state within the recording.
*
* @param recording
* The guac_common_recording associated with the mouse that has moved.
* The guac_recording associated with the mouse that has moved.
*
* @param x
* The new X coordinate of the mouse cursor, in pixels.
@ -188,14 +194,14 @@ void guac_common_recording_free(guac_common_recording* recording);
* @see GUAC_CLIENT_MOUSE_SCROLL_UP
* @see GUAC_CLIENT_MOUSE_SCROLL_DOWN
*/
void guac_common_recording_report_mouse(guac_common_recording* recording,
void guac_recording_report_mouse(guac_recording* recording,
int x, int y, int button_mask);
/**
* Reports the current state of a touch contact within the recording.
*
* @param recording
* The guac_common_recording associated with the touch contact that
* The guac_recording associated with the touch contact that
* has changed state.
*
* @param id
@ -225,7 +231,7 @@ void guac_common_recording_report_mouse(guac_common_recording* recording,
* (the touch has been lifted) and 1 is maximum force (the maximum amount
* of force representable by the device).
*/
void guac_common_recording_report_touch(guac_common_recording* recording,
void guac_recording_report_touch(guac_recording* recording,
int id, int x, int y, int x_radius, int y_radius,
double angle, double force);
@ -233,7 +239,7 @@ void guac_common_recording_report_touch(guac_common_recording* recording,
* Reports a change in the state of an individual key within the recording.
*
* @param recording
* The guac_common_recording associated with the key that was pressed or
* The guac_recording associated with the key that was pressed or
* released.
*
* @param keysym
@ -243,7 +249,7 @@ void guac_common_recording_report_touch(guac_common_recording* recording,
* Non-zero if the key represented by the given keysym is currently
* pressed, zero if it is released.
*/
void guac_common_recording_report_key(guac_common_recording* recording,
void guac_recording_report_key(guac_recording* recording,
int keysym, int pressed);
#endif

View File

@ -109,6 +109,28 @@ size_t guac_strlcpy(char* restrict dest, const char* restrict src, size_t n);
*/
size_t guac_strlcat(char* restrict dest, const char* restrict src, size_t n);
/**
* Search for the null-terminated string needle in the possibly null-
* terminated haystack, looking at no more than len bytes.
*
* @param haystack
* The string to search. It may or may not be null-terminated. Only the
* first len bytes are searched.
*
* @param needle
* The string to look for. It must be null-terminated.
*
* @param len
* The maximum number of bytes to examine in haystack.
*
* @return
* A pointer to the first instance of needle within haystack, or NULL if
* needle does not exist in haystack. If needle is the empty string,
* haystack is returned.
*
*/
char* guac_strnstr(const char *haystack, const char *needle, size_t len);
/**
* Simple wrapper for strdup() which behaves identically to standard strdup(),
* except that NULL will be returned if the provided string is NULL.

View File

@ -88,7 +88,7 @@ struct guac_user_info {
* stated resolution of the display size request is recommended.
*/
int optimal_resolution;
/**
* The timezone of the remote system. If the client does not provide
* a specific timezone then this will be NULL. The format of the timezone
@ -102,6 +102,14 @@ struct guac_user_info {
*/
guac_protocol_version protocol_version;
/**
* The human-readable name of the Guacamole user, supplied by the client
* during the handshake. This is an arbitrary value, with no requirements or
* constraints, including that it need not uniquely identify the user.
* If the client does not provide a name then this will be NULL.
*/
const char* name;
};
struct guac_user {
@ -850,6 +858,17 @@ void guac_user_stream_webp(guac_user* user, 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 user supports the "msg" instruction.
*
* @param user
* The Guacamole user to check for support of the "msg" instruction.
*
* @return
* Non-zero if the user supports the "msg" instruction, otherwise zero.
*/
int guac_user_supports_msg(guac_user* user);
/**
* Returns whether the given user supports the "required" instruction.
*

View File

@ -65,6 +65,7 @@ guac_protocol_version_mapping guac_protocol_version_table[] = {
{ GUAC_PROTOCOL_VERSION_1_0_0, "VERSION_1_0_0" },
{ GUAC_PROTOCOL_VERSION_1_1_0, "VERSION_1_1_0" },
{ GUAC_PROTOCOL_VERSION_1_3_0, "VERSION_1_3_0" },
{ GUAC_PROTOCOL_VERSION_1_5_0, "VERSION_1_5_0" },
{ GUAC_PROTOCOL_VERSION_UNKNOWN, NULL }
};
@ -658,6 +659,23 @@ int guac_protocol_send_log(guac_socket* socket, const char* format, ...) {
}
int guac_protocol_send_msg(guac_socket* socket, guac_message_type msg,
const char** args) {
int ret_val;
guac_socket_instruction_begin(socket);
ret_val =
guac_socket_write_string(socket, "3.msg,")
|| __guac_socket_write_length_int(socket, msg)
|| guac_socket_write_array(socket, args)
|| guac_socket_write_string(socket, ";");
guac_socket_instruction_end(socket);
return ret_val;
}
int guac_protocol_send_file(guac_socket* socket, const guac_stream* stream,
const char* mimetype, const char* name) {
@ -1181,7 +1199,8 @@ int guac_protocol_send_start(guac_socket* socket, const guac_layer* layer,
}
int guac_protocol_send_sync(guac_socket* socket, guac_timestamp timestamp) {
int guac_protocol_send_sync(guac_socket* socket, guac_timestamp timestamp,
int frames) {
int ret_val;
@ -1189,6 +1208,8 @@ int guac_protocol_send_sync(guac_socket* socket, guac_timestamp timestamp) {
ret_val =
guac_socket_write_string(socket, "4.sync,")
|| __guac_socket_write_length_int(socket, timestamp)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_int(socket, frames)
|| guac_socket_write_string(socket, ";");
guac_socket_instruction_end(socket);

View File

@ -17,12 +17,11 @@
* under the License.
*/
#include "common/recording.h"
#include <guacamole/client.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
#include <guacamole/timestamp.h>
#include "guacamole/client.h"
#include "guacamole/protocol.h"
#include "guacamole/recording.h"
#include "guacamole/socket.h"
#include "guacamole/timestamp.h"
#ifdef __MINGW32__
#include <direct.h>
@ -64,7 +63,7 @@
* The file descriptor of the open data file if open succeeded, or -1 on
* failure.
*/
static int guac_common_recording_open(const char* path,
static int guac_recording_open(const char* path,
const char* name, char* basename, int basename_size) {
int i;
@ -84,7 +83,7 @@ static int guac_common_recording_open(const char* path,
/* Attempt to open recording */
int fd = open(basename,
O_CREAT | O_EXCL | O_WRONLY,
S_IRUSR | S_IWUSR);
S_IRUSR | S_IWUSR | S_IRGRP);
/* Continuously retry with alternate names on failure */
if (fd == -1) {
@ -103,7 +102,7 @@ static int guac_common_recording_open(const char* path,
/* Retry with newly-suffixed filename */
fd = open(basename,
O_CREAT | O_EXCL | O_WRONLY,
S_IRUSR | S_IWUSR);
S_IRUSR | S_IWUSR | S_IRGRP);
}
@ -135,7 +134,7 @@ static int guac_common_recording_open(const char* path,
}
guac_common_recording* guac_common_recording_create(guac_client* client,
guac_recording* guac_recording_create(guac_client* client,
const char* path, const char* name, int create_path,
int include_output, int include_mouse, int include_touch,
int include_keys) {
@ -144,7 +143,8 @@ guac_common_recording* guac_common_recording_create(guac_client* client,
/* Create path if it does not exist, fail if impossible */
#ifndef __MINGW32__
if (create_path && mkdir(path, S_IRWXU) && errno != EEXIST) {
if (create_path && mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP)
&& errno != EEXIST) {
#else
if (create_path && _mkdir(path) && errno != EEXIST) {
#endif
@ -154,7 +154,7 @@ guac_common_recording* guac_common_recording_create(guac_client* client,
}
/* Attempt to open recording file */
int fd = guac_common_recording_open(path, name, filename, sizeof(filename));
int fd = guac_recording_open(path, name, filename, sizeof(filename));
if (fd == -1) {
guac_client_log(client, GUAC_LOG_ERROR,
"Creation of recording failed: %s", strerror(errno));
@ -162,7 +162,7 @@ guac_common_recording* guac_common_recording_create(guac_client* client,
}
/* Create recording structure with reference to underlying socket */
guac_common_recording* recording = malloc(sizeof(guac_common_recording));
guac_recording* recording = malloc(sizeof(guac_recording));
recording->socket = guac_socket_open(fd);
recording->include_output = include_output;
recording->include_mouse = include_mouse;
@ -183,7 +183,7 @@ guac_common_recording* guac_common_recording_create(guac_client* client,
}
void guac_common_recording_free(guac_common_recording* recording) {
void guac_recording_free(guac_recording* recording) {
/* If not including broadcast output, the output socket is not associated
* with the client, and must be freed manually */
@ -195,7 +195,7 @@ void guac_common_recording_free(guac_common_recording* recording) {
}
void guac_common_recording_report_mouse(guac_common_recording* recording,
void guac_recording_report_mouse(guac_recording* recording,
int x, int y, int button_mask) {
/* Report mouse location only if recording should contain mouse events */
@ -205,7 +205,7 @@ void guac_common_recording_report_mouse(guac_common_recording* recording,
}
void guac_common_recording_report_touch(guac_common_recording* recording,
void guac_recording_report_touch(guac_recording* recording,
int id, int x, int y, int x_radius, int y_radius,
double angle, double force) {
@ -216,7 +216,7 @@ void guac_common_recording_report_touch(guac_common_recording* recording,
}
void guac_common_recording_report_key(guac_common_recording* recording,
void guac_recording_report_key(guac_recording* recording,
int keysym, int pressed) {
/* Report key state only if recording should contain key events */

View File

@ -81,6 +81,38 @@ size_t guac_strlcat(char* restrict dest, const char* restrict src, size_t n) {
}
char* guac_strnstr(const char *haystack, const char *needle, size_t len) {
#ifdef HAVE_STRNSTR
return strnstr(haystack, needle, len);
#else
char* chr;
size_t nlen = strlen(needle), off = 0;
/* Follow documented API: return haystack if needle is the empty string. */
if (nlen == 0)
return (char *)haystack;
/* Use memchr to find candidates. It might be optimized in asm. */
while (off < len && NULL != (chr = memchr(haystack + off, needle[0], len - off))) {
/* chr is guaranteed to be in bounds of and >= haystack. */
off = chr - haystack;
/* If needle would go beyond provided len, it doesn't exist in haystack. */
if (off + nlen > len)
return NULL;
/* Now that we know we have at least nlen bytes, compare them. */
if (!memcmp(chr, needle, nlen))
return chr;
/* Make sure we make progress. */
off += 1;
}
/* memchr ran out of candidates, needle wasn't found. */
return NULL;
#endif
}
char* guac_strdup(const char* str) {
/* Return NULL if no string provided */

View File

@ -48,6 +48,7 @@ test_libguac_SOURCES = \
string/strlcat.c \
string/strlcpy.c \
string/strljoin.c \
string/strnstr.c \
unicode/charsize.c \
unicode/read.c \
unicode/strlen.c \

View File

@ -27,11 +27,11 @@
*/
void test_guac_protocol__version_to_string() {
guac_protocol_version version_a = GUAC_PROTOCOL_VERSION_1_3_0;
guac_protocol_version version_a = GUAC_PROTOCOL_VERSION_1_5_0;
guac_protocol_version version_b = GUAC_PROTOCOL_VERSION_1_0_0;
guac_protocol_version version_c = GUAC_PROTOCOL_VERSION_UNKNOWN;
CU_ASSERT_STRING_EQUAL(guac_protocol_version_to_string(version_a), "VERSION_1_3_0");
CU_ASSERT_STRING_EQUAL(guac_protocol_version_to_string(version_a), "VERSION_1_5_0");
CU_ASSERT_STRING_EQUAL(guac_protocol_version_to_string(version_b), "VERSION_1_0_0");
CU_ASSERT_PTR_NULL(guac_protocol_version_to_string(version_c));

View File

@ -54,7 +54,7 @@ static void write_instructions(int fd) {
/* Write instructions */
guac_protocol_send_name(socket, "a" UTF8_4 "b" UTF8_4 "c");
guac_protocol_send_sync(socket, 12345);
guac_protocol_send_sync(socket, 12345, 1);
guac_socket_flush(socket);
/* Close and free socket */
@ -76,7 +76,7 @@ static void read_expected_instructions(int fd) {
char expected[] =
"4.name,11.a" UTF8_4 "b" UTF8_4 "c;"
"4.sync,5.12345;";
"4.sync,5.12345,1.1;";
int numread;
char buffer[1024];

View File

@ -65,7 +65,7 @@ static void write_instructions(int fd) {
/* Write instructions */
guac_protocol_send_name(nested_socket, "a" UTF8_4 "b" UTF8_4 "c");
guac_protocol_send_sync(nested_socket, 12345);
guac_protocol_send_sync(nested_socket, 12345, 1);
/* Close and free sockets */
guac_socket_free(nested_socket);
@ -86,9 +86,9 @@ static void write_instructions(int fd) {
static void read_expected_instructions(int fd) {
char expected[] =
"4.nest,3.123,37."
"4.nest,3.123,41."
"4.name,11.a" UTF8_4 "b" UTF8_4 "c;"
"4.sync,5.12345;"
"4.sync,5.12345,1.1;"
";";
int numread;

View File

@ -0,0 +1,73 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <CUnit/CUnit.h>
#include <guacamole/string.h>
#include <stdlib.h>
#include <string.h>
/**
* Verify guac_strnstr() behaviors:
*/
void test_string__strnstr() {
char haystack[8] = {'a', 'h', 'i', ' ', 't', 'u', 'n', 'a'};
char* result;
/* needle exists at start of haystack */
result = guac_strnstr(haystack, "ah", sizeof(haystack));
CU_ASSERT_EQUAL(result, haystack);
/* needle exists in the middle of haystack */
result = guac_strnstr(haystack, "hi", sizeof(haystack));
CU_ASSERT_EQUAL(result, haystack + 1);
/* needle exists at end of haystack */
result = guac_strnstr(haystack, "tuna", sizeof(haystack));
CU_ASSERT_EQUAL(result, haystack + 4);
/* needle doesn't exist in haystack, needle[0] isn't in haystack */
result = guac_strnstr(haystack, "mahi", sizeof(haystack));
CU_ASSERT_EQUAL(result, NULL);
/*
* needle doesn't exist in haystack, needle[0] is in haystack,
* length wouldn't allow needle to exist
*/
result = guac_strnstr(haystack, "narwhal", sizeof(haystack));
CU_ASSERT_EQUAL(result, NULL);
/*
* needle doesn't exist in haystack, needle[0] is in haystack,
* length would allow needle to exist
*/
result = guac_strnstr(haystack, "taco", sizeof(haystack));
CU_ASSERT_EQUAL(result, NULL);
/*
* needle doesn't exist in haystack, needle[0] is in haystack
* multiple times
*/
result = guac_strnstr(haystack, "ahha", sizeof(haystack));
CU_ASSERT_EQUAL(result, NULL);
/* empty needle should return haystack according to API docs */
result = guac_strnstr(haystack, "", sizeof(haystack));
CU_ASSERT_EQUAL(result, haystack);
}

View File

@ -64,6 +64,7 @@ __guac_instruction_handler_mapping __guac_handshake_handler_map[] = {
{"video", __guac_handshake_video_handler},
{"image", __guac_handshake_image_handler},
{"timezone", __guac_handshake_timezone_handler},
{"name", __guac_handshake_name_handler},
{NULL, NULL}
};
@ -120,31 +121,39 @@ int __guac_handle_sync(guac_user* user, int argc, char** argv) {
/* Calculate length of frame, including network and processing lag */
frame_duration = current - timestamp;
/* Update lag statistics if at least one frame has been rendered */
/* Calculate processing lag portion of length of frame */
int frame_processing_lag = 0;
if (user->last_frame_duration != 0) {
/* Calculate lag using the previous frame as a baseline */
int processing_lag = frame_duration - user->last_frame_duration;
frame_processing_lag = frame_duration - user->last_frame_duration;
/* Adjust back to zero if cumulative error leads to a negative
* value */
if (processing_lag < 0)
processing_lag = 0;
user->processing_lag = processing_lag;
if (frame_processing_lag < 0)
frame_processing_lag = 0;
}
/* Record baseline duration of frame by excluding lag */
user->last_frame_duration = frame_duration - user->processing_lag;
/* Record baseline duration of frame by excluding lag (this is the
* network round-trip time) */
int estimated_rtt = frame_duration - frame_processing_lag;
user->last_frame_duration = estimated_rtt;
/* Calculate cumulative accumulated processing lag relative to server timeline */
int processing_lag = current - user->last_received_timestamp - estimated_rtt;
if (processing_lag < 0)
processing_lag = 0;
user->processing_lag = processing_lag;
}
/* Log received timestamp and calculated lag (at TRACE level only) */
guac_user_log(user, GUAC_LOG_TRACE,
"User confirmation of frame %" PRIu64 "ms received "
"at %" PRIu64 "ms (processing_lag=%ims)",
timestamp, current, user->processing_lag);
"at %" PRIu64 "ms (processing_lag=%ims, estimated_rtt=%ims)",
timestamp, current, user->processing_lag, user->last_frame_duration);
if (user->sync_handler)
return user->sync_handler(user, timestamp);
@ -676,6 +685,23 @@ int __guac_handshake_image_handler(guac_user* user, int argc, char** argv) {
}
int __guac_handshake_name_handler(guac_user* user, int argc, char** argv) {
/* Free any past value for the user's name */
free((char *) user->info.name);
/* If a value is provided for the name, copy it into guac_user. */
if (argc > 0 && strcmp(argv[0], ""))
user->info.name = (const char*) strdup(argv[0]);
/* No or empty value was provided, so make sure this is NULLed out. */
else
user->info.name = NULL;
return 0;
}
int __guac_handshake_timezone_handler(guac_user* user, int argc, char** argv) {
/* Free any past value */

View File

@ -218,6 +218,13 @@ __guac_instruction_handler __guac_handshake_video_handler;
*/
__guac_instruction_handler __guac_handshake_image_handler;
/**
* Internal handler function that is called when the name instruction is
* received during the handshake process, specifying the name of the Guacamole
* user establishing the connection.
*/
__guac_instruction_handler __guac_handshake_name_handler;
/**
* Internal handler function that is called when the timezone instruction is
* received during the handshake process, specifying the timezone of the

View File

@ -296,6 +296,7 @@ int guac_user_handle_connection(guac_user* user, int usec_timeout) {
user->info.audio_mimetypes = NULL;
user->info.image_mimetypes = NULL;
user->info.video_mimetypes = NULL;
user->info.name = NULL;
user->info.timezone = NULL;
/* Count number of arguments. */
@ -370,7 +371,8 @@ int guac_user_handle_connection(guac_user* user, int usec_timeout) {
guac_free_mimetypes((char **) user->info.image_mimetypes);
guac_free_mimetypes((char **) user->info.video_mimetypes);
/* Free timezone info. */
/* Free name and timezone info. */
free((char *) user->info.name);
free((char *) user->info.timezone);
guac_parser_free(parser);

View File

@ -316,6 +316,15 @@ void guac_user_stream_webp(guac_user* user, guac_socket* socket,
}
int guac_user_supports_msg(guac_user* user) {
if (user == NULL)
return 0;
return (user->info.protocol_version >= GUAC_PROTOCOL_VERSION_1_5_0);
}
int guac_user_supports_required(guac_user* user) {
if (user == NULL)

View File

@ -53,8 +53,9 @@ int guac_kubernetes_argv_callback(guac_user* user, const char* mimetype,
}
/* Update Kubernetes terminal size */
guac_kubernetes_resize(client, terminal->term_height,
terminal->term_width);
guac_kubernetes_resize(client,
guac_terminal_get_rows(terminal),
guac_terminal_get_columns(terminal));
return 0;
@ -67,20 +68,20 @@ void* guac_kubernetes_send_current_argv(guac_user* user, void* data) {
/* Send current color scheme */
guac_user_stream_argv(user, user->socket, "text/plain",
GUAC_KUBERNETES_ARGV_COLOR_SCHEME, terminal->color_scheme);
GUAC_KUBERNETES_ARGV_COLOR_SCHEME,
guac_terminal_get_color_scheme(terminal));
/* Send current font name */
guac_user_stream_argv(user, user->socket, "text/plain",
GUAC_KUBERNETES_ARGV_FONT_NAME, terminal->font_name);
GUAC_KUBERNETES_ARGV_FONT_NAME,
guac_terminal_get_font_name(terminal));
/* Send current font size */
char font_size[64];
sprintf(font_size, "%i", terminal->font_size);
sprintf(font_size, "%i", guac_terminal_get_font_size(terminal));
guac_user_stream_argv(user, user->socket, "text/plain",
GUAC_KUBERNETES_ARGV_FONT_SIZE, font_size);
return NULL;
}

View File

@ -19,7 +19,6 @@
#include "argv.h"
#include "client.h"
#include "common/clipboard.h"
#include "kubernetes.h"
#include "settings.h"
#include "user.h"
@ -95,9 +94,6 @@ int guac_client_init(guac_client* client) {
guac_kubernetes_client* kubernetes_client = calloc(1, sizeof(guac_kubernetes_client));
client->data = kubernetes_client;
/* Init clipboard */
kubernetes_client->clipboard = guac_common_clipboard_alloc(GUAC_KUBERNETES_CLIPBOARD_MAX_LENGTH);
/* Set handlers */
client->join_handler = guac_kubernetes_user_join_handler;
client->free_handler = guac_kubernetes_client_free_handler;
@ -133,7 +129,6 @@ int guac_kubernetes_client_free_handler(guac_client* client) {
if (kubernetes_client->settings != NULL)
guac_kubernetes_settings_free(kubernetes_client->settings);
guac_common_clipboard_free(kubernetes_client->clipboard);
free(kubernetes_client);
return 0;

View File

@ -22,11 +22,6 @@
#include <guacamole/client.h>
/**
* The maximum number of bytes to allow within the clipboard.
*/
#define GUAC_KUBERNETES_CLIPBOARD_MAX_LENGTH 262144
/**
* Static reference to the guac_client associated with the active Kubernetes
* connection. While libwebsockets provides some means of storing and

View File

@ -18,8 +18,8 @@
*/
#include "clipboard.h"
#include "common/clipboard.h"
#include "kubernetes.h"
#include "terminal/terminal.h"
#include <guacamole/client.h>
#include <guacamole/stream.h>
@ -33,7 +33,7 @@ int guac_kubernetes_clipboard_handler(guac_user* user, guac_stream* stream,
(guac_kubernetes_client*) client->data;
/* Clear clipboard and prepare for new data */
guac_common_clipboard_reset(kubernetes_client->clipboard, mimetype);
guac_terminal_clipboard_reset(kubernetes_client->term, mimetype);
/* Set handlers for clipboard stream */
stream->blob_handler = guac_kubernetes_clipboard_blob_handler;
@ -50,7 +50,7 @@ int guac_kubernetes_clipboard_blob_handler(guac_user* user,
(guac_kubernetes_client*) client->data;
/* Append new data */
guac_common_clipboard_append(kubernetes_client->clipboard, data, length);
guac_terminal_clipboard_append(kubernetes_client->term, data, length);
return 0;
}

View File

@ -17,12 +17,12 @@
* under the License.
*/
#include "common/recording.h"
#include "input.h"
#include "kubernetes.h"
#include "terminal/terminal.h"
#include <guacamole/client.h>
#include <guacamole/recording.h>
#include <guacamole/user.h>
#include <stdlib.h>
@ -41,7 +41,7 @@ int guac_kubernetes_user_mouse_handler(guac_user* user,
/* Report mouse position within recording */
if (kubernetes_client->recording != NULL)
guac_common_recording_report_mouse(kubernetes_client->recording, x, y,
guac_recording_report_mouse(kubernetes_client->recording, x, y,
mask);
guac_terminal_send_mouse(term, user, x, y, mask);
@ -57,7 +57,7 @@ int guac_kubernetes_user_key_handler(guac_user* user, int keysym, int pressed) {
/* Report key state within recording */
if (kubernetes_client->recording != NULL)
guac_common_recording_report_key(kubernetes_client->recording,
guac_recording_report_key(kubernetes_client->recording,
keysym, pressed);
/* Skip if terminal not yet ready */
@ -86,8 +86,9 @@ int guac_kubernetes_user_size_handler(guac_user* user, int width, int height) {
guac_terminal_resize(terminal, width, height);
/* Update Kubernetes terminal window size if connected */
guac_kubernetes_resize(client, terminal->term_height,
terminal->term_width);
guac_kubernetes_resize(client,
guac_terminal_get_rows(terminal),
guac_terminal_get_columns(terminal));
return 0;
}

View File

@ -21,7 +21,6 @@
#include "argv.h"
#include "client.h"
#include "common/recording.h"
#include "io.h"
#include "kubernetes.h"
#include "ssl.h"
@ -30,6 +29,7 @@
#include <guacamole/client.h>
#include <guacamole/protocol.h>
#include <guacamole/recording.h>
#include <libwebsockets.h>
#include <pthread.h>
@ -229,7 +229,7 @@ void* guac_kubernetes_client_thread(void* data) {
/* Set up screen recording, if requested */
if (settings->recording_path != NULL) {
kubernetes_client->recording = guac_common_recording_create(client,
kubernetes_client->recording = guac_recording_create(client,
settings->recording_path,
settings->recording_name,
settings->create_recording_path,
@ -239,12 +239,23 @@ void* guac_kubernetes_client_thread(void* data) {
settings->recording_include_keys);
}
/* Create terminal options with required parameters */
guac_terminal_options* options = guac_terminal_options_create(
settings->width, settings->height, settings->resolution);
/* Set optional parameters */
options->disable_copy = settings->disable_copy;
options->max_scrollback = settings->max_scrollback;
options->font_name = settings->font_name;
options->font_size = settings->font_size;
options->color_scheme = settings->color_scheme;
options->backspace = settings->backspace;
/* Create terminal */
kubernetes_client->term = guac_terminal_create(client,
kubernetes_client->clipboard, settings->disable_copy,
settings->max_scrollback, settings->font_name, settings->font_size,
settings->resolution, settings->width, settings->height,
settings->color_scheme, settings->backspace);
kubernetes_client->term = guac_terminal_create(client, options);
/* Free options struct now that it's been used */
free(options);
/* Fail if terminal init failed */
if (kubernetes_client->term == NULL) {
@ -358,7 +369,7 @@ fail:
/* Clean up recording, if in progress */
if (kubernetes_client->recording != NULL)
guac_common_recording_free(kubernetes_client->recording);
guac_recording_free(kubernetes_client->recording);
/* Free WebSocket context if successfully allocated */
if (kubernetes_client->context != NULL)
@ -402,8 +413,8 @@ void guac_kubernetes_force_redraw(guac_client* client) {
/* Get current terminal dimensions */
guac_terminal* term = kubernetes_client->term;
int rows = term->term_height;
int columns = term->term_width;
int rows = guac_terminal_get_rows(term);
int columns = guac_terminal_get_columns(term);
/* Force a redraw by increasing the terminal size by one character in
* each dimension and then resizing it back to normal (the same technique

View File

@ -21,12 +21,12 @@
#define GUAC_KUBERNETES_H
#include "common/clipboard.h"
#include "common/recording.h"
#include "io.h"
#include "settings.h"
#include "terminal/terminal.h"
#include <guacamole/client.h>
#include <guacamole/recording.h>
#include <libwebsockets.h>
#include <pthread.h>
@ -102,11 +102,6 @@ typedef struct guac_kubernetes_client {
*/
pthread_t client_thread;
/**
* The current clipboard contents.
*/
guac_common_clipboard* clipboard;
/**
* The terminal which will render all output from the Kubernetes pod.
*/
@ -128,7 +123,7 @@ typedef struct guac_kubernetes_client {
* The in-progress session recording, or NULL if no recording is in
* progress.
*/
guac_common_recording* recording;
guac_recording* recording;
} guac_kubernetes_client;

View File

@ -19,6 +19,7 @@
#include "argv.h"
#include "settings.h"
#include "terminal/terminal.h"
#include <guacamole/user.h>
@ -215,8 +216,8 @@ enum KUBERNETES_ARGS_IDX {
IDX_READ_ONLY,
/**
* ASCII code, as an integer to use for the backspace key, or 127
* if not specified.
* ASCII code, as an integer to use for the backspace key, or
* GUAC_TERMINAL_DEFAULT_BACKSPACE if not specified.
*/
IDX_BACKSPACE,
@ -320,22 +321,22 @@ guac_kubernetes_settings* guac_kubernetes_parse_args(guac_user* user,
/* Read maximum scrollback size */
settings->max_scrollback =
guac_user_parse_args_int(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
IDX_SCROLLBACK, GUAC_KUBERNETES_DEFAULT_MAX_SCROLLBACK);
IDX_SCROLLBACK, GUAC_TERMINAL_DEFAULT_MAX_SCROLLBACK);
/* Read font name */
settings->font_name =
guac_user_parse_args_string(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
IDX_FONT_NAME, GUAC_KUBERNETES_DEFAULT_FONT_NAME);
IDX_FONT_NAME, GUAC_TERMINAL_DEFAULT_FONT_NAME);
/* Read font size */
settings->font_size =
guac_user_parse_args_int(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
IDX_FONT_SIZE, GUAC_KUBERNETES_DEFAULT_FONT_SIZE);
IDX_FONT_SIZE, GUAC_TERMINAL_DEFAULT_FONT_SIZE);
/* Copy requested color scheme */
settings->color_scheme =
guac_user_parse_args_string(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
IDX_COLOR_SCHEME, "");
IDX_COLOR_SCHEME, GUAC_TERMINAL_DEFAULT_COLOR_SCHEME);
/* Pull width/height/resolution directly from user */
settings->width = user->info.optimal_width;
@ -390,7 +391,7 @@ guac_kubernetes_settings* guac_kubernetes_parse_args(guac_user* user,
/* Parse backspace key code */
settings->backspace =
guac_user_parse_args_int(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
IDX_BACKSPACE, 127);
IDX_BACKSPACE, GUAC_TERMINAL_DEFAULT_BACKSPACE);
/* Parse clipboard copy disable flag */
settings->disable_copy =

View File

@ -24,17 +24,6 @@
#include <stdbool.h>
/**
* The name of the font to use for the terminal if no name is specified.
*/
#define GUAC_KUBERNETES_DEFAULT_FONT_NAME "monospace"
/**
* The size of the font to use for the terminal if no font size is specified,
* in points.
*/
#define GUAC_KUBERNETES_DEFAULT_FONT_SIZE 12
/**
* The port to connect to when initiating any Kubernetes connection, if no
* other port is specified.
@ -57,11 +46,6 @@
*/
#define GUAC_KUBERNETES_DEFAULT_RECORDING_NAME "recording"
/**
* The default maximum scrollback size in rows.
*/
#define GUAC_KUBERNETES_DEFAULT_MAX_SCROLLBACK 1000
/**
* Settings for the Kubernetes connection. The values for this structure are
* parsed from the arguments given during the Guacamole protocol handshake

View File

@ -109,8 +109,8 @@ int guac_kubernetes_user_leave_handler(guac_user* user) {
guac_kubernetes_client* kubernetes_client =
(guac_kubernetes_client*) user->client->data;
/* Update shared cursor state */
guac_common_cursor_remove_user(kubernetes_client->term->cursor, user);
/* Remove the user from the terminal */
guac_terminal_remove_user(kubernetes_client->term, user);
/* Free settings if not owner (owner settings will be freed with client) */
if (!user->owner) {

View File

@ -57,6 +57,7 @@ libguac_client_rdp_la_SOURCES = \
channels/rdpdr/rdpdr-printer.c \
channels/rdpdr/rdpdr.c \
channels/rdpei.c \
channels/rdpgfx.c \
channels/rdpsnd/rdpsnd-messages.c \
channels/rdpsnd/rdpsnd.c \
client.c \
@ -103,6 +104,7 @@ noinst_HEADERS = \
channels/rdpdr/rdpdr-printer.h \
channels/rdpdr/rdpdr.h \
channels/rdpei.h \
channels/rdpgfx.h \
channels/rdpsnd/rdpsnd-messages.h \
channels/rdpsnd/rdpsnd.h \
client.h \
@ -228,6 +230,7 @@ BUILT_SOURCES = \
rdp_keymaps = \
$(srcdir)/keymaps/base.keymap \
$(srcdir)/keymaps/failsafe.keymap \
$(srcdir)/keymaps/cs-cz-qwertz.keymap \
$(srcdir)/keymaps/de_de_qwertz.keymap \
$(srcdir)/keymaps/de_ch_qwertz.keymap \
$(srcdir)/keymaps/en_gb_qwerty.keymap \
@ -235,12 +238,14 @@ rdp_keymaps = \
$(srcdir)/keymaps/es_es_qwerty.keymap \
$(srcdir)/keymaps/es_latam_qwerty.keymap \
$(srcdir)/keymaps/fr_be_azerty.keymap \
$(srcdir)/keymaps/fr_ca_qwerty.keymap \
$(srcdir)/keymaps/fr_ch_qwertz.keymap \
$(srcdir)/keymaps/fr_fr_azerty.keymap \
$(srcdir)/keymaps/hu_hu_qwertz.keymap \
$(srcdir)/keymaps/it_it_qwerty.keymap \
$(srcdir)/keymaps/ja_jp_qwerty.keymap \
$(srcdir)/keymaps/no_no_qwerty.keymap \
$(srcdir)/keymaps/pl_pl_qwerty.keymap \
$(srcdir)/keymaps/pt_br_qwerty.keymap \
$(srcdir)/keymaps/sv_se_qwerty.keymap \
$(srcdir)/keymaps/da_dk_qwerty.keymap \

View File

@ -149,7 +149,7 @@ BOOL guac_rdp_bitmap_setsurface(rdpContext* context, rdpBitmap* bitmap, BOOL pri
else {
/* Make sure that the recieved bitmap is not NULL before processing */
/* Make sure that the received bitmap is not NULL before processing */
if (bitmap == NULL) {
guac_client_log(client, GUAC_LOG_INFO, "NULL bitmap found in bitmap_setsurface instruction.");
return TRUE;

View File

@ -358,7 +358,7 @@ static UINT guac_rdp_cliprdr_format_data_request(CliprdrClientContext* cliprdr,
guac_iconv_write* remote_writer;
const char* input = clipboard->clipboard->buffer;
char* output = malloc(GUAC_RDP_CLIPBOARD_MAX_LENGTH);
char* output = malloc(GUAC_COMMON_CLIPBOARD_MAX_LENGTH);
/* Map requested clipboard format to a guac_iconv writer */
switch (format_data_request->requestedFormatId) {
@ -389,7 +389,7 @@ static UINT guac_rdp_cliprdr_format_data_request(CliprdrClientContext* cliprdr,
BYTE* start = (BYTE*) output;
guac_iconv_read* local_reader = settings->normalize_clipboard ? GUAC_READ_UTF8_NORMALIZED : GUAC_READ_UTF8;
guac_iconv(local_reader, &input, clipboard->clipboard->length,
remote_writer, &output, GUAC_RDP_CLIPBOARD_MAX_LENGTH);
remote_writer, &output, GUAC_COMMON_CLIPBOARD_MAX_LENGTH);
CLIPRDR_FORMAT_DATA_RESPONSE data_response = {
.requestedFormatData = (BYTE*) start,
@ -449,7 +449,7 @@ static UINT guac_rdp_cliprdr_format_data_response(CliprdrClientContext* cliprdr,
return CHANNEL_RC_OK;
}
char received_data[GUAC_RDP_CLIPBOARD_MAX_LENGTH];
char received_data[GUAC_COMMON_CLIPBOARD_MAX_LENGTH];
guac_iconv_read* remote_reader;
const char* input = (char*) format_data_response->requestedFormatData;
@ -509,12 +509,12 @@ static UINT guac_rdp_cliprdr_format_data_response(CliprdrClientContext* cliprdr,
* @param context
* The rdpContext associated with the active RDP session.
*
* @param e
* @param args
* 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_cliprdr_channel_connected(rdpContext* context,
ChannelConnectedEventArgs* e) {
ChannelConnectedEventArgs* args) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
@ -526,12 +526,12 @@ static void guac_rdp_cliprdr_channel_connected(rdpContext* context,
assert(clipboard != NULL);
/* Ignore connection event if it's not for the CLIPRDR channel */
if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) != 0)
if (strcmp(args->name, CLIPRDR_SVC_CHANNEL_NAME) != 0)
return;
/* The structure pointed to by pInterface is guaranteed to be a
* CliprdrClientContext if the channel is CLIPRDR */
CliprdrClientContext* cliprdr = (CliprdrClientContext*) e->pInterface;
CliprdrClientContext* cliprdr = (CliprdrClientContext*) args->pInterface;
/* Associate FreeRDP CLIPRDR context and its Guacamole counterpart with
* eachother */
@ -562,12 +562,12 @@ static void guac_rdp_cliprdr_channel_connected(rdpContext* context,
* @param context
* The rdpContext associated with the active RDP session.
*
* @param e
* @param args
* 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_cliprdr_channel_disconnected(rdpContext* context,
ChannelDisconnectedEventArgs* e) {
ChannelDisconnectedEventArgs* args) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
@ -579,7 +579,7 @@ static void guac_rdp_cliprdr_channel_disconnected(rdpContext* context,
assert(clipboard != NULL);
/* Ignore disconnection event if it's not for the CLIPRDR channel */
if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) != 0)
if (strcmp(args->name, CLIPRDR_SVC_CHANNEL_NAME) != 0)
return;
/* Channel is no longer connected */
@ -595,7 +595,7 @@ guac_rdp_clipboard* guac_rdp_clipboard_alloc(guac_client* client) {
/* Allocate clipboard and underlying storage */
guac_rdp_clipboard* clipboard = calloc(1, sizeof(guac_rdp_clipboard));
clipboard->client = client;
clipboard->clipboard = guac_common_clipboard_alloc(GUAC_RDP_CLIPBOARD_MAX_LENGTH);
clipboard->clipboard = guac_common_clipboard_alloc();
clipboard->requested_format = CF_TEXT;
return clipboard;

View File

@ -115,7 +115,7 @@ struct guac_rdp_common_svc {
guac_rdp_common_svc_receive_handler* _receive_handler;
/**
* Handler which is invokved when the SVC has been disconnected and is
* Handler which is involved when the SVC has been disconnected and is
* about to be freed.
*/
guac_rdp_common_svc_terminate_handler* _terminate_handler;

View File

@ -19,6 +19,7 @@
#include "channels/disp.h"
#include "plugins/channels.h"
#include "fs.h"
#include "rdp.h"
#include "settings.h"
@ -67,19 +68,19 @@ void guac_rdp_disp_free(guac_rdp_disp* disp) {
* @param context
* The rdpContext associated with the active RDP session.
*
* @param e
* @param args
* 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_disp_channel_connected(rdpContext* context,
ChannelConnectedEventArgs* e) {
ChannelConnectedEventArgs* args) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
guac_rdp_disp* guac_disp = rdp_client->disp;
/* Ignore connection event if it's not for the Display Update channel */
if (strcmp(e->name, DISP_DVC_CHANNEL_NAME) != 0)
if (strcmp(args->name, DISP_DVC_CHANNEL_NAME) != 0)
return;
/* Init module with current display size */
@ -88,7 +89,7 @@ static void guac_rdp_disp_channel_connected(rdpContext* context,
guac_rdp_get_height(context->instance));
/* Store reference to the display update plugin once it's connected */
DispClientContext* disp = (DispClientContext*) e->pInterface;
DispClientContext* disp = (DispClientContext*) args->pInterface;
guac_disp->disp = disp;
guac_client_log(client, GUAC_LOG_DEBUG, "Display update channel "
@ -109,19 +110,19 @@ static void guac_rdp_disp_channel_connected(rdpContext* context,
* @param context
* The rdpContext associated with the active RDP session.
*
* @param e
* @param args
* 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_disp_channel_disconnected(rdpContext* context,
ChannelDisconnectedEventArgs* e) {
ChannelDisconnectedEventArgs* args) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
guac_rdp_disp* guac_disp = rdp_client->disp;
/* Ignore disconnection event if it's not for the Display Update channel */
if (strcmp(e->name, DISP_DVC_CHANNEL_NAME) != 0)
if (strcmp(args->name, DISP_DVC_CHANNEL_NAME) != 0)
return;
/* Channel is no longer connected */
@ -277,6 +278,18 @@ void guac_rdp_disp_update_size(guac_rdp_disp* disp,
}
int guac_rdp_disp_reconnect_needed(guac_rdp_disp* disp) {
guac_rdp_client* rdp_client = (guac_rdp_client*) disp->client->data;
/* Do not reconnect if files are open. */
if (rdp_client->filesystem != NULL
&& rdp_client->filesystem->open_files > 0)
return 0;
/* Do not reconnect if an active print job is present */
if (rdp_client->active_job != NULL)
return 0;
return disp->reconnect_needed;
}

View File

@ -109,7 +109,7 @@ void guac_rdp_disp_free(guac_rdp_disp* disp);
/**
* Adds FreeRDP's "disp" plugin to the list of dynamic virtual channel plugins
* to be loaded by FreeRDP's "drdynvc" plugin. The context of the plugin will
* automatically be assicated with the guac_rdp_disp instance pointed to by the
* automatically be associated with the guac_rdp_disp instance pointed to by the
* current guac_rdp_client. The plugin will only be loaded once the "drdynvc"
* plugin is loaded. The "disp" plugin ultimately adds support for the Display
* Update channel.

View File

@ -230,22 +230,22 @@ static UINT guac_rdp_rail_handshake_ex(RailClientContext* rail,
* @param context
* The rdpContext associated with the active RDP session.
*
* @param e
* @param args
* 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_rail_channel_connected(rdpContext* context,
ChannelConnectedEventArgs* e) {
ChannelConnectedEventArgs* args) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
/* Ignore connection event if it's not for the RAIL channel */
if (strcmp(e->name, RAIL_SVC_CHANNEL_NAME) != 0)
if (strcmp(args->name, RAIL_SVC_CHANNEL_NAME) != 0)
return;
/* The structure pointed to by pInterface is guaranteed to be a
* RailClientContext if the channel is RAIL */
RailClientContext* rail = (RailClientContext*) e->pInterface;
RailClientContext* rail = (RailClientContext*) args->pInterface;
/* Init FreeRDP RAIL context, ensuring the guac_client can be accessed from
* within any RAIL-specific callbacks */

View File

@ -66,23 +66,23 @@ void guac_rdp_rdpei_free(guac_rdp_rdpei* rdpei) {
* @param context
* The rdpContext associated with the active RDP session.
*
* @param e
* @param args
* 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_rdpei_channel_connected(rdpContext* context,
ChannelConnectedEventArgs* e) {
ChannelConnectedEventArgs* args) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
guac_rdp_rdpei* guac_rdpei = rdp_client->rdpei;
/* Ignore connection event if it's not for the RDPEI channel */
if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) != 0)
if (strcmp(args->name, RDPEI_DVC_CHANNEL_NAME) != 0)
return;
/* Store reference to the RDPEI plugin once it's connected */
RdpeiClientContext* rdpei = (RdpeiClientContext*) e->pInterface;
RdpeiClientContext* rdpei = (RdpeiClientContext*) args->pInterface;
guac_rdpei->rdpei = rdpei;
/* Declare level of multi-touch support */
@ -107,19 +107,19 @@ static void guac_rdp_rdpei_channel_connected(rdpContext* context,
* @param context
* The rdpContext associated with the active RDP session.
*
* @param e
* @param args
* 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_rdpei_channel_disconnected(rdpContext* context,
ChannelDisconnectedEventArgs* e) {
ChannelDisconnectedEventArgs* args) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
guac_rdp_rdpei* guac_rdpei = rdp_client->rdpei;
/* Ignore disconnection event if it's not for the RDPEI channel */
if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) != 0)
if (strcmp(args->name, RDPEI_DVC_CHANNEL_NAME) != 0)
return;
/* Channel is no longer connected */

View File

@ -110,7 +110,7 @@ void guac_rdp_rdpei_free(guac_rdp_rdpei* rdpei);
/**
* Adds FreeRDP's "rdpei" plugin to the list of dynamic virtual channel plugins
* to be loaded by FreeRDP's "drdynvc" plugin. The context of the plugin will
* automatically be assicated with the guac_rdp_rdpei instance pointed to by the
* automatically be associated with the guac_rdp_rdpei instance pointed to by the
* current guac_rdp_client. The plugin will only be loaded once the "drdynvc"
* plugin is loaded. The "rdpei" plugin ultimately adds support for multi-touch
* input via the RDPEI channel.

View File

@ -0,0 +1,122 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "channels/rdpgfx.h"
#include "plugins/channels.h"
#include "rdp.h"
#include "settings.h"
#include <freerdp/client/rdpgfx.h>
#include <freerdp/freerdp.h>
#include <freerdp/gdi/gfx.h>
#include <freerdp/event.h>
#include <guacamole/client.h>
#include <stdlib.h>
#include <string.h>
/**
* Callback which associates handlers specific to Guacamole with the
* RdpgfxClientContext instance allocated by FreeRDP to deal with received
* RDPGFX (Graphics Pipeline) messages.
*
* This function is called whenever a channel connects via the PubSub event
* system within FreeRDP, but only has any effect if the connected channel is
* the RDPGFX channel. This specific callback is registered with the
* PubSub system of the relevant rdpContext when guac_rdp_rdpgfx_load_plugin() is
* called.
*
* @param context
* The rdpContext associated with the active RDP session.
*
* @param args
* 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_rdpgfx_channel_connected(rdpContext* context,
ChannelConnectedEventArgs* args) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
/* Ignore connection event if it's not for the RDPGFX channel */
if (strcmp(args->name, RDPGFX_DVC_CHANNEL_NAME) != 0)
return;
/* Init GDI-backed support for the Graphics Pipeline */
RdpgfxClientContext* rdpgfx = (RdpgfxClientContext*) args->pInterface;
rdpGdi* gdi = context->gdi;
if (!gdi_graphics_pipeline_init(gdi, rdpgfx))
guac_client_log(client, GUAC_LOG_WARNING, "Rendering backend for RDPGFX "
"channel could not be loaded. Graphics may not render at all!");
else
guac_client_log(client, GUAC_LOG_DEBUG, "RDPGFX channel will be used for "
"the RDP Graphics Pipeline Extension.");
}
/**
* Callback which handles any RDPGFX cleanup specific to Guacamole.
*
* This function is called whenever a channel disconnects via the PubSub event
* system within FreeRDP, but only has any effect if the disconnected channel
* is the RDPGFX channel. This specific callback is registered with the PubSub
* system of the relevant rdpContext when guac_rdp_rdpgfx_load_plugin() is
* called.
*
* @param context
* The rdpContext associated with the active RDP session.
*
* @param args
* 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_rdpgfx_channel_disconnected(rdpContext* context,
ChannelDisconnectedEventArgs* args) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
/* Ignore disconnection event if it's not for the RDPGFX channel */
if (strcmp(args->name, RDPGFX_DVC_CHANNEL_NAME) != 0)
return;
/* Un-init GDI-backed support for the Graphics Pipeline */
RdpgfxClientContext* rdpgfx = (RdpgfxClientContext*) args->pInterface;
rdpGdi* gdi = context->gdi;
gdi_graphics_pipeline_uninit(gdi, rdpgfx);
guac_client_log(client, GUAC_LOG_DEBUG, "RDPGFX channel support unloaded.");
}
void guac_rdp_rdpgfx_load_plugin(rdpContext* context) {
/* Subscribe to and handle channel connected events */
PubSub_SubscribeChannelConnected(context->pubSub,
(pChannelConnectedEventHandler) guac_rdp_rdpgfx_channel_connected);
/* Subscribe to and handle channel disconnected events */
PubSub_SubscribeChannelDisconnected(context->pubSub,
(pChannelDisconnectedEventHandler) guac_rdp_rdpgfx_channel_disconnected);
/* Add "rdpgfx" channel */
guac_freerdp_dynamic_channel_collection_add(context->settings, "rdpgfx", NULL);
}

View File

@ -0,0 +1,49 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef GUAC_RDP_CHANNELS_RDPGFX_H
#define GUAC_RDP_CHANNELS_RDPGFX_H
#include "settings.h"
#include <freerdp/client/rdpgfx.h>
#include <freerdp/freerdp.h>
#include <guacamole/client.h>
/**
* Adds FreeRDP's "rdpgfx" plugin to the list of dynamic virtual channel plugins
* to be loaded by FreeRDP's "drdynvc" plugin. The context of the plugin will
* automatically be associated with the guac_rdp_rdpgfx instance pointed to by the
* current guac_rdp_client. The plugin will only be loaded once the "drdynvc"
* plugin is loaded. The "rdpgfx" plugin ultimately adds support for the RDP
* Graphics Pipeline Extension.
*
* If failures occur, messages noting the specifics of those failures will be
* logged.
*
* This MUST be called within the PreConnect callback of the freerdp instance
* for Graphics Pipeline support to be loaded.
*
* @param context
* The rdpContext associated with the active RDP session.
*/
void guac_rdp_rdpgfx_load_plugin(rdpContext* context);
#endif

View File

@ -21,7 +21,6 @@
#include "channels/audio-input/audio-buffer.h"
#include "channels/cliprdr.h"
#include "channels/disp.h"
#include "common/recording.h"
#include "config.h"
#include "fs.h"
#include "log.h"
@ -37,6 +36,7 @@
#include <guacamole/audio.h>
#include <guacamole/client.h>
#include <guacamole/recording.h>
#include <dirent.h>
#include <errno.h>
@ -199,6 +199,14 @@ int guac_rdp_client_free_handler(guac_client* client) {
if (rdp_client->filesystem != NULL)
guac_rdp_fs_free(rdp_client->filesystem);
/* End active print job, if any */
guac_rdp_print_job* job = (guac_rdp_print_job*) rdp_client->active_job;
if (job != NULL) {
guac_rdp_print_job_kill(job);
guac_rdp_print_job_free(job);
rdp_client->active_job = NULL;
}
#ifdef ENABLE_COMMON_SSH
/* Free SFTP filesystem, if loaded */
if (rdp_client->sftp_filesystem)
@ -217,7 +225,7 @@ int guac_rdp_client_free_handler(guac_client* client) {
/* Clean up recording, if in progress */
if (rdp_client->recording != NULL)
guac_common_recording_free(rdp_client->recording);
guac_recording_free(rdp_client->recording);
/* Clean up audio stream, if allocated */
if (rdp_client->audio != NULL)

View File

@ -62,11 +62,6 @@
*/
#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.

View File

@ -26,6 +26,7 @@
#include <cairo/cairo.h>
#include <freerdp/freerdp.h>
#include <freerdp/gdi/gdi.h>
#include <freerdp/graphics.h>
#include <freerdp/primary.h>
#include <guacamole/client.h>
@ -247,7 +248,7 @@ BOOL guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
int x_src = memblt->nXSrc;
int y_src = memblt->nYSrc;
/* Make sure that the recieved bitmap is not NULL before processing */
/* Make sure that the received bitmap is not NULL before processing */
if (bitmap == NULL) {
guac_client_log(client, GUAC_LOG_INFO, "NULL bitmap found in memblt instruction.");
return TRUE;
@ -371,11 +372,112 @@ BOOL guac_rdp_gdi_set_bounds(rdpContext* context, const rdpBounds* bounds) {
}
BOOL guac_rdp_gdi_end_paint(rdpContext* context) {
/* IGNORE */
void guac_rdp_gdi_mark_frame(rdpContext* context, int starting) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
/* The server supports defining explicit frames */
rdp_client->frames_supported = 1;
/* A new frame is beginning */
if (starting) {
rdp_client->in_frame = 1;
return;
}
/* The current frame has ended */
guac_timestamp frame_end = guac_timestamp_current();
int time_elapsed = frame_end - client->last_sent_timestamp;
rdp_client->in_frame = 0;
/* A new frame has been received from the RDP server and processed */
rdp_client->frames_received++;
/* Flush a new frame if the client is ready for it */
if (time_elapsed >= guac_client_get_processing_lag(client)) {
guac_common_display_flush(rdp_client->display);
guac_client_end_multiple_frames(client, rdp_client->frames_received);
guac_socket_flush(client->socket);
rdp_client->frames_received = 0;
}
}
BOOL guac_rdp_gdi_frame_marker(rdpContext* context, const FRAME_MARKER_ORDER* frame_marker) {
guac_rdp_gdi_mark_frame(context, frame_marker->action == FRAME_START);
return TRUE;
}
BOOL guac_rdp_gdi_surface_frame_marker(rdpContext* context, const SURFACE_FRAME_MARKER* surface_frame_marker) {
guac_rdp_gdi_mark_frame(context, surface_frame_marker->frameAction == SURFACECMD_FRAMEACTION_END);
if (context->settings->FrameAcknowledge > 0)
IFCALL(context->update->SurfaceFrameAcknowledge, context,
surface_frame_marker->frameId);
return TRUE;
}
BOOL guac_rdp_gdi_begin_paint(rdpContext* context) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
/* Leverage BeginPaint handler to detect start of frame for RDPGFX channel */
if (rdp_client->settings->enable_gfx && rdp_client->frames_supported)
guac_rdp_gdi_mark_frame(context, 1);
return TRUE;
}
BOOL guac_rdp_gdi_end_paint(rdpContext* context) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
rdpGdi* gdi = context->gdi;
/* Ignore EndPaint handler unless needed to detect end of frame for RDPGFX
* channel */
if (!rdp_client->settings->enable_gfx)
return TRUE;
/* Ignore paint if GDI output is suppressed */
if (gdi->suppressOutput)
return TRUE;
/* Ignore paint if nothing has been done (empty rect) */
if (gdi->primary->hdc->hwnd->invalid->null)
return TRUE;
INT32 x = gdi->primary->hdc->hwnd->invalid->x;
INT32 y = gdi->primary->hdc->hwnd->invalid->y;
UINT32 w = gdi->primary->hdc->hwnd->invalid->w;
UINT32 h = gdi->primary->hdc->hwnd->invalid->h;
/* Create surface from image data */
cairo_surface_t* surface = cairo_image_surface_create_for_data(
gdi->primary_buffer + 4*x + y*gdi->stride,
CAIRO_FORMAT_RGB24, w, h, gdi->stride);
/* Send surface to buffer */
guac_common_surface_draw(rdp_client->display->default_surface, x, y, surface);
/* Free surface */
cairo_surface_destroy(surface);
/* Next frame */
if (gdi->inGfxFrame) {
guac_rdp_gdi_mark_frame(context, 0);
}
return TRUE;
}
BOOL guac_rdp_gdi_desktop_resize(rdpContext* context) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
@ -391,7 +493,8 @@ BOOL guac_rdp_gdi_desktop_resize(rdpContext* context) {
guac_rdp_get_width(context->instance),
guac_rdp_get_height(context->instance));
return TRUE;
return gdi_resize(context->gdi, guac_rdp_get_width(context->instance),
guac_rdp_get_height(context->instance));
}

View File

@ -156,8 +156,68 @@ BOOL guac_rdp_gdi_opaquerect(rdpContext* context,
BOOL guac_rdp_gdi_set_bounds(rdpContext* context, const rdpBounds* bounds);
/**
* Handler called when a paint operation is complete. We don't actually
* use this, but FreeRDP requires it. Calling this function has no effect.
* Notifies the internal GDI implementation that a frame is either starting or
* ending. If the frame is ending and the connected client is ready to receive
* a new frame, a new frame will be flushed to the client.
*
* @param context
* The rdpContext associated with the current RDP session.
*
* @param starting
* Non-zero if the frame in question is starting, zero if the frame is
* ending.
*/
void guac_rdp_gdi_mark_frame(rdpContext* context, int starting);
/**
* Handler called when a frame boundary is received from the RDP server in the
* form of a frame marker command. Each frame boundary may be the beginning or
* the end of a frame.
*
* @param context
* The rdpContext associated with the current RDP session.
*
* @param frame_marker
* The received frame marker.
*
* @return
* TRUE if successful, FALSE otherwise.
*/
BOOL guac_rdp_gdi_frame_marker(rdpContext* context, const FRAME_MARKER_ORDER* frame_marker);
/**
* Handler called when a frame boundary is received from the RDP server in the
* form of a surface frame marker. Each frame boundary may be the beginning or
* the end of a frame.
*
* @param context
* The rdpContext associated with the current RDP session.
*
* @param surface_frame_marker
* The received frame marker.
*
* @return
* TRUE if successful, FALSE otherwise.
*/
BOOL guac_rdp_gdi_surface_frame_marker(rdpContext* context, const SURFACE_FRAME_MARKER* surface_frame_marker);
/**
* Handler called when a paint operation is beginning. This function is
* expected to be called by the FreeRDP GDI implementation of RemoteFX when a
* new frame has started.
*
* @param context
* The rdpContext associated with the current RDP session.
*
* @return
* TRUE if successful, FALSE otherwise.
*/
BOOL guac_rdp_gdi_begin_paint(rdpContext* context);
/**
* Handler called when a paint operation is complete. This function is
* expected to be called by the FreeRDP GDI implementation of RemoteFX when a
* new frame has been completed.
*
* @param context
* The rdpContext associated with the current RDP session.

View File

@ -96,11 +96,11 @@ BOOL guac_rdp_glyph_new(rdpContext* context, const rdpGlyph* glyph);
* The height of the glyph being drawn.
*
* @param sx
* The X coordinare of the upper-left corner of the glyph within the source
* The X coordinate of the upper-left corner of the glyph within the source
* cache surface containing the glyph.
*
* @param sy
* The Y coordinare of the upper-left corner of the glyph within the source
* The Y coordinate of the upper-left corner of the glyph within the source
* cache surface containing the glyph.
*
* @param redundant

View File

@ -21,7 +21,6 @@
#include "channels/rdpei.h"
#include "common/cursor.h"
#include "common/display.h"
#include "common/recording.h"
#include "input.h"
#include "keyboard.h"
#include "rdp.h"
@ -30,6 +29,7 @@
#include <freerdp/freerdp.h>
#include <freerdp/input.h>
#include <guacamole/client.h>
#include <guacamole/recording.h>
#include <guacamole/user.h>
#include <stdlib.h>
@ -51,7 +51,7 @@ int guac_rdp_user_mouse_handler(guac_user* user, int x, int y, int mask) {
/* Report mouse position within recording */
if (rdp_client->recording != NULL)
guac_common_recording_report_mouse(rdp_client->recording, x, y, mask);
guac_recording_report_mouse(rdp_client->recording, x, y, mask);
/* If button mask unchanged, just send move event */
if (mask == rdp_client->mouse_button_mask) {
@ -145,7 +145,7 @@ int guac_rdp_user_touch_handler(guac_user* user, int id, int x, int y,
/* Report touch event within recording */
if (rdp_client->recording != NULL)
guac_common_recording_report_touch(rdp_client->recording, id, x, y,
guac_recording_report_touch(rdp_client->recording, id, x, y,
x_radius, y_radius, angle, force);
/* Forward touch event along RDPEI channel */
@ -167,7 +167,7 @@ int guac_rdp_user_key_handler(guac_user* user, int keysym, int pressed) {
/* Report key state within recording */
if (rdp_client->recording != NULL)
guac_common_recording_report_key(rdp_client->recording,
guac_recording_report_key(rdp_client->recording,
keysym, pressed);
/* Skip if keyboard not yet ready */

View File

@ -140,7 +140,7 @@ static void guac_rdp_send_unicode_event(guac_rdp_client* rdp_client,
}
/**
* Immediately sends an RDP synchonize event having the given flags. An RDP
* Immediately sends an RDP synchronize event having the given flags. An RDP
* synchronize event sets the state of remote lock keys absolutely, where a
* lock key will be active only if its corresponding flag is set in the event.
*

View File

@ -0,0 +1,79 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
parent "base"
name "cs-cz-qwertz"
freerdp "KBD_CZECH"
#
# Basic keys
#
map -caps -altgr -shift 0x29 0x02..0x0D ~ ";+ěščřžýáíé=´"
map -caps -altgr -shift 0x10..0x1B ~ "qwertzuıopú)"
map -caps -altgr -shift 0x1E..0x28 0x2B ~ "asdfghjklů§¨"
map -caps -altgr -shift 0x2C..0x35 ~ "yxcvbnm,.-"
map -caps -altgr +shift 0x29 0x02..0x0D ~ "°1234567890%ˇ"
map -caps -altgr +shift 0x10..0x1B ~ "QWERTZUIOP/("
map -caps -altgr +shift 0x1E..0x28 0x2B ~ "ASDFGHJKL"!'"
map -caps -altgr +shift 0x2C..0x35 ~ "YXCVBNM?:_"
map +caps -altgr -shift 0x29 0x02..0x0D ~ ";+ĚŠČŘŽÝÁÍÉ=´"
map +caps -altgr -shift 0x10..0x1B ~ "QWERTZUIOPÚ)"
map +caps -altgr -shift 0x1E..0x28 0x2B ~ "ASDFGHJKL٧¨"
map +caps -altgr -shift 0x2C..0x35 ~ "YXCVBNM,.-"
map +caps -altgr +shift 0x29 0x02..0x0D ~ "°1234567890%ˇ"
map +caps -altgr +shift 0x10..0x1B ~ "qwertzuiop/("
map +caps -altgr +shift 0x1E..0x28 0x2B ~ "asdfghjkl"!'"
map +caps -altgr +shift 0x2C..0x35 ~ "yxcvbnm?:_"
#
# Keys requiring AltGr
#
map +altgr -shift 0x02 ~ "~"
map +altgr -shift 0x10 ~ "\"
map +altgr -shift 0x11 ~ "|"
map +altgr -shift 0x12 ~ "€"
map +altgr -shift 0x1A ~ "÷"
map +altgr -shift 0x1B ~ "×"
map +altgr -shift 0x1F ~ "đ"
map +altgr -shift 0x20 ~ "Đ"
map +altgr -shift 0x21 ~ "["
map +altgr -shift 0x22 ~ "]"
map +altgr -shift 0x25 ~ "ł"
map +altgr -shift 0x26 ~ "Ł"
map +altgr -shift 0x27 ~ "$"
map +altgr -shift 0x28 ~ "ß"
map +altgr -shift 0x2B ~ "¤"
map +altgr -shift 0x2D ~ "#"
map +altgr -shift 0x2E ~ "&"
map +altgr -shift 0x2F ~ "@"
map +altgr -shift 0x30 ~ "{"
map +altgr -shift 0x31 ~ "}"
map +altgr -shift 0x33 ~ "<"
map +altgr -shift 0x34 ~ ">"
map +altgr -shift 0x35 ~ "*"
# END

View File

@ -0,0 +1,55 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
parent "base"
name "fr-ca-qwerty"
freerdp "KBD_CANADIAN_FRENCH"
#
# Basic keys
#
map -altgr -shift 0x29 0x02..0x0D ~ "#1234567890-="
map -altgr -shift 0x10..0x1B 0x2B ~ "qwertyuiop^¸<"
map -altgr -shift 0x1E..0x28 ~ "asdfghjkl;`"
map -altgr -shift 0x2C..0x35 ~ "zxcvbnm,.é"
map -altgr +shift 0x29 0x02..0x0D ~ "|!"/$%?&*()_+"
map -altgr +shift 0x10..0x1B 0x2B ~ "QWERTYUIOP^¨>"
map -altgr +shift 0x1E..0x28 ~ "ASDFGHJKL:`"
map -altgr +shift 0x2C..0x35 ~ "ZXCVBNM'.É"
#
# Keys requiring AltGr
#
map +altgr -shift 0x29 0x02..0x0D ~ "\±@£¢¤¬¦²³¼½¾"
map +altgr -shift 0x18..0x1B 0x2B ~ "§¶[]}"
map +altgr -shift 0x27..0x28 ~ "~{"
map +altgr -shift 0x32..0x33 0x35 ~ "µ¯´"
#
# Combined accents
#
map -altgr -shift 0x1A ~ 0x0302 # COMBINING CIRCUMFLEX ACCENT
map -altgr -shift 0x1B ~ 0x0327 # COMBINING CEDILLA
map -altgr +shift 0x1B ~ 0x0308 # COMBINING DIAERESIS
map -altgr -shift 0x28 ~ 0x0300 # COMBINING GRAVE ACCENT
map +altgr -shift 0x35 ~ 0x0301 # COMBINING ACUTE ACCENT

View File

@ -0,0 +1,64 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
parent "base"
name "pl-pl-qwerty"
freerdp "KBD_POLISH_PROGRAMMERS"
map -caps -shift 0x29 0x02..0x0D ~ "`1234567890-="
map -caps -shift 0x10..0x1B 0x2B ~ "qwertyuiop[]\"
map -caps -shift 0x1E..0x28 ~ "asdfghjkl;'"
map -caps -shift 0x2C..0x35 ~ "zxcvbnm,./"
map -caps +shift 0x29 0x02..0x0D ~ "~!@#$%^&*()_+"
map -caps +shift 0x10..0x1B 0x2B ~ "QWERTYUIOP{}|"
map -caps +shift 0x1E..0x28 ~ "ASDFGHJKL:""
map -caps +shift 0x2C..0x35 ~ "ZXCVBNM<>?"
map +caps -shift 0x29 0x02..0x0D ~ "`1234567890-="
map +caps -shift 0x10..0x1B 0x2B ~ "QWERTYUIOP[]\"
map +caps -shift 0x1E..0x28 ~ "ASDFGHJKL;'"
map +caps -shift 0x2C..0x35 ~ "ZXCVBNM,./"
map +caps +shift 0x29 0x02..0x0D ~ "~!@#$%^&*()_+"
map +caps +shift 0x10..0x1B 0x2B ~ "qwertyuiop{}|"
map +caps +shift 0x1E..0x28 ~ "asdfghjkl:""
map +caps +shift 0x2C..0x35 ~ "zxcvbnm<>?"
#
# Keys requiring AltGr
#
map +altgr -shift 0x16 ~ "€"
map -caps -shift +altgr 0x12 0x18 ~ "ęó"
map -caps -shift +altgr 0x1E 0x1F 0x26 ~ "ąśł"
map -caps -shift +altgr 0x2C 0x2D 0x2E 0x31 ~ "żźćń"
map -caps +shift +altgr 0x12 0x18 ~ "ĘÓ"
map -caps +shift +altgr 0x1E 0x1F 0x26 ~ "ĄŚŁ"
map -caps +shift +altgr 0x2C 0x2D 0x2E 0x31 ~ "ŻŹĆŃ"
map +caps -shift +altgr 0x12 0x18 ~ "ĘÓ"
map +caps -shift +altgr 0x1E 0x1F 0x26 ~ "ĄŚŁ"
map +caps -shift +altgr 0x2C 0x2D 0x2E 0x31 ~ "ŻŹĆŃ"
map +caps +shift +altgr 0x12 0x18 ~ "ęó"
map +caps +shift +altgr 0x1E 0x1F 0x26 ~ "ąśł"
map +caps +shift +altgr 0x2C 0x2D 0x2E 0x31 ~ "żźćń"

View File

@ -251,7 +251,7 @@ void guac_rdp_ai_process_version(guac_client* client,
/* Verify we have at least 4 bytes available (UINT32) */
if (Stream_GetRemainingLength(stream) < 4) {
guac_client_log(client, GUAC_LOG_WARNING, "Audio input Versoin PDU "
guac_client_log(client, GUAC_LOG_WARNING, "Audio input Version PDU "
"does not contain the expected number of bytes. Audio input "
"redirection may not work as expected.");
return;

View File

@ -279,7 +279,7 @@ static UINT guac_rdp_ai_terminated(IWTSPlugin* plugin) {
/**
* Entry point for AUDIO_INPUT dynamic virtual channel.
*/
int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) {
UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) {
/* Pull guac_client from arguments */
ADDIN_ARGV* args = pEntryPoints->GetPluginData(pEntryPoints);

View File

@ -21,6 +21,7 @@
#include "common/cursor.h"
#include "common/display.h"
#include "common/surface.h"
#include "gdi.h"
#include "pointer.h"
#include "rdp.h"
@ -78,11 +79,22 @@ BOOL guac_rdp_pointer_set(rdpContext* context, const rdpPointer* pointer) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
/* Add explicit frame boundaries around cursor set operation if not already
* in a frame (the RDP protocol does not send nor expect frame boundaries
* for cursor changes, but Guacamole does expect this) */
int in_frame = rdp_client->in_frame;
if (rdp_client->frames_supported && !in_frame)
guac_rdp_gdi_mark_frame(context, 1);
/* Set cursor */
guac_common_cursor_set_surface(rdp_client->display->cursor,
pointer->xPos, pointer->yPos,
((guac_rdp_pointer*) pointer)->layer->surface);
if (rdp_client->frames_supported && !in_frame)
guac_rdp_gdi_mark_frame(context, 0);
return TRUE;
}
@ -106,9 +118,20 @@ BOOL guac_rdp_pointer_set_null(rdpContext* context) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
/* Add explicit frame boundaries around cursor set operation if not already
* in a frame (the RDP protocol does not send nor expect frame boundaries
* for cursor changes, but Guacamole does expect this) */
int in_frame = rdp_client->in_frame;
if (rdp_client->frames_supported && !in_frame)
guac_rdp_gdi_mark_frame(context, 1);
/* Set cursor to empty/blank graphic */
guac_common_cursor_set_blank(rdp_client->display->cursor);
if (rdp_client->frames_supported && !in_frame)
guac_rdp_gdi_mark_frame(context, 0);
return TRUE;
}
@ -118,9 +141,20 @@ BOOL guac_rdp_pointer_set_default(rdpContext* context) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
/* Add explicit frame boundaries around cursor set operation if not already
* in a frame (the RDP protocol does not send nor expect frame boundaries
* for cursor changes, but Guacamole does expect this) */
int in_frame = rdp_client->in_frame;
if (rdp_client->frames_supported && !in_frame)
guac_rdp_gdi_mark_frame(context, 1);
/* Set cursor to embedded pointer */
guac_common_cursor_set_pointer(rdp_client->display->cursor);
if (rdp_client->frames_supported && !in_frame)
guac_rdp_gdi_mark_frame(context, 0);
return TRUE;
}

View File

@ -18,6 +18,7 @@
*/
#include "print-job.h"
#include "rdp.h"
#include <guacamole/client.h>
#include <guacamole/protocol.h>
@ -27,6 +28,7 @@
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@ -603,6 +605,9 @@ static void guac_rdp_print_job_read_filename(guac_rdp_print_job* job,
int guac_rdp_print_job_write(guac_rdp_print_job* job,
void* buffer, int length) {
guac_client* client = job->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
/* Create print job, if not yet created */
if (job->bytes_received == 0) {
@ -618,19 +623,40 @@ int guac_rdp_print_job_write(guac_rdp_print_job* job,
/* Update counter of bytes received */
job->bytes_received += length;
/* Write data to filter process */
return write(job->input_fd, buffer, length);
/* Write data to filter process, unblocking any threads waiting on the
* generic RDP message lock as this may be a lengthy operation that depends
* on other threads sending outstanding messages (resulting in deadlock if
* those messages are blocked) */
int unlock_status = pthread_mutex_unlock(&(rdp_client->message_lock));
int write_status = write(job->input_fd, buffer, length);
/* Restore RDP message lock state */
if (!unlock_status)
pthread_mutex_lock(&(rdp_client->message_lock));
return write_status;
}
void guac_rdp_print_job_free(guac_rdp_print_job* job) {
guac_client* client = job->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
/* No more input will be provided */
close(job->input_fd);
/* Wait for job to terminate */
/* Wait for job to terminate, unblocking any threads waiting on the generic
* RDP message lock as this may be a lengthy operation that depends on
* other threads sending outstanding messages (resulting in deadlock if
* those messages are blocked) */
int unlock_status = pthread_mutex_unlock(&(rdp_client->message_lock));
pthread_join(job->output_thread, NULL);
/* Restore RDP message lock state */
if (!unlock_status)
pthread_mutex_lock(&(rdp_client->message_lock));
/* Destroy lock */
pthread_mutex_destroy(&(job->state_lock));
@ -641,6 +667,9 @@ void guac_rdp_print_job_free(guac_rdp_print_job* job) {
void guac_rdp_print_job_kill(guac_rdp_print_job* job) {
/* Forcibly kill filter process, if running */
kill(job->filter_pid, SIGKILL);
/* Stop all handling of I/O */
close(job->input_fd);
close(job->output_fd);

View File

@ -28,12 +28,12 @@
#include "channels/rail.h"
#include "channels/rdpdr/rdpdr.h"
#include "channels/rdpei.h"
#include "channels/rdpgfx.h"
#include "channels/rdpsnd/rdpsnd.h"
#include "client.h"
#include "color.h"
#include "common/cursor.h"
#include "common/display.h"
#include "common/recording.h"
#include "config.h"
#include "error.h"
#include "fs.h"
@ -71,6 +71,7 @@
#include <guacamole/audio.h>
#include <guacamole/client.h>
#include <guacamole/protocol.h>
#include <guacamole/recording.h>
#include <guacamole/socket.h>
#include <guacamole/string.h>
#include <guacamole/timestamp.h>
@ -137,15 +138,6 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
}
/* Load plugin providing Dynamic Virtual Channel support, if required */
if (instance->settings->SupportDynamicChannels &&
guac_freerdp_channels_load_plugin(context, "drdynvc",
instance->settings)) {
guac_client_log(client, GUAC_LOG_WARNING,
"Failed to load drdynvc plugin. Display update and audio "
"input support will be disabled.");
}
/* Init FreeRDP internal GDI implementation */
if (!gdi_init(instance, guac_rdp_get_native_pixel_format(FALSE)))
return FALSE;
@ -187,9 +179,13 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
/* Set up GDI */
instance->update->DesktopResize = guac_rdp_gdi_desktop_resize;
instance->update->BeginPaint = guac_rdp_gdi_begin_paint;
instance->update->EndPaint = guac_rdp_gdi_end_paint;
instance->update->SetBounds = guac_rdp_gdi_set_bounds;
instance->update->SurfaceFrameMarker = guac_rdp_gdi_surface_frame_marker;
instance->update->altsec->FrameMarker = guac_rdp_gdi_frame_marker;
rdpPrimaryUpdate* primary = instance->update->primary;
primary->DstBlt = guac_rdp_gdi_dstblt;
primary->PatBlt = guac_rdp_gdi_patblt;
@ -204,6 +200,19 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
offscreen_cache_register_callbacks(instance->update);
palette_cache_register_callbacks(instance->update);
/* Load "rdpgfx" plugin for Graphics Pipeline Extension */
if (settings->enable_gfx)
guac_rdp_rdpgfx_load_plugin(context);
/* Load plugin providing Dynamic Virtual Channel support, if required */
if (instance->settings->SupportDynamicChannels &&
guac_freerdp_channels_load_plugin(context, "drdynvc",
instance->settings)) {
guac_client_log(client, GUAC_LOG_WARNING,
"Failed to load drdynvc plugin. Display update and audio "
"input support will be disabled.");
}
return TRUE;
}
@ -544,7 +553,6 @@ static int guac_rdp_handle_connection(guac_client* client) {
if (wait_result > 0) {
int processing_lag = guac_client_get_processing_lag(client);
guac_timestamp frame_start = guac_timestamp_current();
/* Read server messages until frame is built */
do {
@ -564,7 +572,15 @@ static int guac_rdp_handle_connection(guac_client* client) {
break;
}
/* Continue handling inbound data if we are in the middle of an RDP frame */
if (rdp_client->in_frame) {
wait_result = rdp_guac_client_wait_for_messages(client, GUAC_RDP_FRAME_START_TIMEOUT);
if (wait_result >= 0)
continue;
}
/* Calculate time remaining in frame */
guac_timestamp frame_start = client->last_sent_timestamp;
frame_end = guac_timestamp_current();
frame_remaining = frame_start + GUAC_RDP_FRAME_DURATION
- frame_end;
@ -587,12 +603,6 @@ static int guac_rdp_handle_connection(guac_client* client) {
} while (wait_result > 0);
/* Record end of frame, excluding server-side rendering time (we
* assume server-side rendering time will be consistent between any
* two subsequent frames, and that this time should thus be
* excluded from the required wait period of the next frame). */
last_frame_end = frame_start;
}
/* Test whether the RDP server is closing the connection */
@ -607,11 +617,13 @@ static int guac_rdp_handle_connection(guac_client* client) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_UNAVAILABLE,
"Connection closed.");
/* Flush frame only if successful */
else {
/* Flush frame only if successful and an RDP frame is not known to be
* in progress */
else if (!rdp_client->frames_supported || rdp_client->frames_received) {
guac_common_display_flush(rdp_client->display);
guac_client_end_frame(client);
guac_client_end_multiple_frames(client, rdp_client->frames_received);
guac_socket_flush(client->socket);
rdp_client->frames_received = 0;
}
}
@ -806,7 +818,7 @@ void* guac_rdp_client_thread(void* data) {
/* Set up screen recording, if requested */
if (settings->recording_path != NULL) {
rdp_client->recording = guac_common_recording_create(client,
rdp_client->recording = guac_recording_create(client,
settings->recording_path,
settings->recording_name,
settings->create_recording_path,

View File

@ -27,7 +27,6 @@
#include "common/clipboard.h"
#include "common/display.h"
#include "common/list.h"
#include "common/recording.h"
#include "common/surface.h"
#include "config.h"
#include "fs.h"
@ -45,6 +44,7 @@
#include <freerdp/freerdp.h>
#include <guacamole/audio.h>
#include <guacamole/client.h>
#include <guacamole/recording.h>
#include <winpr/wtypes.h>
#include <pthread.h>
@ -91,6 +91,24 @@ typedef struct guac_rdp_client {
*/
guac_common_surface* current_surface;
/**
* Whether the RDP server supports defining explicit frame boundaries.
*/
int frames_supported;
/**
* Whether the RDP server has reported that a new frame is in progress, and
* we are now receiving updates relevant to that frame.
*/
int in_frame;
/**
* The number of distinct frames received from the RDP server since last
* flush, if the RDP server supports reporting frame boundaries. If the RDP
* server does not support tracking frames, this will be zero.
*/
int frames_received;
/**
* The current state of the keyboard with respect to the RDP session.
*/
@ -142,7 +160,7 @@ typedef struct guac_rdp_client {
* The in-progress session recording, or NULL if no recording is in
* progress.
*/
guac_common_recording* recording;
guac_recording* recording;
/**
* Display size update module.

View File

@ -28,6 +28,7 @@
#include <freerdp/settings.h>
#include <freerdp/freerdp.h>
#include <guacamole/client.h>
#include <guacamole/fips.h>
#include <guacamole/string.h>
#include <guacamole/user.h>
#include <guacamole/wol-constants.h>
@ -39,6 +40,16 @@
#include <stdlib.h>
#include <string.h>
/**
* A warning to log when NLA mode is selected while FIPS mode is active on the
* guacd server.
*/
const char fips_nla_mode_warning[] = (
"NLA security mode was selected, but is known to be currently incompatible "
"with FIPS mode (see FreeRDP/FreeRDP#3412). Security negotiation with the "
"RDP server may fail unless TLS security mode is selected instead."
);
/* Client plugin arguments */
const char* GUAC_RDP_CLIENT_ARGS[] = {
"hostname",
@ -80,6 +91,7 @@ const char* GUAC_RDP_CLIENT_ARGS[] = {
"disable-bitmap-caching",
"disable-offscreen-caching",
"disable-glyph-caching",
"disable-gfx",
"preconnection-id",
"preconnection-blob",
"timezone",
@ -360,7 +372,7 @@ enum RDP_ARGS_IDX {
IDX_DISABLE_BITMAP_CACHING,
/**
* "true" if the offscreen caching should be disabled, false if offscren
* "true" if the offscreen caching should be disabled, false if offscreen
* caching should remain enabled.
*/
IDX_DISABLE_OFFSCREEN_CACHING,
@ -371,6 +383,13 @@ enum RDP_ARGS_IDX {
*/
IDX_DISABLE_GLYPH_CACHING,
/**
* "true" if the RDP Graphics Pipeline Extension should not be used, and
* traditional RDP graphics should be used instead, "false" or blank if the
* Graphics Pipeline Extension should be used if available.
*/
IDX_DISABLE_GFX,
/**
* The preconnection ID to send within the preconnection PDU when
* initiating an RDP connection, if any.
@ -698,12 +717,27 @@ guac_rdp_settings* guac_rdp_parse_args(guac_user* user,
if (strcmp(argv[IDX_SECURITY], "nla") == 0) {
guac_user_log(user, GUAC_LOG_INFO, "Security mode: NLA");
settings->security_mode = GUAC_SECURITY_NLA;
/*
* NLA is known not to work with FIPS; allow the mode selection but
* warn that it will not work.
*/
if (guac_fips_enabled())
guac_user_log(user, GUAC_LOG_WARNING, fips_nla_mode_warning);
}
/* Extended NLA security */
else if (strcmp(argv[IDX_SECURITY], "nla-ext") == 0) {
guac_user_log(user, GUAC_LOG_INFO, "Security mode: Extended NLA");
settings->security_mode = GUAC_SECURITY_EXTENDED_NLA;
/*
* NLA is known not to work with FIPS; allow the mode selection but
* warn that it will not work.
*/
if (guac_fips_enabled())
guac_user_log(user, GUAC_LOG_WARNING, fips_nla_mode_warning);
}
/* TLS security */
@ -908,11 +942,6 @@ guac_rdp_settings* guac_rdp_parse_args(guac_user* user,
GUAC_RDP_CLIENT_ARGS[IDX_DISABLE_GLYPH_CACHING]);
}
/* Session color depth */
settings->color_depth =
guac_user_parse_args_int(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_COLOR_DEPTH, RDP_DEFAULT_DEPTH);
/* Preconnection ID */
settings->preconnection_id = -1;
if (argv[IDX_PRECONNECTION_ID][0] != '\0') {
@ -1129,6 +1158,16 @@ guac_rdp_settings* guac_rdp_parse_args(guac_user* user,
settings->resize_method = GUAC_RESIZE_NONE;
}
/* RDP Graphics Pipeline enable/disable */
settings->enable_gfx =
!guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_DISABLE_GFX, 0);
/* Session color depth */
settings->color_depth =
guac_user_parse_args_int(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_COLOR_DEPTH, settings->enable_gfx ? RDP_GFX_REQUIRED_DEPTH : RDP_DEFAULT_DEPTH);
/* Multi-touch input enable/disable */
settings->enable_touch =
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
@ -1397,6 +1436,29 @@ void guac_rdp_push_settings(guac_client* client,
/* Explicitly set flag value */
rdp_settings->PerformanceFlags = guac_rdp_get_performance_flags(guac_settings);
/* Always request frame markers */
rdp_settings->FrameMarkerCommandEnabled = TRUE;
rdp_settings->SurfaceFrameMarkerEnabled = TRUE;
/* Enable RemoteFX / Graphics Pipeline */
if (guac_settings->enable_gfx) {
rdp_settings->SupportGraphicsPipeline = TRUE;
rdp_settings->RemoteFxCodec = TRUE;
if (rdp_settings->ColorDepth != RDP_GFX_REQUIRED_DEPTH) {
guac_client_log(client, GUAC_LOG_WARNING, "Ignoring requested "
"color depth of %i bpp, as the RDP Graphics Pipeline "
"requires %i bpp.", rdp_settings->ColorDepth, RDP_GFX_REQUIRED_DEPTH);
}
/* Required for RemoteFX / Graphics Pipeline */
rdp_settings->FastPathOutput = TRUE;
rdp_settings->ColorDepth = RDP_GFX_REQUIRED_DEPTH;
rdp_settings->SoftwareGdi = TRUE;
}
/* Set individual flags - some FreeRDP versions overwrite the above */
rdp_settings->AllowFontSmoothing = guac_settings->font_smoothing_enabled;
rdp_settings->DisableWallpaper = !guac_settings->wallpaper_enabled;
@ -1493,7 +1555,21 @@ void guac_rdp_push_settings(guac_client* client,
case GUAC_SECURITY_ANY:
rdp_settings->RdpSecurity = TRUE;
rdp_settings->TlsSecurity = TRUE;
rdp_settings->NlaSecurity = guac_settings->username && guac_settings->password;
/* Explicitly disable NLA if FIPS mode is enabled - it won't work */
if (guac_fips_enabled()) {
guac_client_log(client, GUAC_LOG_INFO,
"FIPS mode is enabled. Excluding NLA security mode from security negotiation "
"(see: https://github.com/FreeRDP/FreeRDP/issues/3412).");
rdp_settings->NlaSecurity = FALSE;
}
/* NLA mode is allowed if FIPS is not enabled */
else
rdp_settings->NlaSecurity = TRUE;
rdp_settings->ExtSecurity = FALSE;
break;

View File

@ -58,6 +58,11 @@
*/
#define RDP_DEFAULT_DEPTH 16
/**
* The color depth required by the RDPGFX channel, in bits.
*/
#define RDP_GFX_REQUIRED_DEPTH 32
/**
* The filename to use for the screen recording, if not specified.
*/
@ -552,6 +557,11 @@ typedef struct guac_rdp_settings {
*/
int enable_audio_input;
/**
* Whether the RDP Graphics Pipeline Extension is enabled.
*/
int enable_gfx;
/**
* Whether multi-touch support is enabled.
*/

View File

@ -27,6 +27,7 @@
#include <guacamole/user.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -54,10 +55,12 @@ int guac_ssh_argv_callback(guac_user* user, const char* mimetype,
}
/* Update SSH pty size if connected */
int term_width = guac_terminal_get_columns(terminal);
int term_height = guac_terminal_get_rows(terminal);
if (ssh_client->term_channel != NULL) {
pthread_mutex_lock(&(ssh_client->term_channel_lock));
libssh2_channel_request_pty_size(ssh_client->term_channel,
terminal->term_width, terminal->term_height);
term_width, term_height);
pthread_mutex_unlock(&(ssh_client->term_channel_lock));
}
@ -72,15 +75,17 @@ void* guac_ssh_send_current_argv(guac_user* user, void* data) {
/* Send current color scheme */
guac_user_stream_argv(user, user->socket, "text/plain",
GUAC_SSH_ARGV_COLOR_SCHEME, terminal->color_scheme);
GUAC_SSH_ARGV_COLOR_SCHEME,
guac_terminal_get_color_scheme(terminal));
/* Send current font name */
guac_user_stream_argv(user, user->socket, "text/plain",
GUAC_SSH_ARGV_FONT_NAME, terminal->font_name);
GUAC_SSH_ARGV_FONT_NAME,
guac_terminal_get_font_name(terminal));
/* Send current font size */
char font_size[64];
sprintf(font_size, "%i", terminal->font_size);
sprintf(font_size, "%i", guac_terminal_get_font_size(terminal));
guac_user_stream_argv(user, user->socket, "text/plain",
GUAC_SSH_ARGV_FONT_SIZE, font_size);

View File

@ -21,8 +21,6 @@
#include "argv.h"
#include "client.h"
#include "common/clipboard.h"
#include "common/recording.h"
#include "common-ssh/sftp.h"
#include "ssh.h"
#include "terminal/terminal.h"
@ -35,6 +33,7 @@
#include <guacamole/argv.h>
#include <guacamole/client.h>
#include <guacamole/recording.h>
int guac_client_init(guac_client* client) {
@ -45,9 +44,6 @@ int guac_client_init(guac_client* client) {
guac_ssh_client* ssh_client = calloc(1, sizeof(guac_ssh_client));
client->data = ssh_client;
/* Init clipboard */
ssh_client->clipboard = guac_common_clipboard_alloc(GUAC_SSH_CLIPBOARD_MAX_LENGTH);
/* Set handlers */
client->join_handler = guac_ssh_user_join_handler;
client->free_handler = guac_ssh_client_free_handler;
@ -103,7 +99,7 @@ int guac_ssh_client_free_handler(guac_client* client) {
/* Clean up recording, if in progress */
if (ssh_client->recording != NULL)
guac_common_recording_free(ssh_client->recording);
guac_recording_free(ssh_client->recording);
/* Free interactive SSH session */
if (ssh_client->session != NULL)
@ -118,7 +114,6 @@ int guac_ssh_client_free_handler(guac_client* client) {
guac_ssh_settings_free(ssh_client->settings);
/* Free client structure */
guac_common_clipboard_free(ssh_client->clipboard);
free(ssh_client);
guac_common_ssh_uninit();

View File

@ -22,11 +22,6 @@
#include <guacamole/client.h>
/**
* The maximum number of bytes to allow within the clipboard.
*/
#define GUAC_SSH_CLIPBOARD_MAX_LENGTH 262144
/**
* Handler which is invoked when the SSH client needs to be disconnected (if
* connected) and freed. This can happen if initialization fails, or all users

View File

@ -19,7 +19,6 @@
#include "config.h"
#include "clipboard.h"
#include "common/clipboard.h"
#include "ssh.h"
#include "terminal/terminal.h"
@ -33,7 +32,7 @@ int guac_ssh_clipboard_handler(guac_user* user, guac_stream* stream,
/* Clear clipboard and prepare for new data */
guac_client* client = user->client;
guac_ssh_client* ssh_client = (guac_ssh_client*) client->data;
guac_common_clipboard_reset(ssh_client->clipboard, mimetype);
guac_terminal_clipboard_reset(ssh_client->term, mimetype);
/* Set handlers for clipboard stream */
stream->blob_handler = guac_ssh_clipboard_blob_handler;
@ -48,7 +47,7 @@ int guac_ssh_clipboard_blob_handler(guac_user* user, guac_stream* stream,
/* Append new data */
guac_client* client = user->client;
guac_ssh_client* ssh_client = (guac_ssh_client*) client->data;
guac_common_clipboard_append(ssh_client->clipboard, data, length);
guac_terminal_clipboard_append(ssh_client->term, data, length);
return 0;
}

View File

@ -21,11 +21,11 @@
#include "common/cursor.h"
#include "common/display.h"
#include "common/recording.h"
#include "ssh.h"
#include "terminal/terminal.h"
#include <guacamole/client.h>
#include <guacamole/recording.h>
#include <guacamole/user.h>
#include <libssh2.h>
@ -43,7 +43,7 @@ int guac_ssh_user_mouse_handler(guac_user* user, int x, int y, int mask) {
/* Report mouse position within recording */
if (ssh_client->recording != NULL)
guac_common_recording_report_mouse(ssh_client->recording, x, y, mask);
guac_recording_report_mouse(ssh_client->recording, x, y, mask);
/* Send mouse event */
guac_terminal_send_mouse(term, user, x, y, mask);
@ -58,7 +58,7 @@ int guac_ssh_user_key_handler(guac_user* user, int keysym, int pressed) {
/* Report key state within recording */
if (ssh_client->recording != NULL)
guac_common_recording_report_key(ssh_client->recording,
guac_recording_report_key(ssh_client->recording,
keysym, pressed);
/* Skip if terminal not yet ready */
@ -88,7 +88,8 @@ int guac_ssh_user_size_handler(guac_user* user, int width, int height) {
if (ssh_client->term_channel != NULL) {
pthread_mutex_lock(&(ssh_client->term_channel_lock));
libssh2_channel_request_pty_size(ssh_client->term_channel,
terminal->term_width, terminal->term_height);
guac_terminal_get_columns(terminal),
guac_terminal_get_rows(terminal));
pthread_mutex_unlock(&(ssh_client->term_channel_lock));
}

View File

@ -23,6 +23,7 @@
#include "client.h"
#include "common/defaults.h"
#include "settings.h"
#include "terminal/terminal.h"
#include <guacamole/user.h>
#include <guacamole/wol-constants.h>
@ -248,8 +249,8 @@ enum SSH_ARGS_IDX {
/**
* The ASCII code, as an integer, to send for the backspace key, as configured
* by the SSH connection from the client. By default this will be 127,
* the ASCII DELETE code.
* by the SSH connection from the client. By default this will be
* GUAC_TERMINAL_DEFAULT_BACKSPACE.
*/
IDX_BACKSPACE,
@ -375,22 +376,22 @@ guac_ssh_settings* guac_ssh_parse_args(guac_user* user,
/* Read maximum scrollback size */
settings->max_scrollback =
guac_user_parse_args_int(user, GUAC_SSH_CLIENT_ARGS, argv,
IDX_SCROLLBACK, GUAC_SSH_DEFAULT_MAX_SCROLLBACK);
IDX_SCROLLBACK, GUAC_TERMINAL_DEFAULT_MAX_SCROLLBACK);
/* Read font name */
settings->font_name =
guac_user_parse_args_string(user, GUAC_SSH_CLIENT_ARGS, argv,
IDX_FONT_NAME, GUAC_SSH_DEFAULT_FONT_NAME);
IDX_FONT_NAME, GUAC_TERMINAL_DEFAULT_FONT_NAME);
/* Read font size */
settings->font_size =
guac_user_parse_args_int(user, GUAC_SSH_CLIENT_ARGS, argv,
IDX_FONT_SIZE, GUAC_SSH_DEFAULT_FONT_SIZE);
IDX_FONT_SIZE, GUAC_TERMINAL_DEFAULT_FONT_SIZE);
/* Copy requested color scheme */
settings->color_scheme =
guac_user_parse_args_string(user, GUAC_SSH_CLIENT_ARGS, argv,
IDX_COLOR_SCHEME, "");
IDX_COLOR_SCHEME, GUAC_TERMINAL_DEFAULT_COLOR_SCHEME);
/* Pull width/height/resolution directly from user */
settings->width = user->info.optimal_width;
@ -491,7 +492,7 @@ guac_ssh_settings* guac_ssh_parse_args(guac_user* user,
/* Parse backspace key setting */
settings->backspace =
guac_user_parse_args_int(user, GUAC_SSH_CLIENT_ARGS, argv,
IDX_BACKSPACE, 127);
IDX_BACKSPACE, GUAC_TERMINAL_DEFAULT_BACKSPACE);
/* Read terminal emulator type. */
settings->terminal_type =

View File

@ -26,17 +26,6 @@
#include <stdbool.h>
/**
* The name of the font to use for the terminal if no name is specified.
*/
#define GUAC_SSH_DEFAULT_FONT_NAME "monospace"
/**
* The size of the font to use for the terminal if no font size is specified,
* in points.
*/
#define GUAC_SSH_DEFAULT_FONT_SIZE 12
/**
* The port to connect to when initiating any SSH connection, if no other port
* is specified.
@ -58,11 +47,6 @@
*/
#define GUAC_SSH_DEFAULT_POLL_TIMEOUT 1000
/**
* The default maximum scrollback size in rows.
*/
#define GUAC_SSH_DEFAULT_MAX_SCROLLBACK 1000
/**
* Settings for the SSH connection. The values for this structure are parsed
* from the arguments given during the Guacamole protocol handshake using the

View File

@ -20,7 +20,6 @@
#include "config.h"
#include "argv.h"
#include "common/recording.h"
#include "common-ssh/sftp.h"
#include "common-ssh/ssh.h"
#include "settings.h"
@ -36,6 +35,9 @@
#include <libssh2.h>
#include <libssh2_sftp.h>
#include <guacamole/client.h>
#include <guacamole/recording.h>
#include <guacamole/socket.h>
#include <guacamole/timestamp.h>
#include <guacamole/wol.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
@ -98,7 +100,7 @@ static guac_common_ssh_user* guac_ssh_get_user(guac_client* client) {
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)");
@ -148,23 +150,23 @@ static guac_common_ssh_user* guac_ssh_get_user(guac_client* client) {
* A function used to generate a terminal prompt to gather additional
* credentials from the guac_client during a connection, and using
* the specified string to generate the prompt for the user.
*
*
* @param client
* The guac_client object associated with the current connection
* where additional credentials are required.
*
*
* @param cred_name
* The prompt text to display to the screen when prompting for the
* additional credentials.
*
* @return
*
* @return
* The string of credentials gathered from the user.
*/
static char* guac_ssh_get_credential(guac_client *client, char* cred_name) {
guac_ssh_client* ssh_client = (guac_ssh_client*) client->data;
return guac_terminal_prompt(ssh_client->term, cred_name, false);
}
void* ssh_input_thread(void* data) {
@ -206,17 +208,17 @@ void* ssh_client_thread(void* data) {
if (settings->wol_send_packet) {
guac_client_log(client, GUAC_LOG_DEBUG, "Sending Wake-on-LAN packet, "
"and pausing for %d seconds.", settings->wol_wait_time);
/* Send the Wake-on-LAN request. */
if (guac_wol_wake(settings->wol_mac_addr, settings->wol_broadcast_addr,
settings->wol_udp_port))
return NULL;
/* If wait time is specified, sleep for that amount of time. */
if (settings->wol_wait_time > 0)
guac_timestamp_msleep(settings->wol_wait_time * 1000);
}
/* Init SSH base libraries */
if (guac_common_ssh_init(client)) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
@ -228,7 +230,7 @@ void* ssh_client_thread(void* data) {
/* Set up screen recording, if requested */
if (settings->recording_path != NULL) {
ssh_client->recording = guac_common_recording_create(client,
ssh_client->recording = guac_recording_create(client,
settings->recording_path,
settings->recording_name,
settings->create_recording_path,
@ -238,12 +240,23 @@ void* ssh_client_thread(void* data) {
settings->recording_include_keys);
}
/* Create terminal options with required parameters */
guac_terminal_options* options = guac_terminal_options_create(
settings->width, settings->height, settings->resolution);
/* Set optional parameters */
options->disable_copy = settings->disable_copy;
options->max_scrollback = settings->max_scrollback;
options->font_name = settings->font_name;
options->font_size = settings->font_size;
options->color_scheme = settings->color_scheme;
options->backspace = settings->backspace;
/* Create terminal */
ssh_client->term = guac_terminal_create(client, ssh_client->clipboard,
settings->disable_copy, settings->max_scrollback,
settings->font_name, settings->font_size, settings->resolution,
settings->width, settings->height, settings->color_scheme,
settings->backspace);
ssh_client->term = guac_terminal_create(client, options);
/* Free options struct now that it's been used */
free(options);
/* Fail if terminal init failed */
if (ssh_client->term == NULL) {
@ -347,10 +360,12 @@ void* ssh_client_thread(void* data) {
/* Init handlers for Guacamole-specific console codes */
if (!settings->sftp_disable_upload)
ssh_client->term->upload_path_handler = guac_sftp_set_upload_path;
guac_terminal_set_upload_path_handler(ssh_client->term,
guac_sftp_set_upload_path);
if (!settings->sftp_disable_download)
ssh_client->term->file_download_handler = guac_sftp_download_file;
guac_terminal_set_file_download_handler(ssh_client->term,
guac_sftp_download_file);
guac_client_log(client, GUAC_LOG_DEBUG, "SFTP session initialized");
@ -364,10 +379,11 @@ void* ssh_client_thread(void* data) {
" Backspace may not work as expected.");
/* Request PTY */
int term_height = guac_terminal_get_rows(ssh_client->term);
int term_width = guac_terminal_get_columns(ssh_client->term);
if (libssh2_channel_request_pty_ex(ssh_client->term_channel,
settings->terminal_type, strlen(settings->terminal_type),
ssh_ttymodes, ttymodeBytes, ssh_client->term->term_width,
ssh_client->term->term_height, 0, 0)) {
ssh_ttymodes, ttymodeBytes, term_width, term_height, 0, 0)) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Unable to allocate PTY.");
return NULL;
}

View File

@ -23,7 +23,6 @@
#include "config.h"
#include "common/clipboard.h"
#include "common/recording.h"
#include "common-ssh/sftp.h"
#include "common-ssh/ssh.h"
#include "common-ssh/user.h"
@ -35,6 +34,7 @@
#endif
#include <guacamole/client.h>
#include <guacamole/recording.h>
#include <pthread.h>
@ -90,11 +90,6 @@ typedef struct guac_ssh_client {
*/
pthread_mutex_t term_channel_lock;
/**
* The current clipboard contents.
*/
guac_common_clipboard* clipboard;
/**
* The terminal which will render all output from the SSH client.
*/
@ -104,7 +99,7 @@ typedef struct guac_ssh_client {
* The in-progress session recording, or NULL if no recording is in
* progress.
*/
guac_common_recording* recording;
guac_recording* recording;
} guac_ssh_client ;

View File

@ -25,6 +25,7 @@
#include "input.h"
#include "user.h"
#include "pipe.h"
#include "terminal/terminal.h"
#include "sftp.h"
#include "ssh.h"
#include "settings.h"
@ -113,8 +114,8 @@ int guac_ssh_user_leave_handler(guac_user* user) {
guac_ssh_client* ssh_client = (guac_ssh_client*) user->client->data;
/* Update shared cursor state */
guac_common_cursor_remove_user(ssh_client->term->cursor, user);
/* Remove the user from the terminal */
guac_terminal_remove_user(ssh_client->term, user);
/* Free settings if not owner (owner settings will be freed with client) */
if (!user->owner) {

View File

@ -26,6 +26,7 @@
#include <guacamole/socket.h>
#include <guacamole/user.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -54,8 +55,9 @@ int guac_telnet_argv_callback(guac_user* user, const char* mimetype,
/* Update terminal window size if connected */
if (telnet_client->telnet != NULL && telnet_client->naws_enabled)
guac_telnet_send_naws(telnet_client->telnet, terminal->term_width,
terminal->term_height);
guac_telnet_send_naws(telnet_client->telnet,
guac_terminal_get_columns(terminal),
guac_terminal_get_rows(terminal));
return 0;
@ -68,15 +70,17 @@ void* guac_telnet_send_current_argv(guac_user* user, void* data) {
/* Send current color scheme */
guac_user_stream_argv(user, user->socket, "text/plain",
GUAC_TELNET_ARGV_COLOR_SCHEME, terminal->color_scheme);
GUAC_TELNET_ARGV_COLOR_SCHEME,
guac_terminal_get_color_scheme(terminal));
/* Send current font name */
guac_user_stream_argv(user, user->socket, "text/plain",
GUAC_TELNET_ARGV_FONT_NAME, terminal->font_name);
GUAC_TELNET_ARGV_FONT_NAME,
guac_terminal_get_font_name(terminal));
/* Send current font size */
char font_size[64];
sprintf(font_size, "%i", terminal->font_size);
sprintf(font_size, "%i", guac_terminal_get_font_size(terminal));
guac_user_stream_argv(user, user->socket, "text/plain",
GUAC_TELNET_ARGV_FONT_SIZE, font_size);

View File

@ -20,10 +20,8 @@
#include "config.h"
#include "argv.h"
#include "client.h"
#include "common/recording.h"
#include "settings.h"
#include "telnet.h"
#include "terminal/terminal.h"
#include "user.h"
#include <langinfo.h>
@ -31,9 +29,11 @@
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <guacamole/argv.h>
#include <guacamole/client.h>
#include <guacamole/recording.h>
int guac_client_init(guac_client* client) {
@ -44,9 +44,6 @@ int guac_client_init(guac_client* client) {
guac_telnet_client* telnet_client = calloc(1, sizeof(guac_telnet_client));
client->data = telnet_client;
/* Init clipboard */
telnet_client->clipboard = guac_common_clipboard_alloc(GUAC_TELNET_CLIPBOARD_MAX_LENGTH);
/* Init telnet client */
telnet_client->socket_fd = -1;
telnet_client->naws_enabled = 0;
@ -85,7 +82,7 @@ int guac_telnet_client_free_handler(guac_client* client) {
/* Clean up recording, if in progress */
if (telnet_client->recording != NULL)
guac_common_recording_free(telnet_client->recording);
guac_recording_free(telnet_client->recording);
/* Kill terminal */
guac_terminal_free(telnet_client->term);
@ -100,7 +97,6 @@ int guac_telnet_client_free_handler(guac_client* client) {
if (telnet_client->settings != NULL)
guac_telnet_settings_free(telnet_client->settings);
guac_common_clipboard_free(telnet_client->clipboard);
free(telnet_client);
return 0;

Some files were not shown because too many files have changed in this diff Show More