Compare commits

...

51 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
35 changed files with 972 additions and 269 deletions

View File

@ -21,25 +21,32 @@
# Dockerfile for guacamole-server # Dockerfile for guacamole-server
# #
# The Ubuntu image that should be used as the basis for the guacd image # The Alpine Linux image that should be used as the basis for the guacd image
ARG UBUNTU_BASE_IMAGE=21.10 ARG ALPINE_BASE_IMAGE=latest
FROM alpine:${ALPINE_BASE_IMAGE} AS builder
# Use Debian as base for the build # Install build dependencies
FROM ubuntu:${UBUNTU_BASE_IMAGE} AS builder 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
# # Copy source to container for sake of build
# The Debian repository that should be preferred for dependencies (this will be ARG BUILD_DIR=/tmp/guacamole-server
# added to /etc/apt/sources.list if not already present) COPY . ${BUILD_DIR}
#
# 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 UBUNTU_RELEASE=impish-backports
# Add repository for specified Ubuntu release if not already present in
# sources.list
RUN grep " ${UBUNTU_RELEASE} " /etc/apt/sources.list || echo >> /etc/apt/sources.list \
"deb http://archive.ubuntu.com/ubuntu/ ${UBUNTU_RELEASE} main contrib non-free"
# #
# Base directory for installed build artifacts. # Base directory for installed build artifacts.
@ -47,70 +54,99 @@ RUN grep " ${UBUNTU_RELEASE} " /etc/apt/sources.list || echo >> /etc/apt/sources
# NOTE: Due to limitations of the Docker image build process, this value is # NOTE: Due to limitations of the Docker image build process, this value is
# duplicated in an ARG in the second stage of the build. # 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 # Automatically select the latest versions of each core protocol support
ARG BUILD_DEPENDENCIES=" \ # library (these can be overridden at build time if a specific version is
autoconf \ # needed)
automake \ #
freerdp2-dev \ ARG WITH_FREERDP='2(\.\d+)+'
gcc \ ARG WITH_LIBSSH2='libssh2-\d+(\.\d+)+'
libcairo2-dev \ ARG WITH_LIBTELNET='\d+(\.\d+)+'
libgcrypt-dev \ ARG WITH_LIBVNCCLIENT='LibVNCServer-\d+(\.\d+)+'
libjpeg-turbo8-dev \ ARG WITH_LIBWEBSOCKETS='v\d+(\.\d+)+'
libossp-uuid-dev \
libpango1.0-dev \
libpulse-dev \
libssh2-1-dev \
libssl-dev \
libtelnet-dev \
libtool \
libvncserver-dev \
libwebsockets-dev \
libwebp-dev \
make"
# 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 ARG FREERDP_OPTS="\
RUN apt-get update && \ -DBUILTIN_CHANNELS=OFF \
apt-get install -t ${UBUNTU_RELEASE} -y $BUILD_DEPENDENCIES && \ -DCHANNEL_URBDRC=OFF \
rm -rf /var/lib/apt/lists/* -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 ARG GUACAMOLE_SERVER_OPTS="\
COPY src/guacd-docker/bin "${PREFIX_DIR}/bin/" --disable-guaclog"
# Copy source to container for sake of build ARG LIBSSH2_OPTS="\
COPY . "$BUILD_DIR" -DBUILD_EXAMPLES=OFF \
-DBUILD_SHARED_LIBS=ON"
# Build guacamole-server from local source ARG LIBTELNET_OPTS="\
RUN ${PREFIX_DIR}/bin/build-guacd.sh "$BUILD_DIR" "$PREFIX_DIR" --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 # 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}/sbin/guacd \
${PREFIX_DIR}/lib/libguac-client-*.so \ ${PREFIX_DIR}/lib/libguac-client-*.so \
${PREFIX_DIR}/lib/freerdp2/*guac*.so \ ${PREFIX_DIR}/lib/freerdp2/*guac*.so \
> ${PREFIX_DIR}/DEPENDENCIES > ${PREFIX_DIR}/DEPENDENCIES
# Use same Debian as the base for the runtime image # Use same Alpine version as the base for the runtime image
FROM ubuntu:${UBUNTU_BASE_IMAGE} FROM alpine:${ALPINE_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 UBUNTU_RELEASE=impish-backports
# Add repository for specified Ubuntu release if not already present in
# sources.list
RUN grep " ${UBUNTU_RELEASE} " /etc/apt/sources.list || echo >> /etc/apt/sources.list \
"deb http://archive.ubuntu.com/ubuntu/ ${UBUNTU_RELEASE} main contrib non-free"
# #
# Base directory for installed build artifacts. See also the # Base directory for installed build artifacts. See also the
@ -119,36 +155,27 @@ RUN grep " ${UBUNTU_RELEASE} " /etc/apt/sources.list || echo >> /etc/apt/sources
# NOTE: Due to limitations of the Docker image build process, this value is # NOTE: Due to limitations of the Docker image build process, this value is
# duplicated in an ARG in the first stage of the build. # duplicated in an ARG in the first stage of the build.
# #
ARG PREFIX_DIR=/usr/local/guacamole ARG PREFIX_DIR=/opt/guacamole
# Runtime environment # Runtime environment
ENV LC_ALL=C.UTF-8 ENV LC_ALL=C.UTF-8
ENV LD_LIBRARY_PATH=${PREFIX_DIR}/lib ENV LD_LIBRARY_PATH=${PREFIX_DIR}/lib
ENV GUACD_LOG_LEVEL=info 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 build artifacts into this stage
COPY --from=builder ${PREFIX_DIR} ${PREFIX_DIR} COPY --from=builder ${PREFIX_DIR} ${PREFIX_DIR}
# Bring runtime environment up to date and install runtime dependencies # Bring runtime environment up to date and install runtime dependencies
RUN apt-get update && \ RUN apk add --no-cache \
apt-get install -t ${UBUNTU_RELEASE} -y --no-install-recommends $RUNTIME_DEPENDENCIES && \ ca-certificates \
apt-get install -t ${UBUNTU_RELEASE} -y --no-install-recommends $(cat "${PREFIX_DIR}"/DEPENDENCIES) && \ ghostscript \
rm -rf /var/lib/apt/lists/* netcat-openbsd \
shadow \
# Link FreeRDP plugins into proper path terminus-font \
RUN ${PREFIX_DIR}/bin/link-freerdp-plugins.sh \ ttf-dejavu \
${PREFIX_DIR}/lib/freerdp2/libguac*.so 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 # 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 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 UID=1000
ARG GID=1000 ARG GID=1000
RUN groupadd --gid $GID guacd 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 # Run with user guacd
USER guacd USER guacd
@ -170,5 +197,5 @@ EXPOSE 4822
# Note the path here MUST correspond to the value specified in the # Note the path here MUST correspond to the value specified in the
# PREFIX_DIR build argument. # 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

@ -117,7 +117,7 @@ error() {
## ##
usage() { usage() {
cat >&2 <<END 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]... Usage: guacctl [OPTION] [FILE or NAME]...
-d, --download download each of the files listed. -d, --download download each of the files listed.

View File

@ -18,7 +18,7 @@
# #
AC_PREREQ([2.61]) AC_PREREQ([2.61])
AC_INIT([guacamole-server], [1.4.0]) AC_INIT([guacamole-server], [1.5.0])
AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_AUX_DIR([build-aux])
AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects]) AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects])
AM_SILENT_RULES([yes]) AM_SILENT_RULES([yes])

View File

@ -22,6 +22,7 @@
#include "common-ssh/user.h" #include "common-ssh/user.h"
#include <guacamole/client.h> #include <guacamole/client.h>
#include <guacamole/fips.h>
#include <libssh2.h> #include <libssh2.h>
#ifdef LIBSSH2_USES_GCRYPT #ifdef LIBSSH2_USES_GCRYPT
@ -46,6 +47,20 @@
GCRY_THREAD_OPTION_PTHREAD_IMPL; GCRY_THREAD_OPTION_PTHREAD_IMPL;
#endif #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 #ifdef OPENSSL_REQUIRES_THREADING_CALLBACKS
/** /**
* Array of mutexes, used by OpenSSL. * 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); CRYPTO_set_locking_callback(guac_common_ssh_openssl_locking_callback);
#endif #endif
/* Init OpenSSL */ #if OPENSSL_VERSION_NUMBER < 0x10100000L
/* Init OpenSSL - only required for OpenSSL Versions < 1.1.0 */
SSL_library_init(); SSL_library_init();
ERR_load_crypto_strings(); ERR_load_crypto_strings();
#endif
/* Init libssh2 */ /* Init libssh2 */
libssh2_init(0); libssh2_init(0);
@ -484,6 +501,17 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
return NULL; 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 */ /* Perform handshake */
if (libssh2_session_handshake(session, fd)) { if (libssh2_session_handshake(session, fd)) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,

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, void guac_common_display_dup(guac_common_display* display, guac_user* user,
guac_socket* socket) { guac_socket* socket) {
guac_client* client = user->client;
pthread_mutex_lock(&display->_lock); pthread_mutex_lock(&display->_lock);
/* Sunchronize shared cursor */ /* 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->layers, user, socket);
guac_common_display_dup_layers(display->buffers, 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); pthread_mutex_unlock(&display->_lock);
} }
@ -384,4 +389,3 @@ void guac_common_display_free_buffer(guac_common_display* display,
pthread_mutex_unlock(&display->_lock); 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,49 +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 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
#
cd "$BUILD_DIR"
autoreconf -fi
./configure --prefix="$PREFIX_DIR" --disable-guaclog --with-freerdp-plugin-dir="$PREFIX_DIR/lib/freerdp2"
make
make install
ldconfig

View File

@ -1,76 +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.
##
##
## Locates the base directory of the 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.
##
where_is_freerdp() {
# Determine the location of any freerdp2 .so files
PATHS="$(find / -iname '*libfreerdp2.so.*' \
| xargs -r dirname \
| xargs -r realpath \
| sort -u)"
# Verify that exactly one location was found
if [ -z "$PATHS" -o "$(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
#
# Determine correct install location for FreeRDP plugins
FREERDP_DIR="$(where_is_freerdp)"
FREERDP_PLUGIN_DIR="${FREERDP_DIR}/freerdp2"
while [ -n "$1" ]; do
# 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 ## @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 ## given binaries. Each package is only listed once, even if multiple binaries
## provided by the same package are given. ## provided by the same package are given.
## ##
@ -35,19 +35,17 @@ while [ -n "$1" ]; do
ldd "$1" | grep -v 'libguac' | awk '/=>/{print $(NF-1)}' \ ldd "$1" | grep -v 'libguac' | awk '/=>/{print $(NF-1)}' \
| while read LIBRARY; do | while read LIBRARY; do
# In some cases, the library that's linked against is a hard link # List the package providing that library, if any
# to the file that's managed by the package, which dpkg doesn't understand. apk info -W "$LIBRARY" 2> /dev/null \
# Searching by */basename ensures the package will be found in these cases. | grep 'is owned by' | grep -o '[^ ]*$' || true
LIBRARY_BASENAME=$(basename "$LIBRARY")
# Determine the Debian package which is associated with that
# library, if any
dpkg-query -S "*/$LIBRARY_BASENAME" || true
done done
# Next binary # Next binary
shift 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); CRYPTO_set_locking_callback(guacd_openssl_locking_callback);
#endif #endif
/* Init SSL */ #if OPENSSL_VERSION_NUMBER < 0x10100000L
/* Init OpenSSL for OpenSSL Versions < 1.1.0 */
SSL_library_init(); SSL_library_init();
SSL_load_error_strings(); SSL_load_error_strings();
ssl_context = SSL_CTX_new(SSLv23_server_method()); 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 */ /* Load key */
if (config->key_file != NULL) { if (config->key_file != NULL) {

View File

@ -213,7 +213,7 @@ int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) {
#endif #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 bitrate, int width, int height, int gop_size, int qmax, int qmin,
int pix_fmt, AVRational time_base) { 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, int guacenc_open_avcodec(AVCodecContext *avcodec_context,
AVCodec *codec, AVDictionary **options, const AVCodec *codec, AVDictionary **options,
AVStream* stream) { AVStream* stream) {
int ret = avcodec_open2(avcodec_context, codec, options); 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. * 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 bitrate, int width, int height, int gop_size, int qmax, int qmin,
int pix_fmt, AVRational time_base); 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. * Zero on success, a negative value on error.
*/ */
int guacenc_open_avcodec(AVCodecContext *avcodec_context, int guacenc_open_avcodec(AVCodecContext *avcodec_context,
AVCodec *codec, AVDictionary **options, const AVCodec *codec, AVDictionary **options,
AVStream* stream); AVStream* stream);
#endif #endif

View File

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

View File

@ -44,6 +44,7 @@ libguacinc_HEADERS = \
guacamole/client-types.h \ guacamole/client-types.h \
guacamole/error.h \ guacamole/error.h \
guacamole/error-types.h \ guacamole/error-types.h \
guacamole/fips.h \
guacamole/hash.h \ guacamole/hash.h \
guacamole/layer.h \ guacamole/layer.h \
guacamole/layer-types.h \ guacamole/layer-types.h \
@ -93,6 +94,7 @@ libguac_la_SOURCES = \
encode-jpeg.c \ encode-jpeg.c \
encode-png.c \ encode-png.c \
error.c \ error.c \
fips.c \
hash.c \ hash.c \
id.c \ id.c \
palette.c \ palette.c \
@ -137,7 +139,7 @@ libguac_la_CFLAGS = \
-Werror -Wall -pedantic -Werror -Wall -pedantic
libguac_la_LDFLAGS = \ libguac_la_LDFLAGS = \
-version-info 20:0:0 \ -version-info 21:0:0 \
-no-undefined \ -no-undefined \
@CAIRO_LIBS@ \ @CAIRO_LIBS@ \
@DL_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)); 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; return retval;
} }
@ -333,6 +337,10 @@ void guac_client_remove_user(guac_client* client, guac_user* user) {
pthread_rwlock_unlock(&(client->__users_lock)); 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 */ /* Call handler, if defined */
if (user->leave_handler) if (user->leave_handler)
user->leave_handler(user); user->leave_handler(user);
@ -675,6 +683,36 @@ static void* __webp_support_callback(guac_user* user, void* data) {
} }
#endif #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() * A callback function which is invoked by guac_client_owner_supports_required()
* to determine if the owner of a client supports the "required" instruction, * to determine if the owner of a client supports the "required" instruction,
@ -705,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) { int guac_client_supports_webp(guac_client* client) {
#ifdef ENABLE_WEBP #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

@ -737,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, guac_composite_mode mode, const guac_layer* layer, int x, int y,
cairo_surface_t* surface, int quality, int lossless); 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" * Returns whether the owner of the given client supports the "required"
* instruction, returning non-zero if the client owner does support the * instruction, returning non-zero if the client owner does support the
@ -752,6 +767,42 @@ void guac_client_stream_webp(guac_client* client, guac_socket* socket,
*/ */
int guac_client_owner_supports_required(guac_client* client); 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 * 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. * not support WebP, or the server cannot encode WebP images, zero is returned.

View File

@ -0,0 +1,33 @@
/*
* 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_FIPS_H
#define GUAC_FIPS_H
/**
* 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 * This version is passed by the __guac_protocol_send_args() function from the
* server to the client during the client/server handshake. * 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 * 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 * allowing connections in guacd to request information from the client and
* await a response. * 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; } 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 #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, int vguac_protocol_send_log(guac_socket* socket, const char* format,
va_list args); 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. * Sends a mouse instruction over the given guac_socket connection.
* *

View File

@ -102,6 +102,14 @@ struct guac_user_info {
*/ */
guac_protocol_version protocol_version; 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 { 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, guac_composite_mode mode, const guac_layer* layer, int x, int y,
cairo_surface_t* surface, int quality, int lossless); 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. * 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_0_0, "VERSION_1_0_0" },
{ GUAC_PROTOCOL_VERSION_1_1_0, "VERSION_1_1_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_3_0, "VERSION_1_3_0" },
{ GUAC_PROTOCOL_VERSION_1_5_0, "VERSION_1_5_0" },
{ GUAC_PROTOCOL_VERSION_UNKNOWN, NULL } { 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, int guac_protocol_send_file(guac_socket* socket, const guac_stream* stream,
const char* mimetype, const char* name) { const char* mimetype, const char* name) {

View File

@ -27,11 +27,11 @@
*/ */
void test_guac_protocol__version_to_string() { 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_b = GUAC_PROTOCOL_VERSION_1_0_0;
guac_protocol_version version_c = GUAC_PROTOCOL_VERSION_UNKNOWN; 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_STRING_EQUAL(guac_protocol_version_to_string(version_b), "VERSION_1_0_0");
CU_ASSERT_PTR_NULL(guac_protocol_version_to_string(version_c)); CU_ASSERT_PTR_NULL(guac_protocol_version_to_string(version_c));

View File

@ -64,6 +64,7 @@ __guac_instruction_handler_mapping __guac_handshake_handler_map[] = {
{"video", __guac_handshake_video_handler}, {"video", __guac_handshake_video_handler},
{"image", __guac_handshake_image_handler}, {"image", __guac_handshake_image_handler},
{"timezone", __guac_handshake_timezone_handler}, {"timezone", __guac_handshake_timezone_handler},
{"name", __guac_handshake_name_handler},
{NULL, NULL} {NULL, NULL}
}; };
@ -684,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) { int __guac_handshake_timezone_handler(guac_user* user, int argc, char** argv) {
/* Free any past value */ /* Free any past value */

View File

@ -218,6 +218,13 @@ __guac_instruction_handler __guac_handshake_video_handler;
*/ */
__guac_instruction_handler __guac_handshake_image_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 * Internal handler function that is called when the timezone instruction is
* received during the handshake process, specifying the timezone of the * 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.audio_mimetypes = NULL;
user->info.image_mimetypes = NULL; user->info.image_mimetypes = NULL;
user->info.video_mimetypes = NULL; user->info.video_mimetypes = NULL;
user->info.name = NULL;
user->info.timezone = NULL; user->info.timezone = NULL;
/* Count number of arguments. */ /* 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.image_mimetypes);
guac_free_mimetypes((char **) user->info.video_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); free((char *) user->info.timezone);
guac_parser_free(parser); 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) { int guac_user_supports_required(guac_user* user) {
if (user == NULL) if (user == NULL)

View File

@ -230,6 +230,7 @@ BUILT_SOURCES = \
rdp_keymaps = \ rdp_keymaps = \
$(srcdir)/keymaps/base.keymap \ $(srcdir)/keymaps/base.keymap \
$(srcdir)/keymaps/failsafe.keymap \ $(srcdir)/keymaps/failsafe.keymap \
$(srcdir)/keymaps/cs-cz-qwertz.keymap \
$(srcdir)/keymaps/de_de_qwertz.keymap \ $(srcdir)/keymaps/de_de_qwertz.keymap \
$(srcdir)/keymaps/de_ch_qwertz.keymap \ $(srcdir)/keymaps/de_ch_qwertz.keymap \
$(srcdir)/keymaps/en_gb_qwerty.keymap \ $(srcdir)/keymaps/en_gb_qwerty.keymap \

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

@ -28,6 +28,7 @@
#include <freerdp/settings.h> #include <freerdp/settings.h>
#include <freerdp/freerdp.h> #include <freerdp/freerdp.h>
#include <guacamole/client.h> #include <guacamole/client.h>
#include <guacamole/fips.h>
#include <guacamole/string.h> #include <guacamole/string.h>
#include <guacamole/user.h> #include <guacamole/user.h>
#include <guacamole/wol-constants.h> #include <guacamole/wol-constants.h>
@ -39,6 +40,16 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.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 */ /* Client plugin arguments */
const char* GUAC_RDP_CLIENT_ARGS[] = { const char* GUAC_RDP_CLIENT_ARGS[] = {
"hostname", "hostname",
@ -706,12 +717,27 @@ guac_rdp_settings* guac_rdp_parse_args(guac_user* user,
if (strcmp(argv[IDX_SECURITY], "nla") == 0) { if (strcmp(argv[IDX_SECURITY], "nla") == 0) {
guac_user_log(user, GUAC_LOG_INFO, "Security mode: NLA"); guac_user_log(user, GUAC_LOG_INFO, "Security mode: NLA");
settings->security_mode = GUAC_SECURITY_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 */ /* Extended NLA security */
else if (strcmp(argv[IDX_SECURITY], "nla-ext") == 0) { else if (strcmp(argv[IDX_SECURITY], "nla-ext") == 0) {
guac_user_log(user, GUAC_LOG_INFO, "Security mode: Extended NLA"); guac_user_log(user, GUAC_LOG_INFO, "Security mode: Extended NLA");
settings->security_mode = GUAC_SECURITY_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 */ /* TLS security */
@ -1529,7 +1555,21 @@ void guac_rdp_push_settings(guac_client* client,
case GUAC_SECURITY_ANY: case GUAC_SECURITY_ANY:
rdp_settings->RdpSecurity = TRUE; rdp_settings->RdpSecurity = TRUE;
rdp_settings->TlsSecurity = 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; rdp_settings->ExtSecurity = FALSE;
break; break;

View File

@ -77,6 +77,8 @@ libguac_terminal_la_LIBADD = \
@LIBGUAC_LTLIB@ @LIBGUAC_LTLIB@
libguac_terminal_la_LDFLAGS = \ libguac_terminal_la_LDFLAGS = \
-version-info 0:0:0 \
-no-undefined \
@CAIRO_LIBS@ \ @CAIRO_LIBS@ \
@MATH_LIBS@ \ @MATH_LIBS@ \
@PANGO_LIBS@ \ @PANGO_LIBS@ \

View File

@ -20,6 +20,7 @@
#include "common/clipboard.h" #include "common/clipboard.h"
#include "common/cursor.h" #include "common/cursor.h"
#include "common/iconv.h"
#include "terminal/buffer.h" #include "terminal/buffer.h"
#include "terminal/color-scheme.h" #include "terminal/color-scheme.h"
#include "terminal/common.h" #include "terminal/common.h"
@ -936,14 +937,13 @@ void guac_terminal_commit_cursor(guac_terminal* term) {
} }
int guac_terminal_write(guac_terminal* term, const char* c, int size) { int guac_terminal_write(guac_terminal* term, const char* buffer, int length) {
guac_terminal_lock(term); guac_terminal_lock(term);
while (size > 0) { for (int written = 0; written < length; written++) {
/* Read and advance to next character */ /* Read and advance to next character */
char current = *(c++); char current = *(buffer++);
size--;
/* Write character to typescript, if any */ /* Write character to typescript, if any */
if (term->typescript != NULL) if (term->typescript != NULL)
@ -956,7 +956,7 @@ int guac_terminal_write(guac_terminal* term, const char* c, int size) {
guac_terminal_unlock(term); guac_terminal_unlock(term);
guac_terminal_notify(term); guac_terminal_notify(term);
return 0; return length;
} }
@ -2165,7 +2165,16 @@ void guac_terminal_clipboard_reset(guac_terminal* terminal,
void guac_terminal_clipboard_append(guac_terminal* terminal, void guac_terminal_clipboard_append(guac_terminal* terminal,
const char* data, int length) { const char* data, int length) {
guac_common_clipboard_append(terminal->clipboard, data, length);
/* Allocate and clear space for the converted data */
char output_data[GUAC_COMMON_CLIPBOARD_MAX_LENGTH];
char* output = output_data;
/* Convert clipboard contents */
guac_iconv(GUAC_READ_UTF8_NORMALIZED, &data, length,
GUAC_WRITE_UTF8, &output, GUAC_COMMON_CLIPBOARD_MAX_LENGTH);
guac_common_clipboard_append(terminal->clipboard, output_data, output - output_data);
} }
void guac_terminal_remove_user(guac_terminal* terminal, guac_user* user) { void guac_terminal_remove_user(guac_terminal* terminal, guac_user* user) {

View File

@ -29,6 +29,22 @@
#include "terminal.h" #include "terminal.h"
#include "typescript.h" #include "typescript.h"
/**
* Handler for characters printed to the terminal. When a character is printed,
* the current char handler for the terminal is called and given that
* character.
*
* @param term
* The terminal receiving the character.
*
* @param c
* The received character.
*
* @return
* Zero if the character was handled successfully, non-zero otherwise.
*/
typedef int guac_terminal_char_handler(guac_terminal* term, unsigned char c);
struct guac_terminal { struct guac_terminal {
/** /**

View File

@ -141,19 +141,35 @@ typedef enum guac_terminal_cursor_type {
} guac_terminal_cursor_type; } guac_terminal_cursor_type;
/** /**
* Handler for characters printed to the terminal. When a character is printed, * Handler that is invoked whenever the necessary terminal codes are sent to
* the current char handler for the terminal is called and given that * to the given terminal to change the path for future file uploads.
* character. *
*/ * @param client
typedef int guac_terminal_char_handler(guac_terminal* term, unsigned char c); * The guac_client associated with the terminal receiving the upload path
* change terminal code.
/** *
* Handler for setting the destination path for file uploads. * @param path
* The requested path.
*/ */
typedef void guac_terminal_upload_path_handler(guac_client* client, char* path); typedef void guac_terminal_upload_path_handler(guac_client* client, char* path);
/** /**
* Handler for creating an outbound file download stream for a specified file. * Handler that is invoked whenever the necessary terminal codes are sent to
* initiate a download of a given remote file.
*
* @param client
* The guac_client associated with the terminal receiving the file download
* terminal code.
*
* @param filename
* The name of the requested file. This may be a relative or absolute path,
* and it is up to the implementation to define how this value is
* interpreted.
*
* @return
* The file stream created for the file download, already configured to
* properly handle "ack" responses, etc. from the client, or NULL if the
* stream could not be created.
*/ */
typedef guac_stream* guac_terminal_file_download_handler(guac_client* client, char* filename); typedef guac_stream* guac_terminal_file_download_handler(guac_client* client, char* filename);
@ -276,11 +292,16 @@ guac_terminal_options* guac_terminal_options_create(
/** /**
* Frees all resources associated with the given terminal. * Frees all resources associated with the given terminal.
*
* @param term
* The terminal to free.
*/ */
void guac_terminal_free(guac_terminal* term); void guac_terminal_free(guac_terminal* term);
/** /**
* Set the upload path handler for the given terminal. * Sets the upload path handler for the given terminal. The upload path handler
* is invoked whenever the terminal codes requesting an upload path change are
* sent.
* *
* @param terminal * @param terminal
* The terminal to set the upload path handler for. * The terminal to set the upload path handler for.
@ -293,12 +314,14 @@ void guac_terminal_set_upload_path_handler(guac_terminal* terminal,
guac_terminal_upload_path_handler* upload_path_handler); guac_terminal_upload_path_handler* upload_path_handler);
/** /**
* Set the file download handler for the given terminal. * Sets the file download handler for the given terminal. The file download
* handler is invoked whenever the terminal codes requesting download of a
* given remote file are sent.
* *
* @param terminal * @param terminal
* The terminal to set the file download handler for. * The terminal to set the file download handler for.
* *
* @param upload_path_handler * @param file_download_handler
* The handler to be called whenever the necessary terminal codes are sent to * The handler to be called whenever the necessary terminal codes are sent to
* the given terminal to initiate a download of a given remote file. * the given terminal to initiate a download of a given remote file.
*/ */
@ -308,6 +331,13 @@ void guac_terminal_set_file_download_handler(guac_terminal* terminal,
/** /**
* Renders a single frame of terminal data. If data is not yet available, * Renders a single frame of terminal data. If data is not yet available,
* this function will block until data is written. * this function will block until data is written.
*
* @param terminal
* The terminal that should be rendered.
*
* @return
* Zero if the frame was rendered successfully, non-zero if an error
* occurred.
*/ */
int guac_terminal_render_frame(guac_terminal* terminal); int guac_terminal_render_frame(guac_terminal* terminal);
@ -316,6 +346,19 @@ int guac_terminal_render_frame(guac_terminal* terminal);
* supplied by calls to guac_terminal_send_key(), * supplied by calls to guac_terminal_send_key(),
* guac_terminal_send_mouse(), and guac_terminal_send_stream(). If input is not * guac_terminal_send_mouse(), and guac_terminal_send_stream(). If input is not
* yet available, this function will block. * yet available, this function will block.
*
* @param terminal
* The terminal to read from.
*
* @param c
* The buffer that should receive the bytes read.
*
* @param size
* The number of bytes available within the buffer.
*
* @return
* The number of bytes read into the buffer, zero if end-of-file is reached
* (STDIN has been closed), or a negative value if an error occurs.
*/ */
int guac_terminal_read_stdin(guac_terminal* terminal, char* c, int size); int guac_terminal_read_stdin(guac_terminal* terminal, char* c, int size);
@ -377,8 +420,23 @@ char* guac_terminal_prompt(guac_terminal* terminal, const char* title,
/** /**
* Writes the given format string and arguments to this terminal's STDOUT in * Writes the given format string and arguments to this terminal's STDOUT in
* the same manner as printf(). This function may block until space is * the same manner as printf(). The entire populated format string will always
* be written unless an error occurs. This function may block until space is
* freed in the output buffer by guac_terminal_render_frame(). * freed in the output buffer by guac_terminal_render_frame().
*
* @param terminal
* The terminal to write to.
*
* @param format
* A printf-style format string describing the data to be written to
* STDOUT.
*
* @param ...
* Any arguments to use when filling the format string.
*
* @return
* The number of bytes written to STDOUT, or a negative value if an error
* occurs preventing the data from being written in its entirety.
*/ */
int guac_terminal_printf(guac_terminal* terminal, const char* format, ...); int guac_terminal_printf(guac_terminal* terminal, const char* format, ...);
@ -486,9 +544,24 @@ int guac_terminal_send_data(guac_terminal* term, const char* data, int length);
int guac_terminal_send_string(guac_terminal* term, const char* data); int guac_terminal_send_string(guac_terminal* term, const char* data);
/** /**
* Writes the given string of characters to the terminal. * Writes the given buffer to the given terminal's STDOUT. All requested bytes
* will be written unless an error occurs. This function may block until space
* is freed in the output buffer by guac_terminal_render_frame().
*
* @param term
* The terminal to write to.
*
* @param buffer
* A buffer containing the characters to be written to the terminal.
*
* @param length
* The number of bytes within the given string to write to the terminal.
*
* @return
* The number of bytes written to STDOUT, or a negative value if an error
* occurs preventing the data from being written in its entirety.
*/ */
int guac_terminal_write(guac_terminal* term, const char* c, int size); int guac_terminal_write(guac_terminal* term, const char* buffer, int length);
/** /**
* Initializes the handlers of the given guac_stream such that it serves as the * Initializes the handlers of the given guac_stream such that it serves as the
@ -532,7 +605,7 @@ int guac_terminal_send_stream(guac_terminal* term, guac_user* user,
* STDIN. * STDIN.
* *
* @param ... * @param ...
* Any srguments to use when filling the format string. * Any arguments to use when filling the format string.
* *
* @return * @return
* The number of bytes written to STDIN, or a negative value if an error * The number of bytes written to STDIN, or a negative value if an error
@ -560,7 +633,20 @@ void guac_terminal_dup(guac_terminal* term, guac_user* user,
guac_socket* socket); guac_socket* socket);
/** /**
* Resize the terminal to the given dimensions. * Resize the client display and terminal to the given pixel dimensions.
*
* @param term
* The terminal to resize.
*
* @param width
* The new terminal width, in pixels.
*
* @param height
* The new terminal height, in pixels.
*
* @return
* Zero if the terminal was successfully resized to the given dimensions,
* non-zero otherwise.
*/ */
int guac_terminal_resize(guac_terminal* term, int width, int height); int guac_terminal_resize(guac_terminal* term, int width, int height);