Compare commits

..

5 Commits

Author SHA1 Message Date
Nick Couchman
0e2e3d20ae [WIP]: Check RDP sound format value before use. 2020-04-16 13:53:14 -04:00
Nick Couchman
f3f8be0591 [WIP]: Check argc in user handler. 2020-04-16 13:50:34 -04:00
Nick Couchman
4135c6e3b0 [WIP]: Check surface allocations 2020-04-16 13:47:30 -04:00
Nick Couchman
e011cf38eb [WIP] Check VNC buffer allocation. 2020-04-16 13:42:25 -04:00
Nick Couchman
ab38b6c912 [WIP]: Fix upload path out-of-bounds error. 2020-04-16 13:39:34 -04:00
243 changed files with 3479 additions and 11453 deletions

View File

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

4
.gitignore vendored
View File

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

View File

@ -21,173 +21,93 @@
# Dockerfile for guacamole-server # Dockerfile for guacamole-server
# #
# 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
# Install build dependencies # Use Debian as base for the build
RUN apk add --no-cache \ ARG DEBIAN_VERSION=stable
autoconf \ FROM debian:${DEBIAN_VERSION} AS builder
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
ARG BUILD_DIR=/tmp/guacamole-server
COPY . ${BUILD_DIR}
#
# Base directory for installed build artifacts. # Base directory for installed build artifacts.
# # 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=/opt/guacamole ARG PREFIX_DIR=/usr/local/guacamole
# # Build arguments
# Automatically select the latest versions of each core protocol support ARG BUILD_DIR=/tmp/guacd-docker-BUILD
# library (these can be overridden at build time if a specific version is ARG BUILD_DEPENDENCIES=" \
# needed) autoconf \
# automake \
ARG WITH_FREERDP='2(\.\d+)+' freerdp2-dev \
ARG WITH_LIBSSH2='libssh2-\d+(\.\d+)+' gcc \
ARG WITH_LIBTELNET='\d+(\.\d+)+' libcairo2-dev \
ARG WITH_LIBVNCCLIENT='LibVNCServer-\d+(\.\d+)+' libjpeg62-turbo-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"
# # Bring build environment up to date and install build dependencies
# Default build options for each core protocol support library, as well as RUN apt-get update && \
# guacamole-server itself (these can be overridden at build time if different apt-get install -y $BUILD_DEPENDENCIES && \
# options are needed) rm -rf /var/lib/apt/lists/*
#
ARG FREERDP_OPTS="\ # Add configuration scripts
-DBUILTIN_CHANNELS=OFF \ COPY src/guacd-docker/bin "${PREFIX_DIR}/bin/"
-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"
ARG GUACAMOLE_SERVER_OPTS="\ # Copy source to container for sake of build
--disable-guaclog" COPY . "$BUILD_DIR"
ARG LIBSSH2_OPTS="\ # Build guacamole-server from local source
-DBUILD_EXAMPLES=OFF \ RUN ${PREFIX_DIR}/bin/build-guacd.sh "$BUILD_DIR" "$PREFIX_DIR"
-DBUILD_SHARED_LIBS=ON"
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 # Record the packages of all runtime library dependencies
RUN ${BUILD_DIR}/src/guacd-docker/bin/list-dependencies.sh \ RUN ${PREFIX_DIR}/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 Alpine version as the base for the runtime image # Use same Debian as the base for the runtime image
FROM alpine:${ALPINE_BASE_IMAGE} FROM debian:${DEBIAN_VERSION}-slim
# # Base directory for installed build artifacts.
# Base directory for installed build artifacts. See also the # Due to limitations of the Docker image build process, this value is
# duplicated in an ARG in the first stage of the build. See also the
# CMD directive at the end of this build stage. # CMD directive at the end of this build stage.
# #
# NOTE: Due to limitations of the Docker image build process, this value is ARG PREFIX_DIR=/usr/local/guacamole
# duplicated in an ARG in the first stage of the build.
#
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=" \
ca-certificates \
ghostscript \
fonts-liberation \
fonts-dejavu \
xfonts-terminus"
# 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 apk add --no-cache \ RUN apt-get update && \
ca-certificates \ apt-get install -y $RUNTIME_DEPENDENCIES && \
ghostscript \ apt-get install -y $(cat "${PREFIX_DIR}"/DEPENDENCIES) && \
netcat-openbsd \ rm -rf /var/lib/apt/lists/*
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 # Link FreeRDP plugins into proper path
HEALTHCHECK --interval=5m --timeout=5s CMD nc -z 127.0.0.1 4822 || exit 1 RUN ${PREFIX_DIR}/bin/link-freerdp-plugins.sh \
${PREFIX_DIR}/lib/freerdp2/libguac*.so
# Create a new user guacd
ARG UID=1000
ARG GID=1000
RUN groupadd --gid $GID guacd
RUN useradd --system --create-home --shell /sbin/nologin --uid $UID --gid $GID guacd
# Run with user guacd
USER guacd
# Expose the default listener port # Expose the default listener port
EXPOSE 4822 EXPOSE 4822
@ -197,5 +117,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 /opt/guacamole/sbin/guacd -b 0.0.0.0 -L $GUACD_LOG_LEVEL -f CMD /usr/local/guacamole/sbin/guacd -b 0.0.0.0 -L $GUACD_LOG_LEVEL -f

View File

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

View File

@ -117,7 +117,7 @@ error() {
## ##
usage() { usage() {
cat >&2 <<END cat >&2 <<END
guacctl 1.5.0, Apache Guacamole terminal session control utility. guacctl 1.2.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.5.0]) AC_INIT([guacamole-server], [1.2.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])
@ -75,50 +75,21 @@ AC_CHECK_LIB([dl], [dlopen],
AC_MSG_ERROR("libdl is required on systems which do not otherwise provide dlopen()"), AC_MSG_ERROR("libdl is required on systems which do not otherwise provide dlopen()"),
[#include <dlfcn.h>])]) [#include <dlfcn.h>])])
# # OSSP UUID
# libuuid AC_CHECK_LIB([ossp-uuid], [uuid_make], [UUID_LIBS=-lossp-uuid],
# AC_CHECK_LIB([uuid], [uuid_make], [UUID_LIBS=-luuid],
AC_MSG_ERROR("The OSSP UUID library is required")))
have_libuuid=disabled # Check for and validate OSSP uuid.h header
AC_ARG_WITH([libuuid], AC_CHECK_HEADERS([ossp/uuid.h])
[AS_HELP_STRING([--with-libuuid], AC_CHECK_DECL([uuid_make],,
[use libuuid to generate unique identifiers @<:@default=check@:>@])], AC_MSG_ERROR("No OSSP uuid.h found in include path"),
[], [#ifdef HAVE_OSSP_UUID_H
[with_libuuid=check]) #include <ossp/uuid.h>
#else
if test "x$with_libuuid" != "xno" #include <uuid.h>
then #endif
have_libuuid=yes ])
AC_CHECK_LIB([uuid], [uuid_generate],
[UUID_LIBS=-luuid]
[AC_DEFINE([HAVE_LIBUUID],, [Whether libuuid is available])],
[have_libuuid=no])
fi
# OSSP UUID (if libuuid is unavilable)
if test "x${have_libuuid}" != "xyes"
then
AC_CHECK_LIB([ossp-uuid], [uuid_make], [UUID_LIBS=-lossp-uuid],
AC_CHECK_LIB([uuid], [uuid_make], [UUID_LIBS=-luuid],
AC_MSG_ERROR([
--------------------------------------------
Unable to find libuuid or the OSSP UUID library.
Either libuuid (from util-linux) or the OSSP UUID library is required for
guacamole-server to be built.
--------------------------------------------])))
# Check for and validate OSSP uuid.h header
AC_CHECK_HEADERS([ossp/uuid.h])
AC_CHECK_DECL([uuid_make],,
AC_MSG_ERROR("No OSSP uuid.h found in include path"),
[#ifdef HAVE_OSSP_UUID_H
#include <ossp/uuid.h>
#else
#include <uuid.h>
#endif
])
fi
# cunit # cunit
AC_CHECK_LIB([cunit], [CU_run_test], [CUNIT_LIBS=-lcunit]) AC_CHECK_LIB([cunit], [CU_run_test], [CUNIT_LIBS=-lcunit])
@ -160,11 +131,6 @@ AC_CHECK_DECL([strlcat],
[Whether strlcat() is defined])],, [Whether strlcat() is defined])],,
[#include <string.h>]) [#include <string.h>])
AC_CHECK_DECL([strnstr],
[AC_DEFINE([HAVE_STRNSTR],,
[Whether strnstr() is defined])],,
[#include <string.h>])
# Typedefs # Typedefs
AC_TYPE_SIZE_T AC_TYPE_SIZE_T
AC_TYPE_SSIZE_T AC_TYPE_SSIZE_T
@ -185,16 +151,12 @@ AC_SUBST([PULSE_INCLUDE], '-I$(top_srcdir)/src/pulse')
AC_SUBST([COMMON_SSH_LTLIB], '$(top_builddir)/src/common-ssh/libguac_common_ssh.la') AC_SUBST([COMMON_SSH_LTLIB], '$(top_builddir)/src/common-ssh/libguac_common_ssh.la')
AC_SUBST([COMMON_SSH_INCLUDE], '-I$(top_srcdir)/src/common-ssh') AC_SUBST([COMMON_SSH_INCLUDE], '-I$(top_srcdir)/src/common-ssh')
# Kubernetes support
AC_SUBST([LIBGUAC_CLIENT_KUBERNETES_LTLIB], '$(top_builddir)/src/protocols/kubernetes/libguac-client-kubernetes.la')
AC_SUBST([LIBGUAC_CLIENT_KUBERNETES_INCLUDE], '-I$(top_srcdir)/src/protocols/kubernetes')
# RDP support # RDP support
AC_SUBST([LIBGUAC_CLIENT_RDP_LTLIB], '$(top_builddir)/src/protocols/rdp/libguac-client-rdp.la') AC_SUBST([LIBGUAC_CLIENT_RDP_LTLIB], '$(top_builddir)/src/protocols/rdp/libguac-client-rdp.la')
AC_SUBST([LIBGUAC_CLIENT_RDP_INCLUDE], '-I$(top_srcdir)/src/protocols/rdp') AC_SUBST([LIBGUAC_CLIENT_RDP_INCLUDE], '-I$(top_srcdir)/src/protocols/rdp')
# Terminal emulator # 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)') AC_SUBST([TERMINAL_INCLUDE], '-I$(top_srcdir)/src/terminal $(PANGO_CFLAGS) $(PANGOCAIRO_CFLAGS) $(COMMON_INCLUDE)')
# Init directory # Init directory
@ -240,24 +202,6 @@ fi
AM_CONDITIONAL([ENABLE_AVCODEC], [test "x${have_libavcodec}" = "xyes"]) AM_CONDITIONAL([ENABLE_AVCODEC], [test "x${have_libavcodec}" = "xyes"])
#
# libavformat
#
have_libavformat=disabled
AC_ARG_WITH([libavformat],
[AS_HELP_STRING([--with-libavformat],
[use libavformat when encoding video @<:@default=check@:>@])],
[].
[with_libavformat=check])
if test "x$with_libavformat" != "xno"
then
have_libavformat=yes
PKG_CHECK_MODULES([AVFORMAT], [libavformat],, [have_libavformat=no]);
fi
AM_CONDITIONAL([ENABLE_AVFORMAT], [test "x${have_libavformat}" = "xyes"])
# #
# libavutil # libavutil
# #
@ -325,6 +269,30 @@ then
else else
AC_DEFINE([ENABLE_SSL],, [Whether SSL-related support is enabled]) 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 # OpenSSL 1.1 does away with explicit threading callbacks
AC_MSG_CHECKING([whether libssl requires threading callbacks]) AC_MSG_CHECKING([whether libssl requires threading callbacks])
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
@ -504,27 +472,6 @@ then
AC_CHECK_LIB([vncclient], [rfbInitClient], [VNC_LIBS="$VNC_LIBS -lvncclient"], [have_libvncserver=no]) AC_CHECK_LIB([vncclient], [rfbInitClient], [VNC_LIBS="$VNC_LIBS -lvncclient"], [have_libvncserver=no])
fi fi
#
# Underlying libvncserver usage of gcrypt
#
if test "x${have_libvncserver}" = "xyes"
then
# Whether libvncserver was built against libgcrypt
AC_CHECK_DECL([LIBVNCSERVER_WITH_CLIENT_GCRYPT],
[AC_CHECK_HEADER(gcrypt.h,,
[AC_MSG_WARN([
--------------------------------------------
libvncserver appears to be built against
libgcrypt, but the libgcrypt headers
could not be found. VNC will be disabled.
--------------------------------------------])
have_libvncserver=no])],,
[[#include <rfb/rfbconfig.h>]])
fi
AM_CONDITIONAL([ENABLE_VNC], [test "x${have_libvncserver}" = "xyes"]) AM_CONDITIONAL([ENABLE_VNC], [test "x${have_libvncserver}" = "xyes"])
AC_SUBST(VNC_LIBS) AC_SUBST(VNC_LIBS)
@ -607,34 +554,6 @@ then
fi fi
#
# Generic credential support within libVNCServer (authentication beyond
# basic, standard VNC passwords)
#
if test "x${have_libvncserver}" = "xyes"
then
have_vnc_creds=yes
AC_CHECK_MEMBERS([rfbClient.GetCredential],
[], [have_vnc_creds=no],
[[#include <rfb/rfbclient.h>]])
if test "x${have_vnc_creds}" = "xno"
then
AC_MSG_WARN([
--------------------------------------------
No generic credential support found in libvncclient.
VNC authentication support will be limited to passwords.
--------------------------------------------])
else
AC_DEFINE([ENABLE_VNC_GENERIC_CREDENTIALS],,
[Whether support for generic VNC credentials is available.])
fi
fi
# #
# FreeRDP 2 (libfreerdp2, libfreerdp-client2, and libwinpr2) # FreeRDP 2 (libfreerdp2, libfreerdp-client2, and libwinpr2)
# #
@ -686,44 +605,7 @@ then
fi fi
# It is difficult or impossible to test for variations in FreeRDP behavior in # Variation in memory internal allocation/free behavior
# between releases, as the change in behavior may not (yet) be associated with
# a corresponding change in version number and may not have any detectable
# effect on the FreeRDP API
AC_ARG_ENABLE(allow_freerdp_snapshots,
[AS_HELP_STRING([--enable-allow-freerdp-snapshots],
[allow building against unknown development snapshots of FreeRDP])
],allow_freerdp_snapshots=yes)
if test "x${have_freerdp2}" = "xyes" -a "x${allow_freerdp_snapshots}" != "xyes"
then
AC_MSG_CHECKING([whether FreeRDP appears to be a development version])
AC_EGREP_CPP([\"[0-9]+\\.[0-9]+\\.[0-9]+(-rc[0-9]+)?\"], [
#include <freerdp/version.h>
FREERDP_VERSION_FULL
],
[AC_MSG_RESULT([no])],
[AC_MSG_RESULT([yes])]
[AC_MSG_ERROR([
--------------------------------------------
You are building against a development version of FreeRDP. Non-release
versions of FreeRDP may have differences in behavior that are impossible to
check for at build time. This may result in memory leaks or other strange
behavior.
*** PLEASE USE A RELEASED VERSION OF FREERDP IF POSSIBLE ***
If you are ABSOLUTELY CERTAIN that building against this version of FreeRDP
is OK, rerun configure with the --enable-allow-freerdp-snapshots
--------------------------------------------])])
fi
# Variation in memory internal allocation/free behavior for bitmaps
if test "x${have_freerdp2}" = "xyes" if test "x${have_freerdp2}" = "xyes"
then then
@ -747,29 +629,6 @@ then
fi fi
# Variation in memory internal allocation/free behavior for channel streams
if test "x${have_freerdp2}" = "xyes"
then
# FreeRDP 2.0.0-rc3 through 2.0.0-rc4 automatically free the wStream
# provided to pVirtualChannelWriteEx(). This changed in commit 1b78b59, and
# implementations must manually free the wStream after it is no longer
# needed (after the write is complete or cancelled).
AC_MSG_CHECKING([whether pVirtualChannelWriteEx() frees the wStream upon completion])
AC_EGREP_CPP([\"2\\.0\\.0-(rc|dev)[3-4]\"], [
#include <freerdp/version.h>
FREERDP_VERSION_FULL
],
[AC_MSG_RESULT([yes])]
[AC_DEFINE([FREERDP_SVC_CORE_FREES_WSTREAM],,
[Whether pVirtualChannelWriteEx() frees the wStream upon completion])],
[AC_MSG_RESULT([no])])
fi
# Glyph callback variants # Glyph callback variants
if test "x${have_freerdp2}" = "xyes" if test "x${have_freerdp2}" = "xyes"
then then
@ -878,14 +737,6 @@ then
[[#include <freerdp/freerdp.h>]]) [[#include <freerdp/freerdp.h>]])
fi fi
# Updated certificate verification callback (introduced with 2.0.0, not present
# in 2.0.0-rc4 or earlier)
if test "x${have_freerdp2}" = "xyes"
then
AC_CHECK_MEMBERS([freerdp.VerifyCertificateEx],,,
[[#include <freerdp/freerdp.h>]])
fi
# Restore CPPFLAGS, removing FreeRDP-specific options needed for testing # Restore CPPFLAGS, removing FreeRDP-specific options needed for testing
CPPFLAGS="$OLDCPPFLAGS" CPPFLAGS="$OLDCPPFLAGS"
@ -913,7 +764,7 @@ if test "x$with_ssh" != "xno"
then then
have_libssh2=yes have_libssh2=yes
AC_CHECK_LIB([ssh2], [libssh2_userauth_publickey_frommemory], AC_CHECK_LIB([ssh2], [libssh2_session_init_ex],
[SSH_LIBS="$SSH_LIBS -lssh2"], [SSH_LIBS="$SSH_LIBS -lssh2"],
[have_libssh2=no]) [have_libssh2=no])
fi fi
@ -1144,11 +995,10 @@ AC_ARG_ENABLE([guacenc],
[], [],
[enable_guacenc=yes]) [enable_guacenc=yes])
AM_CONDITIONAL([ENABLE_GUACENC], [test "x${enable_guacenc}" = "xyes" \ AM_CONDITIONAL([ENABLE_GUACENC], [test "x${enable_guacenc}" = "xyes" \
-a "x${have_libavcodec}" = "xyes" \ -a "x${have_libavcodec}" = "xyes" \
-a "x${have_libavutil}" = "xyes" \ -a "x${have_libavutil}" = "xyes" \
-a "x${have_libswscale}" = "xyes" \ -a "x${have_libswscale}" = "xyes"])
-a "x${have_libavformat}" = "xyes"])
# #
# guaclog # guaclog
@ -1167,8 +1017,7 @@ AM_CONDITIONAL([ENABLE_GUACLOG], [test "x${enable_guaclog}" = "xyes"])
# #
AC_CONFIG_FILES([Makefile AC_CONFIG_FILES([Makefile
doc/libguac/Doxyfile doc/Doxyfile
doc/libguac-terminal/Doxyfile
src/common/Makefile src/common/Makefile
src/common/tests/Makefile src/common/tests/Makefile
src/common-ssh/Makefile src/common-ssh/Makefile
@ -1185,7 +1034,6 @@ AC_CONFIG_FILES([Makefile
src/guaclog/man/guaclog.1 src/guaclog/man/guaclog.1
src/pulse/Makefile src/pulse/Makefile
src/protocols/kubernetes/Makefile src/protocols/kubernetes/Makefile
src/protocols/kubernetes/tests/Makefile
src/protocols/rdp/Makefile src/protocols/rdp/Makefile
src/protocols/rdp/tests/Makefile src/protocols/rdp/tests/Makefile
src/protocols/ssh/Makefile src/protocols/ssh/Makefile
@ -1243,7 +1091,6 @@ $PACKAGE_NAME version $PACKAGE_VERSION
freerdp2 ............ ${have_freerdp2} freerdp2 ............ ${have_freerdp2}
pango ............... ${have_pango} pango ............... ${have_pango}
libavcodec .......... ${have_libavcodec} libavcodec .......... ${have_libavcodec}
libavformat.......... ${have_libavformat}
libavutil ........... ${have_libavutil} libavutil ........... ${have_libavutil}
libssh2 ............. ${have_libssh2} libssh2 ............. ${have_libssh2}
libssl .............. ${have_ssl} libssl .............. ${have_ssl}

View File

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

View File

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

View File

@ -0,0 +1,61 @@
/*
* 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,27 +25,75 @@
#include <guacamole/client.h> #include <guacamole/client.h>
#include <libssh2.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 base64-encoded prefix indicating an OpenSSH v1 private key is NOT protected by a * The expected header of RSA private keys.
* 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 OPENSSH_V1_UNENCRYPTED_KEY "AAAABG5vbmUAAAAEbm9uZQAAAAAAAAAB" #define SSH_RSA_KEY_HEADER "-----BEGIN RSA PRIVATE KEY-----"
/**
* The expected header of DSA private keys.
*/
#define SSH_DSA_KEY_HEADER "-----BEGIN DSA PRIVATE KEY-----"
/**
* The size of single number within a DSA signature, in bytes.
*/
#define DSA_SIG_NUMBER_SIZE 20
/**
* The size of a DSA signature, in bytes.
*/
#define DSA_SIG_SIZE DSA_SIG_NUMBER_SIZE*2
/**
* The type of an SSH key.
*/
typedef enum guac_common_ssh_key_type {
/**
* RSA key.
*/
SSH_KEY_RSA,
/**
* DSA key.
*/
SSH_KEY_DSA
} guac_common_ssh_key_type;
/** /**
* Abstraction of a key used for SSH authentication. * Abstraction of a key used for SSH authentication.
*/ */
typedef struct guac_common_ssh_key { 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. * The private key, encoded as necessary for SSH.
*/ */
@ -56,11 +104,6 @@ typedef struct guac_common_ssh_key {
*/ */
int private_key_length; int private_key_length;
/**
* The private key's passphrase, if any.
*/
char *passphrase;
} guac_common_ssh_key; } guac_common_ssh_key;
/** /**
@ -101,6 +144,31 @@ const char* guac_common_ssh_key_error();
*/ */
void guac_common_ssh_key_free(guac_common_ssh_key* key); 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 * 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 * one or more known_hosts entries. The known_host entries can either be a

View File

@ -17,17 +17,24 @@
* under the License. * under the License.
*/ */
#ifndef GUAC_FIPS_H #ifndef GUAC_COMMON_SSH_RSA_COMPAT_H
#define GUAC_FIPS_H #define GUAC_COMMON_SSH_RSA_COMPAT_H
#include "config.h"
#include <openssl/bn.h>
#include <openssl/rsa.h>
#ifndef HAVE_RSA_GET0_KEY
/** /**
* Returns a non-zero value if FIPS mode is enabled, or zero if FIPS mode * RSA_get0_key() implementation for versions of OpenSSL which lack this
* is not enabled. * function (pre 1.1).
* *
* @return * See: https://www.openssl.org/docs/man1.1.0/crypto/RSA_get0_key.html
* A non-zero value if FIPS mode is enabled, or zero if FIPS mode is
* not enabled.
*/ */
int guac_fips_enabled(); void RSA_get0_key(const RSA* rsa_key, const BIGNUM** n,
const BIGNUM** e, const BIGNUM**d);
#endif
#endif
#endif

View File

@ -69,16 +69,6 @@ typedef struct guac_common_ssh_sftp_filesystem {
* instruction. * instruction.
*/ */
char upload_path[GUAC_COMMON_SSH_SFTP_MAX_PATH]; char upload_path[GUAC_COMMON_SSH_SFTP_MAX_PATH];
/**
* If downloads from SFTP to the local browser should be disabled.
*/
int disable_download;
/**
* If uploads from the local browser to SFTP should be disabled.
*/
int disable_upload;
} guac_common_ssh_sftp_filesystem; } guac_common_ssh_sftp_filesystem;
@ -132,20 +122,13 @@ typedef struct guac_common_ssh_sftp_ls_state {
* The name to send as the name of the filesystem whenever it is exposed * The name to send as the name of the filesystem whenever it is exposed
* to a user, or NULL to automatically generate a name from the provided * to a user, or NULL to automatically generate a name from the provided
* root_path. * root_path.
*
* @param disable_download
* Whether downloads from the SFTP share to the local browser should be
* disabled.
*
* @param disable_upload
* Whether uploads from the local browser to SFTP should be disabled.
* *
* @return * @return
* A new SFTP filesystem object, not yet exposed to users. * A new SFTP filesystem object, not yet exposed to users.
*/ */
guac_common_ssh_sftp_filesystem* guac_common_ssh_create_sftp_filesystem( guac_common_ssh_sftp_filesystem* guac_common_ssh_create_sftp_filesystem(
guac_common_ssh_session* session, const char* root_path, guac_common_ssh_session* session, const char* root_path,
const char* name, int disable_download, int disable_upload); const char* name);
/** /**
* Destroys the given filesystem object, disconnecting from SFTP and freeing * Destroys the given filesystem object, disconnecting from SFTP and freeing

View File

@ -0,0 +1,59 @@
/*
* 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 "config.h"
#include "common-ssh/buffer.h" #include "common-ssh/buffer.h"
#include "common-ssh/dsa-compat.h"
#include "common-ssh/key.h" #include "common-ssh/key.h"
#include "common-ssh/rsa-compat.h"
#include <guacamole/string.h>
#include <openssl/bio.h> #include <openssl/bio.h>
#include <openssl/bn.h> #include <openssl/bn.h>
@ -33,118 +33,119 @@
#include <openssl/pem.h> #include <openssl/pem.h>
#include <openssl/rsa.h> #include <openssl/rsa.h>
#include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.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, guac_common_ssh_key* guac_common_ssh_key_alloc(char* data, int length,
char* passphrase) { char* passphrase) {
/* Because libssh2 will do the actual key parsing (to let it deal with guac_common_ssh_key* key;
* different key algorithms) we need to perform a heuristic here to check BIO* key_bio;
* 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.
*/
if (is_passphrase_needed(data, length) && (passphrase == NULL || *passphrase == '\0')) 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);
return NULL; return NULL;
}
guac_common_ssh_key* key = malloc(sizeof(guac_common_ssh_key));
/* Copy private key to structure */ /* Copy private key to structure */
key->private_key_length = length; key->private_key_length = length;
key->private_key = malloc(length); key->private_key = malloc(length);
memcpy(key->private_key, data, length); memcpy(key->private_key, data, length);
key->passphrase = strdup(passphrase);
BIO_free(key_bio);
return key; return key;
} }
@ -158,11 +159,93 @@ const char* guac_common_ssh_key_error() {
void guac_common_ssh_key_free(guac_common_ssh_key* key) { 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->private_key);
free(key->passphrase); free(key->public_key);
free(key); free(key);
} }
int guac_common_ssh_key_sign(guac_common_ssh_key* key, const char* data,
int length, unsigned char* sig) {
const EVP_MD* md;
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, 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 char* host_key, const char* hostname, int port, const char* remote_hostkey,
const size_t remote_hostkey_len) { const size_t remote_hostkey_len) {

View File

@ -17,15 +17,22 @@
* under the License. * under the License.
*/ */
#ifndef GUAC_COMMON_DEFAULTS_H #include "config.h"
#define GUAC_COMMON_DEFAULTS_H
/** #include <openssl/bn.h>
* The default number of seconds to wait after sending the Wake-on-LAN packet #include <openssl/rsa.h>
* for the destination host to start responding.
*/
#define GUAC_WOL_DEFAULT_BOOT_WAIT_TIME 0
#include <stdlib.h>
#endif /* GUAC_COMMON_DEFAULTS_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

View File

@ -376,18 +376,6 @@ int guac_common_ssh_sftp_handle_file_stream(
char fullpath[GUAC_COMMON_SSH_SFTP_MAX_PATH]; char fullpath[GUAC_COMMON_SSH_SFTP_MAX_PATH];
LIBSSH2_SFTP_HANDLE* file; LIBSSH2_SFTP_HANDLE* file;
/* Ignore upload if uploads have been disabled */
if (filesystem->disable_upload) {
guac_user_log(user, GUAC_LOG_WARNING, "A upload attempt has "
"been blocked due to uploads being disabled, however it "
"should have been blocked at a higher level. This is likely "
"a bug.");
guac_protocol_send_ack(user->socket, stream, "SFTP: Upload disabled",
GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
guac_socket_flush(user->socket);
return 0;
}
/* Concatenate filename with path */ /* Concatenate filename with path */
if (!guac_ssh_append_filename(fullpath, filesystem->upload_path, if (!guac_ssh_append_filename(fullpath, filesystem->upload_path,
filename)) { filename)) {
@ -441,7 +429,7 @@ int guac_common_ssh_sftp_handle_file_stream(
/** /**
* Handler for ack messages which continue an outbound SFTP data transfer * Handler for ack messages which continue an outbound SFTP data transfer
* (download), signaling the current status and requesting additional data. * (download), signalling the current status and requesting additional data.
* The data associated with the given stream is expected to be a pointer to an * The data associated with the given stream is expected to be a pointer to an
* open LIBSSH2_SFTP_HANDLE for the file from which the data is to be read. * open LIBSSH2_SFTP_HANDLE for the file from which the data is to be read.
* *
@ -528,15 +516,6 @@ guac_stream* guac_common_ssh_sftp_download_file(
guac_stream* stream; guac_stream* stream;
LIBSSH2_SFTP_HANDLE* file; LIBSSH2_SFTP_HANDLE* file;
/* Ignore download if downloads have been disabled */
if (filesystem->disable_download) {
guac_user_log(user, GUAC_LOG_WARNING, "A download attempt has "
"been blocked due to downloads being disabled, however it "
"should have been blocked at a higher level. This is likely "
"a bug.");
return NULL;
}
/* Attempt to open file for reading */ /* Attempt to open file for reading */
file = libssh2_sftp_open(filesystem->sftp_session, filename, file = libssh2_sftp_open(filesystem->sftp_session, filename,
LIBSSH2_FXF_READ, 0); LIBSSH2_FXF_READ, 0);
@ -807,14 +786,6 @@ static int guac_common_ssh_sftp_get_handler(guac_user* user,
/* Otherwise, send file contents */ /* Otherwise, send file contents */
else { else {
/* If downloads are disabled, log and return. */
if (filesystem->disable_download) {
guac_user_log(user, GUAC_LOG_INFO,
"Unable to download file \"%s\", "
"file downloads have been disabled.", fullpath);
return 0;
}
/* Open as normal file */ /* Open as normal file */
LIBSSH2_SFTP_HANDLE* file = libssh2_sftp_open(sftp, fullpath, LIBSSH2_SFTP_HANDLE* file = libssh2_sftp_open(sftp, fullpath,
LIBSSH2_FXF_READ, 0); LIBSSH2_FXF_READ, 0);
@ -871,18 +842,6 @@ static int guac_common_ssh_sftp_put_handler(guac_user* user,
guac_common_ssh_sftp_filesystem* filesystem = guac_common_ssh_sftp_filesystem* filesystem =
(guac_common_ssh_sftp_filesystem*) object->data; (guac_common_ssh_sftp_filesystem*) object->data;
/* Ignore upload if uploads have been disabled */
if (filesystem->disable_upload) {
guac_user_log(user, GUAC_LOG_WARNING, "A upload attempt has "
"been blocked due to uploads being disabled, however it "
"should have been blocked at a higher level. This is likely "
"a bug.");
guac_protocol_send_ack(user->socket, stream, "SFTP: Upload disabled",
GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
guac_socket_flush(user->socket);
return 0;
}
LIBSSH2_SFTP* sftp = filesystem->sftp_session; LIBSSH2_SFTP* sftp = filesystem->sftp_session;
/* Translate stream name into filesystem path */ /* Translate stream name into filesystem path */
@ -943,11 +902,7 @@ guac_object* guac_common_ssh_alloc_sftp_filesystem_object(
/* Init filesystem */ /* Init filesystem */
guac_object* fs_object = guac_user_alloc_object(user); guac_object* fs_object = guac_user_alloc_object(user);
fs_object->get_handler = guac_common_ssh_sftp_get_handler; fs_object->get_handler = guac_common_ssh_sftp_get_handler;
fs_object->put_handler = guac_common_ssh_sftp_put_handler;
/* Only handle uploads if not disabled. */
if (!filesystem->disable_upload)
fs_object->put_handler = guac_common_ssh_sftp_put_handler;
fs_object->data = filesystem; fs_object->data = filesystem;
/* Send filesystem to user */ /* Send filesystem to user */
@ -960,7 +915,7 @@ guac_object* guac_common_ssh_alloc_sftp_filesystem_object(
guac_common_ssh_sftp_filesystem* guac_common_ssh_create_sftp_filesystem( guac_common_ssh_sftp_filesystem* guac_common_ssh_create_sftp_filesystem(
guac_common_ssh_session* session, const char* root_path, guac_common_ssh_session* session, const char* root_path,
const char* name, int disable_download, int disable_upload) { const char* name) {
/* Request SFTP */ /* Request SFTP */
LIBSSH2_SFTP* sftp_session = libssh2_sftp_init(session->session); LIBSSH2_SFTP* sftp_session = libssh2_sftp_init(session->session);
@ -974,10 +929,6 @@ guac_common_ssh_sftp_filesystem* guac_common_ssh_create_sftp_filesystem(
/* Associate SSH session with SFTP data and user */ /* Associate SSH session with SFTP data and user */
filesystem->ssh_session = session; filesystem->ssh_session = session;
filesystem->sftp_session = sftp_session; filesystem->sftp_session = sftp_session;
/* Copy over disable flags */
filesystem->disable_download = disable_download;
filesystem->disable_upload = disable_upload;
/* Normalize and store the provided root path */ /* Normalize and store the provided root path */
if (!guac_common_ssh_sftp_normalize_path(filesystem->root_path, if (!guac_common_ssh_sftp_normalize_path(filesystem->root_path,

View File

@ -22,7 +22,6 @@
#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
@ -47,20 +46,6 @@
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.
@ -155,21 +140,11 @@ static void guac_common_ssh_openssl_free_locks(int count) {
int guac_common_ssh_init(guac_client* client) { int guac_common_ssh_init(guac_client* client) {
#ifdef LIBSSH2_USES_GCRYPT #ifdef LIBSSH2_USES_GCRYPT
/* Init threadsafety in libgcrypt */
if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) { gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
if (!gcry_check_version(GCRYPT_VERSION)) {
/* Init threadsafety in libgcrypt */ guac_client_log(client, GUAC_LOG_ERROR, "libgcrypt version mismatch.");
gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); return 1;
/* Initialize GCrypt */
if (!gcry_check_version(GCRYPT_VERSION)) {
guac_client_log(client, GUAC_LOG_ERROR, "libgcrypt version mismatch.");
return 1;
}
/* Mark initialization as completed. */
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
} }
#endif #endif
@ -180,11 +155,9 @@ 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
#if OPENSSL_VERSION_NUMBER < 0x10100000L /* Init OpenSSL */
/* 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);
@ -200,6 +173,55 @@ void guac_common_ssh_uninit() {
#endif #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 * Callback for the keyboard-interactive authentication method. Currently
* supports just one prompt for the password. This callback is invoked as * supports just one prompt for the password. This callback is invoked as
@ -292,9 +314,8 @@ static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session)
} }
/* Get list of supported authentication methods */ /* Get list of supported authentication methods */
size_t username_len = strlen(user->username);
char* user_authlist = libssh2_userauth_list(session, user->username, char* user_authlist = libssh2_userauth_list(session, user->username,
username_len); strlen(user->username));
/* If auth list is NULL, then authentication has succeeded with NONE */ /* If auth list is NULL, then authentication has succeeded with NONE */
if (user_authlist == NULL) { if (user_authlist == NULL) {
@ -318,9 +339,9 @@ static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session)
} }
/* Attempt public key auth */ /* Attempt public key auth */
if (libssh2_userauth_publickey_frommemory(session, user->username, if (libssh2_userauth_publickey(session, user->username,
username_len, NULL, 0, key->private_key, (unsigned char*) key->public_key, key->public_key_length,
key->private_key_length, key->passphrase)) { guac_common_ssh_sign_callback, (void**) key)) {
/* Abort on failure */ /* Abort on failure */
char* error_message; char* error_message;
@ -501,17 +522,6 @@ 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

@ -34,7 +34,6 @@ noinst_HEADERS = \
common/blank_cursor.h \ common/blank_cursor.h \
common/clipboard.h \ common/clipboard.h \
common/cursor.h \ common/cursor.h \
common/defaults.h \
common/display.h \ common/display.h \
common/dot_cursor.h \ common/dot_cursor.h \
common/ibar_cursor.h \ common/ibar_cursor.h \
@ -42,6 +41,7 @@ noinst_HEADERS = \
common/json.h \ common/json.h \
common/list.h \ common/list.h \
common/pointer_cursor.h \ common/pointer_cursor.h \
common/recording.h \
common/rect.h \ common/rect.h \
common/string.h \ common/string.h \
common/surface.h common/surface.h
@ -58,6 +58,7 @@ libguac_common_la_SOURCES = \
json.c \ json.c \
list.c \ list.c \
pointer_cursor.c \ pointer_cursor.c \
recording.c \
rect.c \ rect.c \
string.c \ string.c \
surface.c surface.c

View File

@ -29,15 +29,15 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
guac_common_clipboard* guac_common_clipboard_alloc() { guac_common_clipboard* guac_common_clipboard_alloc(int size) {
guac_common_clipboard* clipboard = malloc(sizeof(guac_common_clipboard)); guac_common_clipboard* clipboard = malloc(sizeof(guac_common_clipboard));
/* Init clipboard */ /* Init clipboard */
clipboard->mimetype[0] = '\0'; clipboard->mimetype[0] = '\0';
clipboard->buffer = malloc(GUAC_COMMON_CLIPBOARD_MAX_LENGTH); clipboard->buffer = malloc(size);
clipboard->available = GUAC_COMMON_CLIPBOARD_MAX_LENGTH;
clipboard->length = 0; clipboard->length = 0;
clipboard->available = size;
pthread_mutex_init(&(clipboard->lock), NULL); pthread_mutex_init(&(clipboard->lock), NULL);
@ -46,14 +46,7 @@ guac_common_clipboard* guac_common_clipboard_alloc() {
} }
void guac_common_clipboard_free(guac_common_clipboard* clipboard) { void guac_common_clipboard_free(guac_common_clipboard* clipboard) {
/* Destroy lock */
pthread_mutex_destroy(&(clipboard->lock));
/* Free buffer */
free(clipboard->buffer); free(clipboard->buffer);
/* Free base structure */
free(clipboard); free(clipboard);
} }

View File

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

View File

@ -99,13 +99,6 @@ typedef struct guac_common_display {
*/ */
guac_common_display_layer* buffers; guac_common_display_layer* buffers;
/**
* Non-zero if all graphical updates for this display should use lossless
* compression, 0 otherwise. By default, newly-created displays will use
* lossy compression when heuristics determine it is appropriate.
*/
int lossless;
/** /**
* Mutex which is locked internally when access to the display must be * Mutex which is locked internally when access to the display must be
* synchronized. All public functions of guac_common_display should be * synchronized. All public functions of guac_common_display should be
@ -235,27 +228,5 @@ void guac_common_display_free_layer(guac_common_display* display,
void guac_common_display_free_buffer(guac_common_display* display, void guac_common_display_free_buffer(guac_common_display* display,
guac_common_display_layer* display_buffer); guac_common_display_layer* display_buffer);
/**
* Sets the overall lossless compression policy of the given display to the
* given value, affecting all current and future layers/buffers maintained by
* the display. By default, newly-created displays will use lossy compression
* for graphical updates when heuristics determine that doing so is
* appropriate. Specifying a non-zero value here will force all graphical
* updates to always use lossless compression, whereas specifying zero will
* restore the default policy.
*
* Note that this can also be adjusted on a per-layer / per-buffer basis with
* guac_common_surface_set_lossless().
*
* @param display
* The display to modify.
*
* @param lossless
* Non-zero if all graphical updates for this display should use lossless
* compression, 0 otherwise.
*/
void guac_common_display_set_lossless(guac_common_display* display,
int lossless);
#endif #endif

View File

@ -76,30 +76,6 @@ guac_iconv_read GUAC_READ_CP1252;
*/ */
guac_iconv_read GUAC_READ_ISO8859_1; guac_iconv_read GUAC_READ_ISO8859_1;
/**
* Read function for UTF-8 which normalizes newline character sequences like
* "\r\n" to Unix-style newlines ('\n').
*/
guac_iconv_read GUAC_READ_UTF8_NORMALIZED;
/**
* Read function for UTF-16 which normalizes newline character sequences like
* "\r\n" to Unix-style newlines ('\n').
*/
guac_iconv_read GUAC_READ_UTF16_NORMALIZED;
/**
* Read function for CP-1252 which normalizes newline character sequences like
* "\r\n" to Unix-style newlines ('\n').
*/
guac_iconv_read GUAC_READ_CP1252_NORMALIZED;
/**
* Read function for ISO 8859-1 which normalizes newline character sequences
* like "\r\n" to Unix-style newlines ('\n').
*/
guac_iconv_read GUAC_READ_ISO8859_1_NORMALIZED;
/** /**
* Write function for UTF8. * Write function for UTF8.
*/ */
@ -120,29 +96,5 @@ guac_iconv_write GUAC_WRITE_CP1252;
*/ */
guac_iconv_write GUAC_WRITE_ISO8859_1; guac_iconv_write GUAC_WRITE_ISO8859_1;
/**
* Write function for UTF-8 which writes newline characters ('\n') as
* Windows-style newlines ("\r\n").
*/
guac_iconv_write GUAC_WRITE_UTF8_CRLF;
/**
* Write function for UTF-16 which writes newline characters ('\n') as
* Windows-style newlines ("\r\n").
*/
guac_iconv_write GUAC_WRITE_UTF16_CRLF;
/**
* Write function for CP-1252 which writes newline characters ('\n') as
* Windows-style newlines ("\r\n").
*/
guac_iconv_write GUAC_WRITE_CP1252_CRLF;
/**
* Write function for ISO 8859-1 which writes newline characters ('\n') as
* Windows-style newlines ("\r\n").
*/
guac_iconv_write GUAC_WRITE_ISO8859_1_CRLF;
#endif #endif

View File

@ -17,17 +17,11 @@
* under the License. * under the License.
*/ */
#ifndef GUAC_RECORDING_H #ifndef GUAC_COMMON_RECORDING_H
#define GUAC_RECORDING_H #define GUAC_COMMON_RECORDING_H
#include <guacamole/client.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 * 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 * to the end of the session recording filename if a recording having the
@ -53,7 +47,7 @@
* that output Guacamole instructions may be dynamically intercepted and * that output Guacamole instructions may be dynamically intercepted and
* written to a file. * written to a file.
*/ */
typedef struct guac_recording { typedef struct guac_common_recording {
/** /**
* The guac_socket which writes directly to the recording file, rather than * The guac_socket which writes directly to the recording file, rather than
@ -77,15 +71,6 @@ typedef struct guac_recording {
*/ */
int include_mouse; int include_mouse;
/**
* Non-zero if multi-touch events should be included in the session
* recording, zero otherwise. Depending on whether the remote desktop will
* automatically provide graphical feedback for touches, including touch
* events may be necessary for multi-touch interactions to be rendered in
* any resulting video.
*/
int include_touch;
/** /**
* Non-zero if keys pressed and released should be included in the session * Non-zero if keys pressed and released should be included in the session
* recording, zero otherwise. Including key events within the recording may * recording, zero otherwise. Including key events within the recording may
@ -95,7 +80,7 @@ typedef struct guac_recording {
*/ */
int include_keys; int include_keys;
} guac_recording; } guac_common_recording;
/** /**
* Replaces the socket of the given client such that all further Guacamole * Replaces the socket of the given client such that all further Guacamole
@ -134,13 +119,6 @@ typedef struct guac_recording {
* otherwise. Including mouse state is necessary for the mouse cursor to be * otherwise. Including mouse state is necessary for the mouse cursor to be
* rendered in any resulting video. * rendered in any resulting video.
* *
* @param include_touch
* Non-zero if touch events should be included in the session recording,
* zero otherwise. Depending on whether the remote desktop will
* automatically provide graphical feedback for touches, including touch
* events may be necessary for multi-touch interactions to be rendered in
* any resulting video.
*
* @param include_keys * @param include_keys
* Non-zero if keys pressed and released should be included in the session * Non-zero if keys pressed and released should be included in the session
* recording, zero otherwise. Including key events within the recording may * recording, zero otherwise. Including key events within the recording may
@ -149,14 +127,13 @@ typedef struct guac_recording {
* passwords, credit card numbers, etc. * passwords, credit card numbers, etc.
* *
* @return * @return
* A new guac_recording structure representing the in-progress * A new guac_common_recording structure representing the in-progress
* recording if the recording file has been successfully created and a * recording if the recording file has been successfully created and a
* recording will be written, NULL otherwise. * recording will be written, NULL otherwise.
*/ */
guac_recording* guac_recording_create(guac_client* client, guac_common_recording* guac_common_recording_create(guac_client* client,
const char* path, const char* name, int create_path, const char* path, const char* name, int create_path,
int include_output, int include_mouse, int include_touch, int include_output, int include_mouse, int include_keys);
int include_keys);
/** /**
* Frees the resources associated with the given in-progress recording. Note * Frees the resources associated with the given in-progress recording. Note
@ -165,15 +142,15 @@ guac_recording* guac_recording_create(guac_client* client,
* freed when the guac_client is freed. * freed when the guac_client is freed.
* *
* @param recording * @param recording
* The guac_recording to free. * The guac_common_recording to free.
*/ */
void guac_recording_free(guac_recording* recording); void guac_common_recording_free(guac_common_recording* recording);
/** /**
* Reports the current mouse position and button state within the recording. * Reports the current mouse position and button state within the recording.
* *
* @param recording * @param recording
* The guac_recording associated with the mouse that has moved. * The guac_common_recording associated with the mouse that has moved.
* *
* @param x * @param x
* The new X coordinate of the mouse cursor, in pixels. * The new X coordinate of the mouse cursor, in pixels.
@ -194,52 +171,14 @@ void guac_recording_free(guac_recording* recording);
* @see GUAC_CLIENT_MOUSE_SCROLL_UP * @see GUAC_CLIENT_MOUSE_SCROLL_UP
* @see GUAC_CLIENT_MOUSE_SCROLL_DOWN * @see GUAC_CLIENT_MOUSE_SCROLL_DOWN
*/ */
void guac_recording_report_mouse(guac_recording* recording, void guac_common_recording_report_mouse(guac_common_recording* recording,
int x, int y, int button_mask); int x, int y, int button_mask);
/**
* Reports the current state of a touch contact within the recording.
*
* @param recording
* The guac_recording associated with the touch contact that
* has changed state.
*
* @param id
* An arbitrary integer ID which uniquely identifies this contact relative
* to other active contacts.
*
* @param x
* The X coordinate of the center of the touch contact.
*
* @param y
* The Y coordinate of the center of the touch contact.
*
* @param x_radius
* The X radius of the ellipse covering the general area of the touch
* contact, in pixels.
*
* @param y_radius
* The Y radius of the ellipse covering the general area of the touch
* contact, in pixels.
*
* @param angle
* The rough angle of clockwise rotation of the general area of the touch
* contact, in degrees.
*
* @param force
* The relative force exerted by the touch contact, where 0 is no force
* (the touch has been lifted) and 1 is maximum force (the maximum amount
* of force representable by the device).
*/
void guac_recording_report_touch(guac_recording* recording,
int id, int x, int y, int x_radius, int y_radius,
double angle, double force);
/** /**
* Reports a change in the state of an individual key within the recording. * Reports a change in the state of an individual key within the recording.
* *
* @param recording * @param recording
* The guac_recording associated with the key that was pressed or * The guac_common_recording associated with the key that was pressed or
* released. * released.
* *
* @param keysym * @param keysym
@ -249,7 +188,7 @@ void guac_recording_report_touch(guac_recording* recording,
* Non-zero if the key represented by the given keysym is currently * Non-zero if the key represented by the given keysym is currently
* pressed, zero if it is released. * pressed, zero if it is released.
*/ */
void guac_recording_report_key(guac_recording* recording, void guac_common_recording_report_key(guac_common_recording* recording,
int keysym, int pressed); int keysym, int pressed);
#endif #endif

View File

@ -120,19 +120,6 @@ typedef struct guac_common_surface {
*/ */
guac_socket* socket; guac_socket* socket;
/**
* The number of simultaneous touches that this surface can accept, where 0
* indicates that the surface does not support touch events at all.
*/
int touches;
/**
* Non-zero if all graphical updates for this surface should use lossless
* compression, 0 otherwise. By default, newly-created surfaces will use
* lossy compression when heuristics determine it is appropriate.
*/
int lossless;
/** /**
* The X coordinate of the upper-left corner of this layer, in pixels, * The X coordinate of the upper-left corner of this layer, in pixels,
* relative to its parent layer. This is only applicable to visible * relative to its parent layer. This is only applicable to visible
@ -499,41 +486,5 @@ void guac_common_surface_flush(guac_common_surface* surface);
void guac_common_surface_dup(guac_common_surface* surface, guac_user* user, void guac_common_surface_dup(guac_common_surface* surface, guac_user* user,
guac_socket* socket); guac_socket* socket);
/**
* Declares that the given surface should receive touch events. By default,
* surfaces are assumed to not expect touch events. This value is advisory, and
* the client is not required to honor the declared level of touch support.
* Implementations are expected to safely handle or ignore any received touch
* events, regardless of the level of touch support declared. regardless of
* the level of touch support declared.
*
* @param surface
* The surface to modify.
*
* @param touches
* The number of simultaneous touches that this surface can accept, where 0
* indicates that the surface does not support touch events at all.
*/
void guac_common_surface_set_multitouch(guac_common_surface* surface,
int touches);
/**
* Sets the lossless compression policy of the given surface to the given
* value. By default, newly-created surfaces will use lossy compression for
* graphical updates when heuristics determine that doing so is appropriate.
* Specifying a non-zero value here will force all graphical updates to always
* use lossless compression, whereas specifying zero will restore the default
* policy.
*
* @param surface
* The surface to modify.
*
* @param lossless
* Non-zero if all graphical updates for this surface should use lossless
* compression, 0 otherwise.
*/
void guac_common_surface_set_lossless(guac_common_surface* surface,
int lossless);
#endif #endif

View File

@ -166,8 +166,6 @@ 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 */
@ -180,33 +178,6 @@ 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);
}
void guac_common_display_set_lossless(guac_common_display* display,
int lossless) {
pthread_mutex_lock(&display->_lock);
/* Update lossless setting to be applied to all newly-allocated
* layers/buffers */
display->lossless = lossless;
/* Update losslessness of all allocated layers/buffers */
guac_common_display_layer* current = display->layers;
while (current != NULL) {
guac_common_surface_set_lossless(current->surface, lossless);
current = current->next;
}
/* Update losslessness of default display layer (not included within layers
* list) */
guac_common_surface_set_lossless(display->default_surface, lossless);
pthread_mutex_unlock(&display->_lock); pthread_mutex_unlock(&display->_lock);
} }
@ -316,9 +287,6 @@ guac_common_display_layer* guac_common_display_alloc_layer(
guac_common_surface* surface = guac_common_surface_alloc(display->client, guac_common_surface* surface = guac_common_surface_alloc(display->client,
display->client->socket, layer, width, height); display->client->socket, layer, width, height);
/* Apply current display losslessness */
guac_common_surface_set_lossless(surface, display->lossless);
/* Add layer and surface to list */ /* Add layer and surface to list */
guac_common_display_layer* display_layer = guac_common_display_layer* display_layer =
guac_common_display_add_layer(&display->layers, layer, surface); guac_common_display_add_layer(&display->layers, layer, surface);
@ -340,9 +308,6 @@ guac_common_display_layer* guac_common_display_alloc_buffer(
guac_common_surface* surface = guac_common_surface_alloc(display->client, guac_common_surface* surface = guac_common_surface_alloc(display->client,
display->client->socket, buffer, width, height); display->client->socket, buffer, width, height);
/* Apply current display losslessness */
guac_common_surface_set_lossless(surface, display->lossless);
/* Add buffer and surface to list */ /* Add buffer and surface to list */
guac_common_display_layer* display_layer = guac_common_display_layer* display_layer =
guac_common_display_add_layer(&display->buffers, buffer, surface); guac_common_display_add_layer(&display->buffers, buffer, surface);
@ -389,3 +354,4 @@ void guac_common_display_free_buffer(guac_common_display* display,
pthread_mutex_unlock(&display->_lock); pthread_mutex_unlock(&display->_lock);
} }

View File

@ -138,70 +138,6 @@ int GUAC_READ_ISO8859_1(const char** input, int remaining) {
} }
/**
* Invokes the given reader function, automatically normalizing newline
* sequences as Unix-style newline characters ('\n'). All other charaters are
* read verbatim.
*
* @param reader
* The reader to use to read the given character.
*
* @param input
* Pointer to the location within the input buffer that the next character
* should be read from.
*
* @param remaining
* The number of bytes remaining in the input buffer.
*
* @return
* The codepoint that was read, or zero if the end of the input string has
* been reached.
*/
static int guac_iconv_read_normalized(guac_iconv_read* reader,
const char** input, int remaining) {
/* Read requested character */
const char* input_start = *input;
int value = reader(input, remaining);
/* Automatically translate CRLF pairs to simple newlines */
if (value == '\r') {
/* Peek ahead by one character, adjusting remaining bytes relative to
* last read */
int peek_remaining = remaining - (*input - input_start);
const char* peek_input = *input;
int peek_value = reader(&peek_input, peek_remaining);
/* Consider read value to be a newline if we have encountered a "\r\n"
* (CRLF) pair */
if (peek_value == '\n') {
value = '\n';
*input = peek_input;
}
}
return value;
}
int GUAC_READ_UTF8_NORMALIZED(const char** input, int remaining) {
return guac_iconv_read_normalized(GUAC_READ_UTF8, input, remaining);
}
int GUAC_READ_UTF16_NORMALIZED(const char** input, int remaining) {
return guac_iconv_read_normalized(GUAC_READ_UTF16, input, remaining);
}
int GUAC_READ_CP1252_NORMALIZED(const char** input, int remaining) {
return guac_iconv_read_normalized(GUAC_READ_CP1252, input, remaining);
}
int GUAC_READ_ISO8859_1_NORMALIZED(const char** input, int remaining) {
return guac_iconv_read_normalized(GUAC_READ_ISO8859_1, input, remaining);
}
void GUAC_WRITE_UTF8(char** output, int remaining, int value) { void GUAC_WRITE_UTF8(char** output, int remaining, int value) {
*output += guac_utf8_write(value, *output, remaining); *output += guac_utf8_write(value, *output, remaining);
} }
@ -254,53 +190,3 @@ void GUAC_WRITE_ISO8859_1(char** output, int remaining, int value) {
(*output)++; (*output)++;
} }
/**
* Invokes the given writer function, automatically writing newline characters
* ('\n') as CRLF ("\r\n"). All other charaters are written verbatim.
*
* @param writer
* The writer to use to write the given character.
*
* @param output
* Pointer to the location within the output buffer that the next character
* should be written.
*
* @param remaining
* The number of bytes remaining in the output buffer.
*
* @param value
* The codepoint of the character to write.
*/
static void guac_iconv_write_crlf(guac_iconv_write* writer, char** output,
int remaining, int value) {
if (value != '\n') {
writer(output, remaining, value);
return;
}
char* output_start = *output;
writer(output, remaining, '\r');
remaining -= *output - output_start;
if (remaining > 0)
writer(output, remaining, '\n');
}
void GUAC_WRITE_UTF8_CRLF(char** output, int remaining, int value) {
guac_iconv_write_crlf(GUAC_WRITE_UTF8, output, remaining, value);
}
void GUAC_WRITE_UTF16_CRLF(char** output, int remaining, int value) {
guac_iconv_write_crlf(GUAC_WRITE_UTF16, output, remaining, value);
}
void GUAC_WRITE_CP1252_CRLF(char** output, int remaining, int value) {
guac_iconv_write_crlf(GUAC_WRITE_CP1252, output, remaining, value);
}
void GUAC_WRITE_ISO8859_1_CRLF(char** output, int remaining, int value) {
guac_iconv_write_crlf(GUAC_WRITE_ISO8859_1, output, remaining, value);
}

View File

@ -97,15 +97,15 @@ int guac_common_json_write_string(guac_user* user,
const char* current = str; const char* current = str;
for (; *current != '\0'; current++) { for (; *current != '\0'; current++) {
/* Escape all quotes and back-slashes */ /* Escape all quotes */
if (*current == '"' || *current == '\\') { if (*current == '"') {
/* Write any string content up to current character */ /* Write any string content up to current character */
if (current != str) if (current != str)
blob_written |= guac_common_json_write(user, stream, blob_written |= guac_common_json_write(user, stream,
json_state, str, current - str); json_state, str, current - str);
/* Escape the character that was just read */ /* Escape the quote that was just read */
blob_written |= guac_common_json_write(user, stream, blob_written |= guac_common_json_write(user, stream,
json_state, "\\", 1); json_state, "\\", 1);

View File

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

View File

@ -103,28 +103,6 @@
*/ */
#define GUAC_SURFACE_WEBP_BLOCK_SIZE 8 #define GUAC_SURFACE_WEBP_BLOCK_SIZE 8
void guac_common_surface_set_multitouch(guac_common_surface* surface,
int touches) {
pthread_mutex_lock(&surface->_lock);
surface->touches = touches;
guac_protocol_send_set_int(surface->socket, surface->layer,
GUAC_PROTOCOL_LAYER_PARAMETER_MULTI_TOUCH, touches);
pthread_mutex_unlock(&surface->_lock);
}
void guac_common_surface_set_lossless(guac_common_surface* surface,
int lossless) {
pthread_mutex_lock(&surface->_lock);
surface->lossless = lossless;
pthread_mutex_unlock(&surface->_lock);
}
void guac_common_surface_move(guac_common_surface* surface, int x, int y) { void guac_common_surface_move(guac_common_surface* surface, int x, int y) {
pthread_mutex_lock(&surface->_lock); pthread_mutex_lock(&surface->_lock);
@ -543,10 +521,6 @@ static int __guac_common_surface_png_optimality(guac_common_surface* surface,
static int __guac_common_surface_should_use_jpeg(guac_common_surface* surface, static int __guac_common_surface_should_use_jpeg(guac_common_surface* surface,
const guac_common_rect* rect) { const guac_common_rect* rect) {
/* Do not use JPEG if lossless quality is required */
if (surface->lossless)
return 0;
/* Calculate the average framerate for the given rect */ /* Calculate the average framerate for the given rect */
int framerate = __guac_common_surface_calculate_framerate(surface, rect); int framerate = __guac_common_surface_calculate_framerate(surface, rect);
@ -1245,10 +1219,14 @@ guac_common_surface* guac_common_surface_alloc(guac_client* client,
/* Create corresponding Cairo surface */ /* Create corresponding Cairo surface */
surface->stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, w); surface->stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, w);
surface->buffer = calloc(h, surface->stride); surface->buffer = calloc(h, surface->stride);
if (surface->buffer == NULL)
return NULL;
/* Create corresponding heat map */ /* Create corresponding heat map */
surface->heat_map = calloc(heat_width * heat_height, surface->heat_map = calloc(heat_width * heat_height,
sizeof(guac_common_surface_heat_cell)); sizeof(guac_common_surface_heat_cell));
if (surface->heat_map == NULL)
return NULL;
/* Reset clipping rect */ /* Reset clipping rect */
guac_common_surface_reset_clip(surface); guac_common_surface_reset_clip(surface);
@ -1312,6 +1290,8 @@ void guac_common_surface_resize(guac_common_surface* surface, int w, int h) {
surface->height = h; surface->height = h;
surface->stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, w); surface->stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, w);
surface->buffer = calloc(h, surface->stride); surface->buffer = calloc(h, surface->stride);
if (surface->buffer == NULL)
return NULL;
__guac_common_bound_rect(surface, &surface->clip_rect, NULL, NULL); __guac_common_bound_rect(surface, &surface->clip_rect, NULL, NULL);
/* Copy relevant old data */ /* Copy relevant old data */
@ -1325,6 +1305,8 @@ void guac_common_surface_resize(guac_common_surface* surface, int w, int h) {
free(surface->heat_map); free(surface->heat_map);
surface->heat_map = calloc(heat_width * heat_height, surface->heat_map = calloc(heat_width * heat_height,
sizeof(guac_common_surface_heat_cell)); sizeof(guac_common_surface_heat_cell));
if (surface->heat_map == NULL)
return NULL;
/* Resize dirty rect to fit new surface dimensions */ /* Resize dirty rect to fit new surface dimensions */
if (surface->dirty) { if (surface->dirty) {
@ -1819,8 +1801,7 @@ static void __guac_common_surface_flush_to_webp(guac_common_surface* surface,
/* Send WebP for rect */ /* Send WebP for rect */
guac_client_stream_webp(surface->client, socket, GUAC_COMP_OVER, layer, guac_client_stream_webp(surface->client, socket, GUAC_COMP_OVER, layer,
surface->dirty_rect.x, surface->dirty_rect.y, rect, surface->dirty_rect.x, surface->dirty_rect.y, rect,
guac_common_surface_suggest_quality(surface->client), guac_common_surface_suggest_quality(surface->client), 0);
surface->lossless ? 1 : 0);
cairo_surface_destroy(rect); cairo_surface_destroy(rect);
surface->realized = 1; surface->realized = 1;
@ -2008,11 +1989,6 @@ void guac_common_surface_dup(guac_common_surface* surface, guac_user* user,
guac_protocol_send_move(socket, surface->layer, guac_protocol_send_move(socket, surface->layer,
surface->parent, surface->x, surface->y, surface->z); surface->parent, surface->x, surface->y, surface->z);
/* Synchronize multi-touch support level */
guac_protocol_send_set_int(surface->socket, surface->layer,
GUAC_PROTOCOL_LAYER_PARAMETER_MULTI_TOUCH,
surface->touches);
} }
/* Sync size to new socket */ /* Sync size to new socket */

View File

@ -33,12 +33,8 @@ ACLOCAL_AMFLAGS = -I m4
check_PROGRAMS = test_common check_PROGRAMS = test_common
TESTS = $(check_PROGRAMS) TESTS = $(check_PROGRAMS)
noinst_HEADERS = \
iconv/convert-test-data.h
test_common_SOURCES = \ test_common_SOURCES = \
iconv/convert.c \ iconv/convert.c \
iconv/convert-test-data.c \
rect/clip_and_split.c \ rect/clip_and_split.c \
rect/constrain.c \ rect/constrain.c \
rect/expand_to_grid.c \ rect/expand_to_grid.c \

View File

@ -1,153 +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 "common/iconv.h"
#include "convert-test-data.h"
encoding_test_parameters test_params[NUM_SUPPORTED_ENCODINGS] = {
/*
* UTF-8
*/
{
"UTF-8",
GUAC_READ_UTF8, GUAC_READ_UTF8_NORMALIZED,
GUAC_WRITE_UTF8, GUAC_WRITE_UTF8_CRLF,
.test_mixed = TEST_STRING(
"pap\xC3\xA0 \xC3\xA8 bello\n"
"pap\xC3\xA0 \xC3\xA8 bello\r\n"
"pap\xC3\xA0 \xC3\xA8 bello\n"
"pap\xC3\xA0 \xC3\xA8 bello\r\n"
"pap\xC3\xA0 \xC3\xA8 bello"
),
.test_unix = TEST_STRING(
"pap\xC3\xA0 \xC3\xA8 bello\n"
"pap\xC3\xA0 \xC3\xA8 bello\n"
"pap\xC3\xA0 \xC3\xA8 bello\n"
"pap\xC3\xA0 \xC3\xA8 bello\n"
"pap\xC3\xA0 \xC3\xA8 bello"
),
.test_windows = TEST_STRING(
"pap\xC3\xA0 \xC3\xA8 bello\r\n"
"pap\xC3\xA0 \xC3\xA8 bello\r\n"
"pap\xC3\xA0 \xC3\xA8 bello\r\n"
"pap\xC3\xA0 \xC3\xA8 bello\r\n"
"pap\xC3\xA0 \xC3\xA8 bello"
)
},
/*
* UTF-16
*/
{
"UTF-16",
GUAC_READ_UTF16, GUAC_READ_UTF16_NORMALIZED,
GUAC_WRITE_UTF16, GUAC_WRITE_UTF16_CRLF,
.test_mixed = TEST_STRING(
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00" "\n\x00"
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00" "\r\x00" "\n\x00"
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00" "\n\x00"
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00" "\r\x00" "\n\x00"
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00"
"\x00"
),
.test_unix = TEST_STRING(
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00" "\n\x00"
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00" "\n\x00"
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00" "\n\x00"
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00" "\n\x00"
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00"
"\x00"
),
.test_windows = TEST_STRING(
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00" "\r\x00" "\n\x00"
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00" "\r\x00" "\n\x00"
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00" "\r\x00" "\n\x00"
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00" "\r\x00" "\n\x00"
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00"
"\x00"
)
},
/*
* ISO 8859-1
*/
{
"ISO 8859-1",
GUAC_READ_ISO8859_1, GUAC_READ_ISO8859_1_NORMALIZED,
GUAC_WRITE_ISO8859_1, GUAC_WRITE_ISO8859_1_CRLF,
.test_mixed = TEST_STRING(
"pap\xE0 \xE8 bello\n"
"pap\xE0 \xE8 bello\r\n"
"pap\xE0 \xE8 bello\n"
"pap\xE0 \xE8 bello\r\n"
"pap\xE0 \xE8 bello"
),
.test_unix = TEST_STRING(
"pap\xE0 \xE8 bello\n"
"pap\xE0 \xE8 bello\n"
"pap\xE0 \xE8 bello\n"
"pap\xE0 \xE8 bello\n"
"pap\xE0 \xE8 bello"
),
.test_windows = TEST_STRING(
"pap\xE0 \xE8 bello\r\n"
"pap\xE0 \xE8 bello\r\n"
"pap\xE0 \xE8 bello\r\n"
"pap\xE0 \xE8 bello\r\n"
"pap\xE0 \xE8 bello"
)
},
/*
* CP-1252
*/
{
"CP-1252",
GUAC_READ_CP1252, GUAC_READ_CP1252_NORMALIZED,
GUAC_WRITE_CP1252, GUAC_WRITE_CP1252_CRLF,
.test_mixed = TEST_STRING(
"pap\xE0 \xE8 bello\n"
"pap\xE0 \xE8 bello\r\n"
"pap\xE0 \xE8 bello\n"
"pap\xE0 \xE8 bello\r\n"
"pap\xE0 \xE8 bello"
),
.test_unix = TEST_STRING(
"pap\xE0 \xE8 bello\n"
"pap\xE0 \xE8 bello\n"
"pap\xE0 \xE8 bello\n"
"pap\xE0 \xE8 bello\n"
"pap\xE0 \xE8 bello"
),
.test_windows = TEST_STRING(
"pap\xE0 \xE8 bello\r\n"
"pap\xE0 \xE8 bello\r\n"
"pap\xE0 \xE8 bello\r\n"
"pap\xE0 \xE8 bello\r\n"
"pap\xE0 \xE8 bello"
)
}
};

View File

@ -1,121 +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 "common/iconv.h"
/**
* Representation of test string data and its length in bytes.
*/
typedef struct test_string {
/**
* The raw content of the test string.
*/
unsigned char* buffer;
/**
* The number of bytes within the test string, including null terminator.
*/
int size;
} test_string;
/**
* Convenience macro which statically-initializes a test_string with the given
* string value, automatically calculating its size in bytes.
*
* @param value
* The string value.
*/
#define TEST_STRING(value) { \
.buffer = (unsigned char*) (value), \
.size = sizeof(value) \
}
/**
* The parameters applicable to a unit test for a particular encoding supported
* by guac_iconv().
*/
typedef struct encoding_test_parameters {
/**
* The human-readable name of this encoding. This will be logged to the
* test suite log to assist with debugging test failures.
*/
const char* name;
/**
* Reader function which reads using this encoding and does not perform any
* transformation on newline characters.
*/
guac_iconv_read* reader;
/**
* Reader function which reads using this encoding and automatically
* normalizes newline sequences to Unix-style newline characters.
*/
guac_iconv_read* reader_normalized;
/**
* Writer function which writes using this encoding and does not perform
* any transformation on newline characters.
*/
guac_iconv_write* writer;
/**
* Writer function which writes using this encoding, but writes newline
* characters as CRLF sequences.
*/
guac_iconv_write* writer_crlf;
/**
* A test string having both Windows- and Unix-style line endings. Except
* for the line endings, the characters represented within this test string
* must be identical to all other test strings.
*/
test_string test_mixed;
/**
* A test string having only Unix-style line endings. Except for the line
* endings, the characters represented within this test string must be
* identical to all other test strings.
*/
test_string test_unix;
/**
* A test string having only Windows-style line endings. Except for the
* line endings, the characters represented within this test string must be
* identical to all other test strings.
*/
test_string test_windows;
} encoding_test_parameters;
/**
* The total number of encodings supported by guac_iconv().
*/
#define NUM_SUPPORTED_ENCODINGS 4
/**
* Test parameters for each supported encoding. The test strings included each
* consist of five repeated lines of "papà è bello", omitting the line ending
* of the final line.
*/
extern encoding_test_parameters test_params[NUM_SUPPORTED_ENCODINGS];

View File

@ -18,10 +18,48 @@
*/ */
#include "common/iconv.h" #include "common/iconv.h"
#include "convert-test-data.h"
#include <CUnit/CUnit.h> #include <CUnit/CUnit.h>
#include <stdio.h>
/**
* UTF8 for "papà è bello".
*/
unsigned char test_string_utf8[] = {
'p', 'a', 'p', 0xC3, 0xA0, ' ',
0xC3, 0xA8, ' ',
'b', 'e', 'l', 'l', 'o',
0x00
};
/**
* UTF16 for "papà è bello".
*/
unsigned char test_string_utf16[] = {
'p', 0x00, 'a', 0x00, 'p', 0x00, 0xE0, 0x00, ' ', 0x00,
0xE8, 0x00, ' ', 0x00,
'b', 0x00, 'e', 0x00, 'l', 0x00, 'l', 0x00, 'o', 0x00,
0x00, 0x00
};
/**
* ISO-8859-1 for "papà è bello".
*/
unsigned char test_string_iso8859_1[] = {
'p', 'a', 'p', 0xE0, ' ',
0xE8, ' ',
'b', 'e', 'l', 'l', 'o',
0x00
};
/**
* CP1252 for "papà è bello".
*/
unsigned char test_string_cp1252[] = {
'p', 'a', 'p', 0xE0, ' ',
0xE8, ' ',
'b', 'e', 'l', 'l', 'o',
0x00
};
/** /**
* Tests that conversion between character sets using the given guac_iconv_read * Tests that conversion between character sets using the given guac_iconv_read
@ -31,20 +69,25 @@
* The guac_iconv_read implementation to use to read the input string. * The guac_iconv_read implementation to use to read the input string.
* *
* @param in_string * @param in_string
* A pointer to the test_string structure describing the input string being * A pointer to the beginning of the input string.
* tested. *
* @param in_length
* The size of the input string in bytes.
* *
* @param writer * @param writer
* The guac_iconv_write implementation to use to write the output string * The guac_iconv_write implementation to use to write the output string
* (the converted input string). * (the converted input string).
* *
* @param out_string * @param out_string
* A pointer to the test_string structure describing the expected result of * A pointer to the beginning of a string which contains the expected
* the conversion. * result of the conversion.
*
* @param out_length
* The size of the expected result in bytes.
*/ */
static void verify_conversion( static void verify_conversion(
guac_iconv_read* reader, test_string* in_string, guac_iconv_read* reader, unsigned char* in_string, int in_length,
guac_iconv_write* writer, test_string* out_string) { guac_iconv_write* writer, unsigned char* out_string, int out_length) {
char output[4096]; char output[4096];
char input[4096]; char input[4096];
@ -52,78 +95,91 @@ static void verify_conversion(
const char* current_input = input; const char* current_input = input;
char* current_output = output; char* current_output = output;
memcpy(input, in_string->buffer, in_string->size); memcpy(input, in_string, in_length);
guac_iconv(reader, &current_input, sizeof(input), guac_iconv(reader, &current_input, sizeof(input),
writer, &current_output, sizeof(output)); writer, &current_output, sizeof(output));
/* Verify output length */ /* Verify output length */
CU_ASSERT_EQUAL(out_string->size, current_output - output); CU_ASSERT_EQUAL(out_length, current_output - output);
/* Verify entire input read */ /* Verify entire input read */
CU_ASSERT_EQUAL(in_string->size, current_input - input); CU_ASSERT_EQUAL(in_length, current_input - input);
/* Verify output content */ /* Verify output content */
CU_ASSERT_EQUAL(0, memcmp(output, out_string->buffer, out_string->size)); CU_ASSERT_EQUAL(0, memcmp(output, out_string, out_length));
} }
/** /**
* Test which verifies that every supported encoding can be correctly converted * Tests which verifies conversion of UTF-8 to itself.
* to every other supported encoding, with all line endings preserved verbatim
* (not normalized).
*/ */
void test_iconv__preserve() { void test_iconv__utf8_to_utf8() {
for (int i = 0; i < NUM_SUPPORTED_ENCODINGS; i++) { verify_conversion(
for (int j = 0; j < NUM_SUPPORTED_ENCODINGS; j++) { GUAC_READ_UTF8, test_string_utf8, sizeof(test_string_utf8),
GUAC_WRITE_UTF8, test_string_utf8, sizeof(test_string_utf8));
encoding_test_parameters* from = &test_params[i];
encoding_test_parameters* to = &test_params[j];
printf("# \"%s\" -> \"%s\" ...\n", from->name, to->name);
verify_conversion(from->reader, &from->test_mixed,
to->writer, &to->test_mixed);
}
}
} }
/** /**
* Test which verifies that every supported encoding can be correctly converted * Tests which verifies conversion of UTF-16 to UTF-8.
* to every other supported encoding, normalizing all line endings to
* Unix-style line endings.
*/ */
void test_iconv__normalize_unix() { void test_iconv__utf8_to_utf16() {
for (int i = 0; i < NUM_SUPPORTED_ENCODINGS; i++) { verify_conversion(
for (int j = 0; j < NUM_SUPPORTED_ENCODINGS; j++) { GUAC_READ_UTF8, test_string_utf8, sizeof(test_string_utf8),
GUAC_WRITE_UTF16, test_string_utf16, sizeof(test_string_utf16));
encoding_test_parameters* from = &test_params[i];
encoding_test_parameters* to = &test_params[j];
printf("# \"%s\" -> \"%s\" ...\n", from->name, to->name);
verify_conversion(from->reader_normalized, &from->test_mixed,
to->writer, &to->test_unix);
}
}
} }
/** /**
* Test which verifies that every supported encoding can be correctly converted * Tests which verifies conversion of UTF-16 to itself.
* to every other supported encoding, normalizing all line endings to
* Windows-style line endings.
*/ */
void test_iconv__normalize_crlf() { void test_iconv__utf16_to_utf16() {
for (int i = 0; i < NUM_SUPPORTED_ENCODINGS; i++) { verify_conversion(
for (int j = 0; j < NUM_SUPPORTED_ENCODINGS; j++) { GUAC_READ_UTF16, test_string_utf16, sizeof(test_string_utf16),
GUAC_WRITE_UTF16, test_string_utf16, sizeof(test_string_utf16));
encoding_test_parameters* from = &test_params[i]; }
encoding_test_parameters* to = &test_params[j];
/**
printf("# \"%s\" -> \"%s\" ...\n", from->name, to->name); * Tests which verifies conversion of UTF-8 to UTF-16.
verify_conversion(from->reader_normalized, &from->test_mixed, */
to->writer_crlf, &to->test_windows); void test_iconv__utf16_to_utf8() {
verify_conversion(
} GUAC_READ_UTF16, test_string_utf16, sizeof(test_string_utf16),
} GUAC_WRITE_UTF8, test_string_utf8, sizeof(test_string_utf8));
}
/**
* Tests which verifies conversion of UTF-16 to ISO 8859-1.
*/
void test_iconv__utf16_to_iso8859_1() {
verify_conversion(
GUAC_READ_UTF16, test_string_utf16, sizeof(test_string_utf16),
GUAC_WRITE_ISO8859_1, test_string_iso8859_1, sizeof(test_string_iso8859_1));
}
/**
* Tests which verifies conversion of UTF-16 to CP1252.
*/
void test_iconv__utf16_to_cp1252() {
verify_conversion(
GUAC_READ_UTF16, test_string_utf16, sizeof(test_string_utf16),
GUAC_WRITE_CP1252, test_string_cp1252, sizeof(test_string_cp1252));
}
/**
* Tests which verifies conversion of CP1252 to UTF-8.
*/
void test_iconv__cp1252_to_utf8() {
verify_conversion(
GUAC_READ_CP1252, test_string_cp1252, sizeof(test_string_cp1252),
GUAC_WRITE_UTF8, test_string_utf8, sizeof(test_string_utf8));
}
/**
* Tests which verifies conversion of ISO 8859-1 to UTF-8.
*/
void test_iconv__iso8859_1_to_utf8() {
verify_conversion(
GUAC_READ_ISO8859_1, test_string_iso8859_1, sizeof(test_string_iso8859_1),
GUAC_WRITE_UTF8, test_string_utf8, sizeof(test_string_utf8));
} }

View File

@ -1,115 +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-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,3 +1,4 @@
#!/bin/sh -e
# #
# Licensed to the Apache Software Foundation (ASF) under one # Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file # or more contributor license agreements. See the NOTICE file
@ -17,42 +18,32 @@
# under the License. # under the License.
# #
# ##
# Project name / version ## @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`.
##
PROJECT_NAME = libguac-terminal BUILD_DIR="$1"
PROJECT_NUMBER = @PACKAGE_VERSION@ PREFIX_DIR="$2"
# #
# Warn about undocumented parameters and return values, but do not fill output # Build guacamole-server
# with verbose progress info.
# #
QUIET = YES cd "$BUILD_DIR"
WARN_NO_PARAMDOC = YES autoreconf -fi
./configure --prefix="$PREFIX_DIR" --disable-guaclog --with-freerdp-plugin-dir="$PREFIX_DIR/lib/freerdp2"
# make
# Output format make install
# ldconfig
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

@ -0,0 +1,86 @@
#!/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 ## @fn list-dependencies.sh
## ##
## Lists the Alpine Linux package names for all library dependencies of the ## Lists the Debian/Ubuntu 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,17 +35,14 @@ 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
# List the package providing that library, if any # Determine the Debian package which is associated with that
apk info -W "$LIBRARY" 2> /dev/null \ # library, if any
| grep 'is owned by' | grep -o '[^ ]*$' || true dpkg-query -S "$LIBRARY" 2> /dev/null || true
done done
# Next binary # Next binary
shift shift
# Strip the "-VERSION" suffix from each package name, listing each resulting done | cut -f1 -d: | sort -u
# package uniquely ("apk add" cannot handle package names that include the
# version number)
done | sed 's/\(.*\)-[0-9]\+\..*$/\1/' | sort -u

View File

@ -176,8 +176,8 @@ guacd_config* guacd_conf_load() {
return NULL; return NULL;
/* Load defaults */ /* Load defaults */
conf->bind_host = strdup(GUACD_DEFAULT_BIND_HOST); conf->bind_host = NULL;
conf->bind_port = strdup(GUACD_DEFAULT_BIND_PORT); conf->bind_port = strdup("4822");
conf->pidfile = NULL; conf->pidfile = NULL;
conf->foreground = 0; conf->foreground = 0;
conf->print_version = 0; conf->print_version = 0;

View File

@ -24,18 +24,6 @@
#include <guacamole/client.h> #include <guacamole/client.h>
/**
* The default host that guacd should bind to, if no other host is explicitly
* specified.
*/
#define GUACD_DEFAULT_BIND_HOST "localhost"
/**
* The default port that guacd should bind to, if no other port is explicitly
* specified.
*/
#define GUACD_DEFAULT_BIND_PORT "4822"
/** /**
* The contents of a guacd configuration file. * The contents of a guacd configuration file.
*/ */

View File

@ -278,13 +278,10 @@ static int guacd_route_connection(guacd_proc_map* map, guac_socket* socket) {
proc = guacd_proc_map_retrieve(map, identifier); proc = guacd_proc_map_retrieve(map, identifier);
new_process = 0; new_process = 0;
/* Warn and ward off client if requested connection does not exist */ /* Warn if requested connection does not exist */
if (proc == NULL) { if (proc == NULL)
guacd_log(GUAC_LOG_INFO, "Connection \"%s\" does not exist", identifier); guacd_log(GUAC_LOG_INFO, "Connection \"%s\" does not exist.",
guac_protocol_send_error(socket, "No such connection.", identifier);
GUAC_PROTOCOL_STATUS_RESOURCE_NOT_FOUND);
}
else else
guacd_log(GUAC_LOG_INFO, "Joining existing connection \"%s\"", guacd_log(GUAC_LOG_INFO, "Joining existing connection \"%s\"",
identifier); identifier);

View File

@ -304,6 +304,20 @@ int main(int argc, char* argv[]) {
} }
/* Get socket */
socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if (socket_fd < 0) {
guacd_log(GUAC_LOG_ERROR, "Error opening socket: %s", strerror(errno));
exit(EXIT_FAILURE);
}
/* Allow socket reuse */
if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR,
(void*) &opt_on, sizeof(opt_on))) {
guacd_log(GUAC_LOG_WARNING, "Unable to set socket options for reuse: %s",
strerror(errno));
}
/* Attempt binding of each address until success */ /* Attempt binding of each address until success */
current_address = addresses; current_address = addresses;
while (current_address != NULL) { while (current_address != NULL) {
@ -319,47 +333,27 @@ int main(int argc, char* argv[]) {
guacd_log(GUAC_LOG_ERROR, "Unable to resolve host: %s", guacd_log(GUAC_LOG_ERROR, "Unable to resolve host: %s",
gai_strerror(retval)); gai_strerror(retval));
/* Get socket */
socket_fd = socket(current_address->ai_family, SOCK_STREAM, 0);
if (socket_fd < 0) {
guacd_log(GUAC_LOG_ERROR, "Error opening socket: %s", strerror(errno));
/* Unable to get a socket for the resolved address family, try next */
current_address = current_address->ai_next;
continue;
}
/* Allow socket reuse */
if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR,
(void*) &opt_on, sizeof(opt_on))) {
guacd_log(GUAC_LOG_WARNING, "Unable to set socket options for reuse: %s",
strerror(errno));
}
/* Attempt to bind socket to address */ /* Attempt to bind socket to address */
if (bind(socket_fd, if (bind(socket_fd,
current_address->ai_addr, current_address->ai_addr,
current_address->ai_addrlen) == 0) { current_address->ai_addrlen) == 0) {
guacd_log(GUAC_LOG_DEBUG, "Successfully bound " guacd_log(GUAC_LOG_DEBUG, "Successfully bound socket to "
"%s socket to host %s, port %s", "host %s, port %s", bound_address, bound_port);
(current_address->ai_family == AF_INET) ? "AF_INET" : "AF_INET6",
bound_address, bound_port);
/* Done if successful bind */ /* Done if successful bind */
break; break;
} }
/* Otherwise log information regarding bind failure */ /* Otherwise log information regarding bind failure */
close(socket_fd); else
socket_fd = -1; guacd_log(GUAC_LOG_DEBUG, "Unable to bind socket to "
guacd_log(GUAC_LOG_DEBUG, "Unable to bind %s socket to " "host %s, port %s: %s",
"host %s, port %s: %s", bound_address, bound_port, strerror(errno));
(current_address->ai_family == AF_INET) ? "AF_INET" : "AF_INET6",
bound_address, bound_port, strerror(errno));
/* Try next address */
current_address = current_address->ai_next; current_address = current_address->ai_next;
} }
/* If unable to bind to anything, fail */ /* If unable to bind to anything, fail */
@ -381,15 +375,10 @@ int main(int argc, char* argv[]) {
CRYPTO_set_locking_callback(guacd_openssl_locking_callback); CRYPTO_set_locking_callback(guacd_openssl_locking_callback);
#endif #endif
#if OPENSSL_VERSION_NUMBER < 0x10100000L /* Init SSL */
/* 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

@ -133,7 +133,7 @@ void guacd_log_guac_error(guac_client_log_level level, const char* message) {
void guacd_log_handshake_failure() { void guacd_log_handshake_failure() {
if (guac_error == GUAC_STATUS_CLOSED) if (guac_error == GUAC_STATUS_CLOSED)
guacd_log(GUAC_LOG_DEBUG, guacd_log(GUAC_LOG_INFO,
"Guacamole connection closed during handshake"); "Guacamole connection closed during handshake");
else if (guac_error == GUAC_STATUS_PROTOCOL_ERROR) else if (guac_error == GUAC_STATUS_PROTOCOL_ERROR)
guacd_log(GUAC_LOG_ERROR, guacd_log(GUAC_LOG_ERROR,

View File

@ -330,9 +330,6 @@ static void guacd_exec_proc(guacd_proc* proc, const char* protocol) {
/* The first file descriptor is the owner */ /* The first file descriptor is the owner */
int owner = 1; int owner = 1;
/* Enable keep alive on the broadcast socket */
guac_socket_require_keep_alive(client->socket);
/* Add each received file descriptor as a new user */ /* Add each received file descriptor as a new user */
int received_fd; int received_fd;
while ((received_fd = guacd_recv_fd(proc->fd_socket)) != -1) { while ((received_fd = guacd_recv_fd(proc->fd_socket)) != -1) {
@ -343,7 +340,7 @@ static void guacd_exec_proc(guacd_proc* proc, const char* protocol) {
owner = 0; owner = 0;
} }
cleanup_client: cleanup_client:
/* Request client to stop/disconnect */ /* Request client to stop/disconnect */

View File

@ -90,7 +90,6 @@ endif
guacenc_CFLAGS = \ guacenc_CFLAGS = \
-Werror -Wall \ -Werror -Wall \
@AVCODEC_CFLAGS@ \ @AVCODEC_CFLAGS@ \
@AVFORMAT_CFLAGS@ \
@AVUTIL_CFLAGS@ \ @AVUTIL_CFLAGS@ \
@LIBGUAC_INCLUDE@ \ @LIBGUAC_INCLUDE@ \
@SWSCALE_CFLAGS@ @SWSCALE_CFLAGS@
@ -98,13 +97,12 @@ guacenc_CFLAGS = \
guacenc_LDADD = \ guacenc_LDADD = \
@LIBGUAC_LTLIB@ @LIBGUAC_LTLIB@
guacenc_LDFLAGS = \ guacenc_LDFLAGS = \
@AVCODEC_LIBS@ \ @AVCODEC_LIBS@ \
@AVFORMAT_LIBS@ \ @AVUTIL_LIBS@ \
@AVUTIL_LIBS@ \ @CAIRO_LIBS@ \
@CAIRO_LIBS@ \ @JPEG_LIBS@ \
@JPEG_LIBS@ \ @SWSCALE_LIBS@ \
@SWSCALE_LIBS@ \
@WEBP_LIBS@ @WEBP_LIBS@
EXTRA_DIST = \ EXTRA_DIST = \

View File

@ -51,41 +51,8 @@
*/ */
static int guacenc_write_packet(guacenc_video* video, void* data, int size) { static int guacenc_write_packet(guacenc_video* video, void* data, int size) {
int ret; /* Write data, logging any errors */
if (fwrite(data, 1, size, video->output) == 0) {
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54,1,0)
AVPacket pkt;
/* Have to create a packet around the encoded data we have */
av_init_packet(&pkt);
if (video->context->coded_frame->pts != AV_NOPTS_VALUE) {
pkt.pts = av_rescale_q(video->context->coded_frame->pts,
video->context->time_base,
video->output_stream->time_base);
}
if (video->context->coded_frame->key_frame) {
pkt->flags |= AV_PKT_FLAG_KEY;
}
pkt.data = data;
pkt.size = size;
pkt.stream_index = video->output_stream->index;
ret = av_interleaved_write_frame(video->container_format_context, &pkt);
#else
/* We know data is already a packet if we're using a newer libavcodec */
AVPacket* pkt = (AVPacket*) data;
av_packet_rescale_ts(pkt, video->context->time_base, video->output_stream->time_base);
pkt->stream_index = video->output_stream->index;
ret = av_interleaved_write_frame(video->container_format_context, pkt);
#endif
if (ret != 0) {
guacenc_log(GUAC_LOG_ERROR, "Unable to write frame " guacenc_log(GUAC_LOG_ERROR, "Unable to write frame "
"#%" PRId64 ": %s", video->next_pts, strerror(errno)); "#%" PRId64 ": %s", video->next_pts, strerror(errno));
return -1; return -1;
@ -95,7 +62,8 @@ static int guacenc_write_packet(guacenc_video* video, void* data, int size) {
guacenc_log(GUAC_LOG_DEBUG, "Frame #%08" PRId64 ": wrote %i bytes", guacenc_log(GUAC_LOG_DEBUG, "Frame #%08" PRId64 ": wrote %i bytes",
video->next_pts, size); video->next_pts, size);
return ret; return 0;
} }
int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) { int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) {
@ -135,15 +103,6 @@ int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) {
#else #else
/* For libavcodec < 57.37.100: input/output was not decoupled and static
* allocation of AVPacket was supported.
*
* NOTE: Since dynamic allocation of AVPacket was added before this point (in
* 57.12.100) and static allocation was deprecated later (in 58.133.100), it is
* convenient to tie static vs. dynamic allocation to the old vs. new I/O
* mechanism and avoid further complicating the version comparison logic. */
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 37, 100)
/* Init video packet */ /* Init video packet */
AVPacket packet; AVPacket packet;
av_init_packet(&packet); av_init_packet(&packet);
@ -152,6 +111,8 @@ int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) {
packet.data = NULL; packet.data = NULL;
packet.size = 0; packet.size = 0;
/* For libavcodec < 57.37.100: input/output was not decoupled */
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57,37,100)
/* Write frame to video */ /* Write frame to video */
int got_data; int got_data;
if (avcodec_encode_video2(video->context, &packet, frame, &got_data) < 0) { if (avcodec_encode_video2(video->context, &packet, frame, &got_data) < 0) {
@ -162,12 +123,10 @@ int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) {
/* Write corresponding data to file */ /* Write corresponding data to file */
if (got_data) { if (got_data) {
guacenc_write_packet(video, (void*) &packet, packet.size); guacenc_write_packet(video, packet.data, packet.size);
av_packet_unref(&packet); av_packet_unref(&packet);
} }
#else #else
/* Write frame to video */ /* Write frame to video */
int result = avcodec_send_frame(video->context, frame); int result = avcodec_send_frame(video->context, frame);
@ -182,25 +141,18 @@ int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) {
return -1; return -1;
} }
AVPacket* packet = av_packet_alloc();
if (packet == NULL)
return -1;
/* Flush all available packets */ /* Flush all available packets */
int got_data = 0; int got_data = 0;
while (avcodec_receive_packet(video->context, packet) == 0) { while (avcodec_receive_packet(video->context, &packet) == 0) {
/* Data was received */ /* Data was received */
got_data = 1; got_data = 1;
/* Attempt to write data to output file */ /* Attempt to write data to output file */
guacenc_write_packet(video, (void*) packet, packet->size); guacenc_write_packet(video, packet.data, packet.size);
av_packet_unref(packet); av_packet_unref(&packet);
} }
av_packet_free(&packet);
#endif #endif
/* Frame may have been queued for later writing / reordering */ /* Frame may have been queued for later writing / reordering */
@ -213,54 +165,3 @@ int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) {
#endif #endif
} }
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) {
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(57, 33, 100)
stream->codec->bit_rate = bitrate;
stream->codec->width = width;
stream->codec->height = height;
stream->codec->gop_size = gop_size;
stream->codec->qmax = qmax;
stream->codec->qmin = qmin;
stream->codec->pix_fmt = pix_fmt;
stream->codec->time_base = time_base;
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(55, 44, 100)
stream->time_base = time_base;
#endif
return stream->codec;
#else
AVCodecContext* context = avcodec_alloc_context3(codec);
if (context) {
context->bit_rate = bitrate;
context->width = width;
context->height = height;
context->gop_size = gop_size;
context->qmax = qmax;
context->qmin = qmin;
context->pix_fmt = pix_fmt;
context->time_base = time_base;
stream->time_base = time_base;
}
return context;
#endif
}
int guacenc_open_avcodec(AVCodecContext *avcodec_context,
const AVCodec *codec, AVDictionary **options,
AVStream* stream) {
int ret = avcodec_open2(avcodec_context, codec, options);
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 33, 100)
/* Copy stream parameters to the muxer */
int codecpar_ret = avcodec_parameters_from_context(stream->codecpar, avcodec_context);
if (codecpar_ret < 0)
return codecpar_ret;
#endif
return ret;
}

View File

@ -52,16 +52,6 @@
#define av_packet_unref av_free_packet #define av_packet_unref av_free_packet
#endif #endif
/* For libavcodec <= 56.41.100: Global header flag didn't have AV_ prefix.
* Guacenc defines its own flag here to avoid conflicts with libavcodec
* macros.
*/
#if LIBAVCODEC_VERSION_INT <= AV_VERSION_INT(56,41,100)
#define GUACENC_FLAG_GLOBAL_HEADER CODEC_FLAG_GLOBAL_HEADER
#else
#define GUACENC_FLAG_GLOBAL_HEADER AV_CODEC_FLAG_GLOBAL_HEADER
#endif
/* For libavutil < 51.42.0: AV_PIX_FMT_* was PIX_FMT_* */ /* For libavutil < 51.42.0: AV_PIX_FMT_* was PIX_FMT_* */
#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51,42,0) #if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51,42,0)
#define AV_PIX_FMT_RGB32 PIX_FMT_RGB32 #define AV_PIX_FMT_RGB32 PIX_FMT_RGB32
@ -88,78 +78,5 @@
*/ */
int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame); int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame);
/**
* Creates and sets up the AVCodecContext for the appropriate version of
* libavformat installed. The AVCodecContext will be built, but the AVStream
* will also be affected by having its time_base field set to the value passed
* into this function.
*
* @param stream
* The open AVStream.
*
* @param codec
* The codec used on the AVStream.
*
* @param bitrate
* The target bitrate for the encoded video
*
* @param width
* The target width for the encoded video.
*
* @param height
* The target height for the encoded video.
*
* @param gop_size
* The size of the Group of Pictures.
*
* @param qmax
* The max value of the quantizer.
*
* @param qmin
* The min value of the quantizer.
*
* @param pix_fmt
* The target pixel format for the encoded video.
*
* @param time_base
* The target time base for the encoded video.
*
* @return
* The pointer to the configured AVCodecContext.
*
*/
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);
/**
* A wrapper for avcodec_open2(). Because libavformat ver 57.33.100 and greater
* use stream->codecpar rather than stream->codec to handle information to the
* codec, there needs to be an additional step in that version. So this
* wrapper handles that. Otherwise, it's the same as avcodec_open2().
*
* @param avcodec_context
* The context to initialize.
*
* @param codec
* The codec to open this context for. If a non-NULL codec has been
* previously passed to avcodec_alloc_context3() or for this context, then
* this parameter MUST be either NULL or equal to the previously passed
* codec.
*
* @param options
* A dictionary filled with AVCodecContext and codec-private options. On
* return this object will be filled with options that were not found.
*
* @param stream
* The stream for the codec context.
*
* @return
* Zero on success, a negative value on error.
*/
int guacenc_open_avcodec(AVCodecContext *avcodec_context,
const AVCodec *codec, AVDictionary **options,
AVStream* stream);
#endif #endif

View File

@ -25,7 +25,6 @@
#include "parse.h" #include "parse.h"
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <getopt.h> #include <getopt.h>
#include <stdbool.h> #include <stdbool.h>
@ -81,10 +80,6 @@ int main(int argc, char* argv[]) {
avcodec_register_all(); avcodec_register_all();
#endif #endif
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100)
av_register_all();
#endif
/* Track number of overall failures */ /* Track number of overall failures */
int total_files = argc - optind; int total_files = argc - optind;
int failures = 0; int failures = 0;

View File

@ -38,7 +38,7 @@ is essentially an implementation of a Guacamole client which accepts
its input from files instead of a network connection, and renders directly to its input from files instead of a network connection, and renders directly to
video instead of to the user's screen. video instead of to the user's screen.
.P .P
Each \fIFILE\fR specified will be encoded as MPEG-4 video to a new Each \fIFILE\fR specified will be encoded as a raw MPEG-4 video stream to a new
file named \fIFILE\fR.m4v, encoded according to the other options specified. By file named \fIFILE\fR.m4v, encoded according to the other options specified. By
default, the output video will be \fI640\fRx\fI480\fR pixels, and will be saved default, the output video will be \fI640\fRx\fI480\fR pixels, and will be saved
with a bitrate of \fI2000000\fR bits per second (2 Mbps). These defaults can be with a bitrate of \fI2000000\fR bits per second (2 Mbps). These defaults can be

View File

@ -25,9 +25,6 @@
#include <cairo/cairo.h> #include <cairo/cairo.h>
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
#ifndef AVFORMAT_AVFORMAT_H
#include <libavformat/avformat.h>
#endif
#include <libavutil/common.h> #include <libavutil/common.h>
#include <libavutil/imgutils.h> #include <libavutil/imgutils.h>
#include <libswscale/swscale.h> #include <libswscale/swscale.h>
@ -37,68 +34,42 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <assert.h> #include <assert.h>
#include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <inttypes.h> #include <inttypes.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <unistd.h> #include <unistd.h>
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) {
const AVOutputFormat *container_format;
AVFormatContext *container_format_context;
AVStream *video_stream;
int ret;
int failed_header = 0;
/* allocate the output media context */
avformat_alloc_output_context2(&container_format_context, NULL, NULL, path);
if (container_format_context == NULL) {
guacenc_log(GUAC_LOG_ERROR, "Failed to determine container from output file name");
goto fail_codec;
}
container_format = container_format_context->oformat;
/* Pull codec based on name */ /* Pull codec based on name */
const AVCodec* codec = avcodec_find_encoder_by_name(codec_name); 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);
goto fail_codec; goto fail_codec;
} }
/* create stream */
video_stream = NULL;
video_stream = avformat_new_stream(container_format_context, codec);
if (video_stream == NULL) {
guacenc_log(GUAC_LOG_ERROR, "Could not allocate encoder stream. Cannot continue.");
goto fail_format_context;
}
video_stream->id = container_format_context->nb_streams - 1;
/* Retrieve encoding context */ /* Retrieve encoding context */
AVCodecContext* avcodec_context = AVCodecContext* context = avcodec_alloc_context3(codec);
guacenc_build_avcodeccontext(video_stream, codec, bitrate, width, if (context == NULL) {
height, /*gop size*/ 10, /*qmax*/ 31, /*qmin*/ 2,
/*pix fmt*/ AV_PIX_FMT_YUV420P,
/*time base*/ (AVRational) { 1, GUACENC_VIDEO_FRAMERATE });
if (avcodec_context == NULL) {
guacenc_log(GUAC_LOG_ERROR, "Failed to allocate context for " guacenc_log(GUAC_LOG_ERROR, "Failed to allocate context for "
"codec \"%s\".", codec_name); "codec \"%s\".", codec_name);
goto fail_context; goto fail_context;
} }
/* If format needs global headers, write them */ /* Init context with encoding parameters */
if (container_format_context->oformat->flags & AVFMT_GLOBALHEADER) { context->bit_rate = bitrate;
avcodec_context->flags |= GUACENC_FLAG_GLOBAL_HEADER; context->width = width;
} context->height = height;
context->time_base = (AVRational) { 1, GUACENC_VIDEO_FRAMERATE };
context->gop_size = 10;
context->max_b_frames = 1;
context->pix_fmt = AV_PIX_FMT_YUV420P;
/* Open codec for use */ /* Open codec for use */
if (guacenc_open_avcodec(avcodec_context, codec, NULL, video_stream) < 0) { if (avcodec_open2(context, codec, NULL) < 0) {
guacenc_log(GUAC_LOG_ERROR, "Failed to open codec \"%s\".", codec_name); guacenc_log(GUAC_LOG_ERROR, "Failed to open codec \"%s\".", codec_name);
goto fail_codec_open; goto fail_codec_open;
} }
@ -110,9 +81,9 @@ guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
} }
/* Copy necessary data for frame from context */ /* Copy necessary data for frame from context */
frame->format = avcodec_context->pix_fmt; frame->format = context->pix_fmt;
frame->width = avcodec_context->width; frame->width = context->width;
frame->height = avcodec_context->height; frame->height = context->height;
/* Allocate actual backing data for frame */ /* Allocate actual backing data for frame */
if (av_image_alloc(frame->data, frame->linesize, frame->width, if (av_image_alloc(frame->data, frame->linesize, frame->width,
@ -120,32 +91,31 @@ guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
goto fail_frame_data; goto fail_frame_data;
} }
/* Open output file, if the container needs it */ /* Open output file */
if (!(container_format->flags & AVFMT_NOFILE)) { int fd = open(path, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR);
ret = avio_open(&container_format_context->pb, path, AVIO_FLAG_WRITE); if (fd == -1) {
if (ret < 0) { guacenc_log(GUAC_LOG_ERROR, "Failed to open output file \"%s\": %s",
guacenc_log(GUAC_LOG_ERROR, "Error occurred while opening output file."); path, strerror(errno));
goto fail_output_avio; goto fail_output_fd;
}
} }
/* write the stream header, if needed */ /* Create stream for output file */
ret = avformat_write_header(container_format_context, NULL); FILE* output = fdopen(fd, "wb");
if (ret < 0) { if (output == NULL) {
guacenc_log(GUAC_LOG_ERROR, "Error occurred while writing output file header."); guacenc_log(GUAC_LOG_ERROR, "Failed to allocate stream for output "
failed_header = true; "file \"%s\": %s", path, strerror(errno));
goto fail_output_file; goto fail_output_file;
} }
/* Allocate video structure */ /* Allocate video structure */
guacenc_video* video = malloc(sizeof(guacenc_video)); guacenc_video* video = malloc(sizeof(guacenc_video));
if (video == NULL) if (video == NULL) {
goto fail_alloc_video; goto fail_video;
}
/* Init properties of video */ /* Init properties of video */
video->output_stream = video_stream; video->output = output;
video->context = avcodec_context; video->context = context;
video->container_format_context = container_format_context;
video->next_frame = frame; video->next_frame = frame;
video->width = width; video->width = width;
video->height = height; video->height = height;
@ -158,16 +128,13 @@ guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
return video; return video;
/* Free all allocated data in case of failure */ /* Free all allocated data in case of failure */
fail_alloc_video: fail_video:
fclose(output);
fail_output_file: fail_output_file:
avio_close(container_format_context->pb); close(fd);
/* Delete the file that was created if it was actually created */ fail_output_fd:
if (unlink(path) == -1 && errno != ENOENT)
guacenc_log(GUAC_LOG_WARNING, "Failed output file \"%s\" could not "
"be automatically deleted: %s", path, strerror(errno));
fail_output_avio:
av_freep(&frame->data[0]); av_freep(&frame->data[0]);
fail_frame_data: fail_frame_data:
@ -175,13 +142,7 @@ fail_frame_data:
fail_frame: fail_frame:
fail_codec_open: fail_codec_open:
avcodec_free_context(&avcodec_context); avcodec_free_context(&context);
fail_format_context:
/* failing to write the container implicitly frees the context */
if (!failed_header) {
avformat_free_context(container_format_context);
}
fail_context: fail_context:
fail_codec: fail_codec:
@ -474,34 +435,26 @@ int guacenc_video_free(guacenc_video* video) {
/* Write final frame */ /* Write final frame */
guacenc_video_flush_frame(video); guacenc_video_flush_frame(video);
/* Init video packet for final flush of encoded data */
AVPacket packet;
av_init_packet(&packet);
/* Flush any unwritten frames */ /* Flush any unwritten frames */
int retval; int retval;
do { do {
retval = guacenc_video_write_frame(video, NULL); retval = guacenc_video_write_frame(video, NULL);
} while (retval > 0); } while (retval > 0);
/* write trailer, if needed */
if (video->container_format_context != NULL &&
video->output_stream != NULL) {
guacenc_log(GUAC_LOG_DEBUG, "Writing trailer: %s",
av_write_trailer(video->container_format_context) == 0 ?
"success" : "failure");
}
/* File is now completely written */ /* File is now completely written */
if (video->container_format_context != NULL) { fclose(video->output);
avio_close(video->container_format_context->pb);
}
/* Free frame encoding data */ /* Free frame encoding data */
av_freep(&video->next_frame->data[0]); av_freep(&video->next_frame->data[0]);
av_frame_free(&video->next_frame); av_frame_free(&video->next_frame);
/* Clean up encoding context */ /* Clean up encoding context */
if (video->context != NULL) { avcodec_close(video->context);
avcodec_close(video->context); avcodec_free_context(&(video->context));
avcodec_free_context(&(video->context));
}
free(video); free(video);
return 0; return 0;

View File

@ -26,14 +26,6 @@
#include <guacamole/timestamp.h> #include <guacamole/timestamp.h>
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
#ifndef AVCODEC_AVCODEC_H
#include <libavcodec/avcodec.h>
#endif
#ifndef AVFORMAT_AVFORMAT_H
#include <libavformat/avformat.h>
#endif
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
@ -50,11 +42,9 @@
typedef struct guacenc_video { typedef struct guacenc_video {
/** /**
* AVStream for video output. * Output file stream.
* Frames sent to this stream are written into
* the output file in the specified container format.
*/ */
AVStream* output_stream; FILE* output;
/** /**
* The open encoding context from libavcodec, created for the codec * The open encoding context from libavcodec, created for the codec
@ -62,12 +52,6 @@ typedef struct guacenc_video {
*/ */
AVCodecContext* context; AVCodecContext* context;
/**
* The open format context from libavformat, created for the file
* container specified when this guacenc_video was created.
*/
AVFormatContext* container_format_context;
/** /**
* The width of the video, in pixels. * The width of the video, in pixels.
*/ */

View File

@ -32,9 +32,6 @@ SUBDIRS = . tests
libguacincdir = $(includedir)/guacamole libguacincdir = $(includedir)/guacamole
libguacinc_HEADERS = \ libguacinc_HEADERS = \
guacamole/argv.h \
guacamole/argv-constants.h \
guacamole/argv-fntypes.h \
guacamole/audio.h \ guacamole/audio.h \
guacamole/audio-fntypes.h \ guacamole/audio-fntypes.h \
guacamole/audio-types.h \ guacamole/audio-types.h \
@ -44,7 +41,6 @@ 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 \
@ -60,7 +56,6 @@ libguacinc_HEADERS = \
guacamole/protocol.h \ guacamole/protocol.h \
guacamole/protocol-constants.h \ guacamole/protocol-constants.h \
guacamole/protocol-types.h \ guacamole/protocol-types.h \
guacamole/recording.h \
guacamole/socket-constants.h \ guacamole/socket-constants.h \
guacamole/socket.h \ guacamole/socket.h \
guacamole/socket-fntypes.h \ guacamole/socket-fntypes.h \
@ -74,9 +69,7 @@ libguacinc_HEADERS = \
guacamole/user.h \ guacamole/user.h \
guacamole/user-constants.h \ guacamole/user-constants.h \
guacamole/user-fntypes.h \ guacamole/user-fntypes.h \
guacamole/user-types.h \ guacamole/user-types.h
guacamole/wol.h \
guacamole/wol-constants.h
noinst_HEADERS = \ noinst_HEADERS = \
id.h \ id.h \
@ -88,13 +81,11 @@ noinst_HEADERS = \
wait-fd.h wait-fd.h
libguac_la_SOURCES = \ libguac_la_SOURCES = \
argv.c \
audio.c \ audio.c \
client.c \ client.c \
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 \
@ -102,7 +93,6 @@ libguac_la_SOURCES = \
pool.c \ pool.c \
protocol.c \ protocol.c \
raw_encoder.c \ raw_encoder.c \
recording.c \
socket.c \ socket.c \
socket-broadcast.c \ socket-broadcast.c \
socket-fd.c \ socket-fd.c \
@ -114,8 +104,7 @@ libguac_la_SOURCES = \
user.c \ user.c \
user-handlers.c \ user-handlers.c \
user-handshake.c \ user-handshake.c \
wait-fd.c \ wait-fd.c
wol.c
# Compile WebP support if available # Compile WebP support if available
if ENABLE_WEBP if ENABLE_WEBP
@ -139,7 +128,7 @@ libguac_la_CFLAGS = \
-Werror -Wall -pedantic -Werror -Wall -pedantic
libguac_la_LDFLAGS = \ libguac_la_LDFLAGS = \
-version-info 21:0:0 \ -version-info 17:0:0 \
-no-undefined \ -no-undefined \
@CAIRO_LIBS@ \ @CAIRO_LIBS@ \
@DL_LIBS@ \ @DL_LIBS@ \

View File

@ -1,350 +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 "guacamole/argv.h"
#include "guacamole/client.h"
#include "guacamole/protocol.h"
#include "guacamole/socket.h"
#include "guacamole/stream.h"
#include "guacamole/string.h"
#include "guacamole/user.h"
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
/**
* The state of an argument that will be automatically processed. Note that
* this is distinct from the state of an argument value that is currently being
* processed. Argument value states are dynamically-allocated and scoped by the
* associated guac_stream.
*/
typedef struct guac_argv_state {
/**
* The name of the argument.
*/
char name[GUAC_ARGV_MAX_NAME_LENGTH];
/**
* Whether at least one value for this argument has been received since it
* was registered.
*/
int received;
/**
* Bitwise OR of all option flags that should affect processing of this
* argument.
*/
int options;
/**
* The callback that should be invoked when a new value for the associated
* argument has been received. If the GUAC_ARGV_OPTION_ONCE flag is set,
* the callback will be invoked at most once.
*/
guac_argv_callback* callback;
/**
* The arbitrary data that should be passed to the callback.
*/
void* data;
} guac_argv_state;
/**
* The current state of automatic processing of "argv" streams.
*/
typedef struct guac_argv_await_state {
/**
* Whether automatic argument processing has been stopped via a call to
* guac_argv_stop().
*/
int stopped;
/**
* The total number of arguments registered.
*/
unsigned int num_registered;
/**
* All registered arguments and their corresponding callbacks.
*/
guac_argv_state registered[GUAC_ARGV_MAX_REGISTERED];
/**
* Lock which protects multi-threaded access to this entire state
* structure, including the condition that signals specific modifications
* to the structure.
*/
pthread_mutex_t lock;
/**
* Condition which is signalled whenever the overall state of "argv"
* processing changes, either through the receipt of a new argument value
* or due to a call to guac_argv_stop().
*/
pthread_cond_t changed;
} guac_argv_await_state;
/**
* The value or current status of a connection parameter received over an
* "argv" stream.
*/
typedef struct guac_argv {
/**
* The state of the specific setting being updated.
*/
guac_argv_state* state;
/**
* The mimetype of the data being received.
*/
char mimetype[GUAC_ARGV_MAX_MIMETYPE_LENGTH];
/**
* Buffer space for containing the received argument value.
*/
char buffer[GUAC_ARGV_MAX_LENGTH];
/**
* The number of bytes received so far.
*/
int length;
} guac_argv;
/**
* Statically-allocated, shared state of the guac_argv_*() family of functions.
*/
static guac_argv_await_state await_state = {
.lock = PTHREAD_MUTEX_INITIALIZER,
.changed = PTHREAD_COND_INITIALIZER
};
/**
* Returns whether at least one value for each of the provided arguments has
* been received.
*
* @param args
* A NULL-terminated array of the names of all arguments to test.
*
* @return
* Non-zero if at least one value has been received for each of the
* provided arguments, zero otherwise.
*/
static int guac_argv_is_received(const char** args) {
for (int i = 0; i < await_state.num_registered; i++) {
/* Ignore all received arguments */
guac_argv_state* state = &await_state.registered[i];
if (state->received)
continue;
/* Fail immediately for any matching non-received arguments */
for (const char** arg = args; *arg != NULL; arg++) {
if (strcmp(state->name, *arg) == 0)
return 0;
}
}
/* All arguments were received */
return 1;
}
int guac_argv_register(const char* name, guac_argv_callback* callback, void* data, int options) {
pthread_mutex_lock(&await_state.lock);
if (await_state.num_registered == GUAC_ARGV_MAX_REGISTERED) {
pthread_mutex_unlock(&await_state.lock);
return 1;
}
guac_argv_state* state = &await_state.registered[await_state.num_registered++];
guac_strlcpy(state->name, name, sizeof(state->name));
state->options = options;
state->callback = callback;
state->data = data;
pthread_mutex_unlock(&await_state.lock);
return 0;
}
int guac_argv_await(const char** args) {
/* Wait for all requested arguments to be received (or for receipt to be
* stopped) */
pthread_mutex_lock(&await_state.lock);
while (!await_state.stopped && !guac_argv_is_received(args))
pthread_cond_wait(&await_state.changed, &await_state.lock);
/* Arguments were successfully received only if receipt was not stopped */
int retval = await_state.stopped;
pthread_mutex_unlock(&await_state.lock);
return retval;
}
/**
* Handler for "blob" instructions which appends the data from received blobs
* to the end of the in-progress argument value buffer.
*
* @see guac_user_blob_handler
*/
static int guac_argv_blob_handler(guac_user* user, guac_stream* stream,
void* data, int length) {
guac_argv* argv = (guac_argv*) stream->data;
/* Calculate buffer size remaining, including space for null terminator,
* adjusting received length accordingly */
int remaining = sizeof(argv->buffer) - argv->length - 1;
if (length > remaining)
length = remaining;
/* Append received data to end of buffer */
memcpy(argv->buffer + argv->length, data, length);
argv->length += length;
return 0;
}
/**
* Handler for "end" instructions which applies the changes specified by the
* argument value buffer associated with the stream.
*
* @see guac_user_end_handler
*/
static int guac_argv_end_handler(guac_user* user, guac_stream* stream) {
int result = 0;
/* Append null terminator to value */
guac_argv* argv = (guac_argv*) stream->data;
argv->buffer[argv->length] = '\0';
pthread_mutex_lock(&await_state.lock);
/* Invoke callback, limiting to a single invocation if
* GUAC_ARGV_OPTION_ONCE applies */
guac_argv_state* state = argv->state;
if (!(state->options & GUAC_ARGV_OPTION_ONCE) || !state->received) {
if (state->callback != NULL)
result = state->callback(user, argv->mimetype, state->name, argv->buffer, state->data);
}
/* Alert connected clients regarding newly-accepted values if echo is
* enabled */
if (!result && (state->options & GUAC_ARGV_OPTION_ECHO)) {
guac_client* client = user->client;
guac_client_stream_argv(client, client->socket, argv->mimetype, state->name, argv->buffer);
}
/* Notify that argument has been received */
state->received = 1;
pthread_cond_broadcast(&await_state.changed);
pthread_mutex_unlock(&await_state.lock);
free(argv);
return 0;
}
int guac_argv_received(guac_stream* stream, const char* mimetype, const char* name) {
pthread_mutex_lock(&await_state.lock);
for (int i = 0; i < await_state.num_registered; i++) {
/* Ignore any arguments that have already been received if they are
* declared as being acceptable only once */
guac_argv_state* state = &await_state.registered[i];
if ((state->options & GUAC_ARGV_OPTION_ONCE) && state->received)
continue;
/* Argument matched */
if (strcmp(state->name, name) == 0) {
guac_argv* argv = malloc(sizeof(guac_argv));
guac_strlcpy(argv->mimetype, mimetype, sizeof(argv->mimetype));
argv->state = state;
argv->length = 0;
stream->data = argv;
stream->blob_handler = guac_argv_blob_handler;
stream->end_handler = guac_argv_end_handler;
pthread_mutex_unlock(&await_state.lock);
return 0;
}
}
/* No such argument awaiting processing */
pthread_mutex_unlock(&await_state.lock);
return 1;
}
void guac_argv_stop() {
pthread_mutex_lock(&await_state.lock);
/* Signal any waiting threads that no further argument values will be
* received */
if (!await_state.stopped) {
await_state.stopped = 1;
pthread_cond_broadcast(&await_state.changed);
}
pthread_mutex_unlock(&await_state.lock);
}
int guac_argv_handler(guac_user* user, guac_stream* stream,
char* mimetype, char* name) {
/* Refuse stream if argument is not registered */
if (guac_argv_received(stream, mimetype, name)) {
guac_protocol_send_ack(user->socket, stream, "Not allowed.",
GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
guac_socket_flush(user->socket);
return 0;
}
/* Signal stream is ready */
guac_protocol_send_ack(user->socket, stream, "Ready for updated "
"parameter.", GUAC_PROTOCOL_STATUS_SUCCESS);
guac_socket_flush(user->socket);
return 0;
}

View File

@ -307,10 +307,6 @@ 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;
} }
@ -337,10 +333,6 @@ 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);
@ -421,19 +413,15 @@ void* guac_client_for_user(guac_client* client, guac_user* user,
} }
int guac_client_end_frame(guac_client* client) { 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 */ /* Update and send timestamp */
client->last_sent_timestamp = guac_timestamp_current(); client->last_sent_timestamp = guac_timestamp_current();
/* Log received timestamp and calculated lag (at TRACE level only) */ /* Log received timestamp and calculated lag (at TRACE level only) */
guac_client_log(client, GUAC_LOG_TRACE, "Server completed " guac_client_log(client, GUAC_LOG_TRACE, "Server completed "
"frame %" PRIu64 "ms (%i logical frames)", client->last_sent_timestamp, frames); "frame %" PRIu64 "ms.", client->last_sent_timestamp);
return guac_protocol_send_sync(client->socket, client->last_sent_timestamp, frames); return guac_protocol_send_sync(client->socket, client->last_sent_timestamp);
} }
@ -490,44 +478,6 @@ int guac_client_load_plugin(guac_client* client, const char* protocol) {
} }
/**
* A callback function which is invoked by guac_client_owner_send_required() to
* send the required parameters to the specified user, who is the owner of the
* client session.
*
* @param user
* The guac_user that will receive the required parameters, who is the owner
* of the client.
*
* @param data
* A pointer to a NULL-terminated array of required parameters that will be
* passed on to the owner to continue the connection.
*
* @return
* Zero if the operation succeeds or non-zero on failure, cast as a void*.
*/
static void* guac_client_owner_send_required_callback(guac_user* user, void* data) {
const char** required = (const char **) data;
/* Send required parameters to owner. */
if (user != NULL)
return (void*) ((intptr_t) guac_protocol_send_required(user->socket, required));
return (void*) ((intptr_t) -1);
}
int guac_client_owner_send_required(guac_client* client, const char** required) {
/* Don't send required instruction if client does not support it. */
if (!guac_client_owner_supports_required(client))
return -1;
return (int) ((intptr_t) guac_client_for_owner(client, guac_client_owner_send_required_callback, required));
}
/** /**
* Updates the provided approximate processing lag, taking into account the * Updates the provided approximate processing lag, taking into account the
* processing lag of the given user. * processing lag of the given user.
@ -683,184 +633,6 @@ 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()
* to determine if the owner of a client supports the "required" 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 "required" 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 "required" instruction, or zero if the user does not. The integer
* is cast as a void*.
*/
static void* guac_owner_supports_required_callback(guac_user* user, void* data) {
return (void*) ((intptr_t) guac_user_supports_required(user));
}
int guac_client_owner_supports_required(guac_client* client) {
return (int) ((intptr_t) guac_client_for_owner(client, guac_owner_supports_required_callback, NULL));
}
/**
* 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

View File

@ -1,51 +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 "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

@ -1,71 +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_ARGV_CONSTANTS_H
#define GUAC_ARGV_CONSTANTS_H
/**
* Constants related to automatic handling of received "argv" instructions.
*
* @file argv-constants.h
*/
/**
* Option flag which declares to guac_argv_register() that the associated
* argument should be processed exactly once. If multiple "argv" streams are
* received for the argument, only the first such stream is processed.
* Additional streams will be rejected.
*/
#define GUAC_ARGV_OPTION_ONCE 1
/**
* Option flag which declares to guac_argv_register() that the values received
* and accepted for the associated argument should be echoed to all connected
* users via outbound "argv" streams.
*/
#define GUAC_ARGV_OPTION_ECHO 2
/**
* The maximum number of bytes to allow for any argument value received via an
* argv stream and processed using guac_argv_received(), including null
* terminator.
*/
#define GUAC_ARGV_MAX_LENGTH 16384
/**
* The maximum number of bytes to allow within the name of any argument
* registered with guac_argv_register(), including null terminator.
*/
#define GUAC_ARGV_MAX_NAME_LENGTH 256
/**
* The maximum number of bytes to allow within the mimetype of any received
* argument value passed to a callback registered with guac_argv_register(),
* including null terminator.
*/
#define GUAC_ARGV_MAX_MIMETYPE_LENGTH 4096
/**
* The maximum number of arguments that may be registered via guac_argv_await()
* or guac_argv_await_async() before further argument registrations will fail.
*/
#define GUAC_ARGV_MAX_REGISTERED 128
#endif

View File

@ -1,62 +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_ARGV_FNTYPES_H
#define GUAC_ARGV_FNTYPES_H
/**
* Function type definitions related to automatic handling of received "argv"
* instructions.
*
* @file argv-fntypes.h
*/
#include "user-types.h"
/**
* Callback which is invoked by the automatic "argv" handling when the full
* value of a received argument has been received.
*
* @param user
* The user that opened the argument value stream.
*
* @param mimetype
* The mimetype of the data that will be sent along the stream.
*
* @param name
* The name of the connection parameter being updated. It is up to the
* implementation of this handler to decide whether and how to update a
* connection parameter.
*
* @param value
* The value of the received argument.
*
* @param data
* Any arbitrary data that was provided when the received argument was
* registered with guac_argv_register().
*
* @return
* Zero if the received argument value has been accepted and has either
* taken effect or is being intentionally ignored, non-zero otherwise.
*/
typedef int guac_argv_callback(guac_user* user, const char* mimetype,
const char* name, const char* value, void* data);
#endif

View File

@ -1,128 +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_ARGV_H
#define GUAC_ARGV_H
/**
* Convenience functions for processing parameter values that are submitted
* dynamically using "argv" instructions.
*
* @file argv.h
*/
#include "argv-constants.h"
#include "argv-fntypes.h"
#include "stream-types.h"
#include "user-fntypes.h"
/**
* Registers the given callback such that it is automatically invoked when an
* "argv" stream for an argument having the given name is processed using
* guac_argv_received(). The maximum number of arguments that may be registered
* in this way is limited by GUAC_ARGV_MAX_REGISTERED. The maximum length of
* any provided argument name is limited by GUAC_ARGV_MAX_NAME_LENGTH.
*
* @see GUAC_ARGV_MAX_NAME_LENGTH
* @see GUAC_ARGV_MAX_REGISTERED
*
* @see GUAC_ARGV_OPTION_ONCE
* @see GUAC_ARGV_OPTION_ECHO
*
* @param name
* The name of the argument that should be handled by the given callback.
*
* @param callback
* The callback to invoke when the value of an argument having the given
* name has finished being received.
*
* @param data
* Arbitrary data to be passed to the given callback when a value is
* received for the given argument.
*
* @param options
* Bitwise OR of all option flags that should affect processing of this
* argument.
*
* @return
* Zero if the callback was successfully registered, non-zero if the
* maximum number of registered callbacks has already been reached.
*/
int guac_argv_register(const char* name, guac_argv_callback* callback, void* data, int options);
/**
* Waits for receipt of each of the given arguments via guac_argv_received().
* This function will block until either ALL of the given arguments have been
* received via guac_argv_received() or until automatic processing of received
* arguments is stopped with guac_argv_stop().
*
* @param args
* A NULL-terminated array of the names of all arguments that this function
* should wait for.
*
* @return
* Zero if all of the specified arguments were received, non-zero if
* guac_argv_stop() was called before all arguments were received.
*/
int guac_argv_await(const char** args);
/**
* Hands off management of the given guac_stream, automatically processing data
* received over that stream as the value of the argument having the given
* name. The argument must have already been registered with
* guac_argv_register(). The blob_handler and end_handler of the given stream,
* if already set, will be overridden without regard to their current value.
*
* It is the responsibility of the caller to properly send any required "ack"
* instructions to accept or reject the received stream.
*
* @param stream
* The guac_stream that will receive the value of the argument having the
* given name.
*
* @param mimetype
* The mimetype of the data that will be received over the stream.
*
* @param name
* The name of the argument being received.
*
* @return
* Zero if handling of the guac_stream has been successfully handed off,
* non-zero if the provided argument has not yet been registered with
* guac_argv_register().
*/
int guac_argv_received(guac_stream* stream, const char* mimetype, const char* name);
/**
* Stops further automatic processing of received "argv" streams. Any call to
* guac_argv_await() that is currently blocking will return, and any future
* calls to guac_argv_await() will return immediately without blocking.
*/
void guac_argv_stop();
/**
* Convenience implementation of the "argv" instruction handler which
* automatically sends any required "ack" instructions and invokes
* guac_argv_received(). Only arguments that are registered with
* guac_argv_register() will be processed.
*/
guac_user_argv_handler guac_argv_handler;
#endif

View File

@ -509,47 +509,18 @@ void* guac_client_for_user(guac_client* client, guac_user* user,
guac_user_callback* callback, void* data); guac_user_callback* callback, void* data);
/** /**
* Marks the end of the current frame by sending a "sync" instruction to all * Marks the end of the current frame by sending a "sync" instruction to
* connected users, where the number of input frames that were considered in * all connected users. This instruction will contain the current timestamp.
* creating this frame is either unknown or inapplicable. This instruction will * The last_sent_timestamp member of guac_client will be updated accordingly.
* 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 * If an error occurs sending the instruction, a non-zero value is
* returned, and guac_error is set appropriately. * returned, and guac_error is set appropriately.
* *
* @param client * @param client The guac_client which has finished a frame.
* The guac_client which has finished a frame. * @return Zero on success, non-zero on error.
*
* @return
* Zero on success, non-zero on error.
*/ */
int guac_client_end_frame(guac_client* client); 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 * Initializes the given guac_client using the initialization routine provided
* by the plugin corresponding to the named protocol. This will automatically * by the plugin corresponding to the named protocol. This will automatically
@ -578,22 +549,6 @@ int guac_client_load_plugin(guac_client* client, const char* protocol);
*/ */
int guac_client_get_processing_lag(guac_client* client); int guac_client_get_processing_lag(guac_client* client);
/**
* Sends a request to the owner of the given guac_client for parameters required
* to continue the connection started by the client. The function returns zero
* on success or non-zero on failure.
*
* @param client
* The client where additional connection parameters are required.
*
* @param required
* The NULL-terminated array of required parameters.
*
* @return
* Zero on success, non-zero on failure.
*/
int guac_client_owner_send_required(guac_client* client, const char** required);
/** /**
* Streams the given connection parameter value over an argument value stream * Streams the given connection parameter value over an argument value stream
* ("argv" instruction), exposing the current value of the named connection * ("argv" instruction), exposing the current value of the named connection
@ -737,72 +692,6 @@ 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"
* 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 "required" instruction.
*
* @return
* Non-zero if the owner of the given client supports the "required"
* instruction, zero otherwise.
*/
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

@ -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_5_0" #define GUACAMOLE_PROTOCOL_VERSION "VERSION_1_1_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
@ -49,20 +49,5 @@
*/ */
#define GUAC_PROTOCOL_BLOB_MAX_LENGTH 6048 #define GUAC_PROTOCOL_BLOB_MAX_LENGTH 6048
/**
* The name of the layer parameter defining the number of simultaneous points
* of contact supported by a layer. This parameter should be set to a non-zero
* value if the associated layer should receive touch events ("touch"
* instructions).
*
* This value specified for this parameter is advisory, and the client is not
* required to honor the declared level of touch support. Implementations are
* expected to safely handle or ignore any received touch events, regardless of
* the level of touch support declared.
*
* @see guac_protocol_send_set_int()
*/
#define GUAC_PROTOCOL_LAYER_PARAMETER_MULTI_TOUCH "multi-touch"
#endif #endif

View File

@ -276,70 +276,5 @@ typedef enum guac_line_join_style {
GUAC_LINE_JOIN_ROUND = 0x2 GUAC_LINE_JOIN_ROUND = 0x2
} guac_line_join_style; } guac_line_join_style;
/**
* The set of protocol versions known to guacd to handle negotiation or feature
* support between differing versions of Guacamole clients and guacd.
*/
typedef enum guac_protocol_version {
/**
* An unknown version of the Guacamole protocol.
*/
GUAC_PROTOCOL_VERSION_UNKNOWN = 0x000000,
/**
* Original protocol version 1.0.0, which lacks support for negotiating
* parameters and protocol version, and requires that parameters in the
* client/server handshake be delivered in order.
*/
GUAC_PROTOCOL_VERSION_1_0_0 = 0x010000,
/**
* Protocol version 1.1.0, which includes support for parameter and version
* negotiation and for sending timezone information from the client
* to the server.
*/
GUAC_PROTOCOL_VERSION_1_1_0 = 0x010100,
/**
* Protocol version 1.3.0, which supports the "required" instruction,
* allowing connections in guacd to request information from the client and
* await a response.
*/
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 #endif

View File

@ -171,27 +171,6 @@ 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.
* *
@ -230,53 +209,6 @@ int guac_protocol_send_msg(guac_socket* socket, guac_message_type msg,
int guac_protocol_send_mouse(guac_socket* socket, int x, int y, int guac_protocol_send_mouse(guac_socket* socket, int x, int y,
int button_mask, guac_timestamp timestamp); int button_mask, guac_timestamp timestamp);
/**
* Sends a touch instruction over the given guac_socket connection.
*
* If an error occurs sending the instruction, a non-zero value is
* returned, and guac_error is set appropriately.
*
* @param socket
* The guac_socket connection to use.
*
* @param id
* An arbitrary integer ID which uniquely identifies this contact relative
* to other active contacts.
*
* @param x
* The X coordinate of the center of the touch contact.
*
* @param y
* The Y coordinate of the center of the touch contact.
*
* @param x_radius
* The X radius of the ellipse covering the general area of the touch
* contact, in pixels.
*
* @param y_radius
* The Y radius of the ellipse covering the general area of the touch
* contact, in pixels.
*
* @param angle
* The rough angle of clockwise rotation of the general area of the touch
* contact, in degrees.
*
* @param force
* The relative force exerted by the touch contact, where 0 is no force
* (the touch has been lifted) and 1 is maximum force (the maximum amount
* of force representable by the device).
*
* @param timestamp
* The server timestamp (in milliseconds) at the point in time this touch
* event was acknowledged.
*
* @return
* Zero on success, non-zero on error.
*/
int guac_protocol_send_touch(guac_socket* socket, int id, int x, int y,
int x_radius, int y_radius, double angle, double force,
guac_timestamp timestamp);
/** /**
* Sends a nest instruction over the given guac_socket connection. * Sends a nest instruction over the given guac_socket connection.
* *
@ -339,32 +271,6 @@ int guac_protocol_send_ready(guac_socket* socket, const char* id);
int guac_protocol_send_set(guac_socket* socket, const guac_layer* layer, int guac_protocol_send_set(guac_socket* socket, const guac_layer* layer,
const char* name, const char* value); const char* name, const char* value);
/**
* Sends a set instruction over the given guac_socket connection. This function
* behavies identically to guac_protocol_send_set() except that the provided
* parameter value is an integer, rather than a string.
*
* If an error occurs sending the instruction, a non-zero value is
* returned, and guac_error is set appropriately.
*
* @param socket
* The guac_socket connection to use.
*
* @param layer
* The layer to set the parameter of.
*
* @param name
* The name of the parameter to set.
*
* @param value
* The value to set the parameter to.
*
* @return
* Zero on success, non-zero on error.
*/
int guac_protocol_send_set_int(guac_socket* socket, const guac_layer* layer,
const char* name, int value);
/** /**
* Sends a select instruction over the given guac_socket connection. * Sends a select instruction over the given guac_socket connection.
* *
@ -384,22 +290,11 @@ int guac_protocol_send_select(guac_socket* socket, const char* protocol);
* If an error occurs sending the instruction, a non-zero value is * If an error occurs sending the instruction, a non-zero value is
* returned, and guac_error is set appropriately. * returned, and guac_error is set appropriately.
* *
* @param socket * @param socket The guac_socket connection to use.
* The guac_socket connection to use. * @param timestamp The current timestamp (in milliseconds).
* * @return Zero on success, non-zero on error.
* @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 */ /* OBJECT INSTRUCTIONS */
@ -899,22 +794,6 @@ int guac_protocol_send_push(guac_socket* socket, const guac_layer* layer);
int guac_protocol_send_rect(guac_socket* socket, const guac_layer* layer, int guac_protocol_send_rect(guac_socket* socket, const guac_layer* layer,
int x, int y, int width, int height); int x, int y, int width, int height);
/**
* Sends a "required" instruction over the given guac_socket connection. This
* instruction indicates to the client that one or more additional parameters
* are needed to continue the connection.
*
* @param socket
* The guac_socket connection to which to send the instruction.
*
* @param required
* A NULL-terminated array of required parameters.
*
* @return
* Zero on success, non-zero on error.
*/
int guac_protocol_send_required(guac_socket* socket, const char** required);
/** /**
* Sends a reset instruction over the given guac_socket connection. * Sends a reset instruction over the given guac_socket connection.
* *
@ -1128,32 +1007,5 @@ int guac_protocol_send_name(guac_socket* socket, const char* name);
*/ */
int guac_protocol_decode_base64(char* base64); int guac_protocol_decode_base64(char* base64);
/**
* Given a string representation of a protocol version, return the enum value of
* that protocol version, or GUAC_PROTOCOL_VERSION_UNKNOWN if the value is not a
* known version.
*
* @param version_string
* The string representation of the protocol version.
*
* @return
* The enum value of the protocol version, or GUAC_PROTOCOL_VERSION_UNKNOWN
* if the provided version is not known.
*/
guac_protocol_version guac_protocol_string_to_version(const char* version_string);
/**
* Given the enum value of the protocol version, return a pointer to the string
* representation of the version, or NULL if the version is unknown.
*
* @param version
* The enum value of the protocol version.
*
* @return
* A pointer to the string representation of the protocol version, or NULL
* if the version is unknown.
*/
const char* guac_protocol_version_to_string(guac_protocol_version version);
#endif #endif

View File

@ -109,41 +109,6 @@ 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); 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.
*
* @param str
* The string to duplicate as a newly-allocated string.
*
* @return
* A newly-allocated string containing identically the same content as the
* given string, or NULL if the given string was NULL.
*/
char* guac_strdup(const char* str);
/** /**
* Concatenates each of the given strings, separated by the given delimiter, * Concatenates each of the given strings, separated by the given delimiter,
* storing the result within a destination buffer. The number of bytes written * storing the result within a destination buffer. The number of bytes written

View File

@ -95,51 +95,6 @@ typedef void* guac_user_callback(guac_user* user, void* data);
typedef int guac_user_mouse_handler(guac_user* user, int x, int y, typedef int guac_user_mouse_handler(guac_user* user, int x, int y,
int button_mask); int button_mask);
/**
* Handler for Guacamole touch events, invoked when a "touch" instruction has
* been received from a user.
*
* @param user
* The user that sent the touch event.
*
* @param id
* An arbitrary integer ID which uniquely identifies this contact relative
* to other active contacts.
*
* @param x
* The X coordinate of the center of the touch contact within the display
* when the event occurred, in pixels. This value is not guaranteed to be
* within the bounds of the display area.
*
* @param y
* The Y coordinate of the center of the touch contact within the display
* when the event occurred, in pixels. This value is not guaranteed to be
* within the bounds of the display area.
*
* @param x_radius
* The X radius of the ellipse covering the general area of the touch
* contact, in pixels.
*
* @param y_radius
* The Y radius of the ellipse covering the general area of the touch
* contact, in pixels.
*
* @param angle
* The rough angle of clockwise rotation of the general area of the touch
* contact, in degrees.
*
* @param force
* The relative force exerted by the touch contact, where 0 is no force
* (the touch has been lifted) and 1 is maximum force (the maximum amount
* of force representable by the device).
*
* @return
* Zero if the touch event was handled successfully, or non-zero if an
* error occurred.
*/
typedef int guac_user_touch_handler(guac_user* user, int id, int x, int y,
int x_radius, int y_radius, double angle, double force);
/** /**
* Handler for Guacamole key events, invoked when a "key" event has been * Handler for Guacamole key events, invoked when a "key" event has been
* received from a user. * received from a user.

View File

@ -88,27 +88,13 @@ struct guac_user_info {
* stated resolution of the display size request is recommended. * stated resolution of the display size request is recommended.
*/ */
int optimal_resolution; int optimal_resolution;
/** /**
* The timezone of the remote system. If the client does not provide * 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 * a specific timezone then this will be NULL. The format of the timezone
* is the standard tzdata naming convention. * is the standard tzdata naming convention.
*/ */
const char* timezone; const char* timezone;
/**
* The Guacamole protocol version that the remote system supports, allowing
* for feature support to be negotiated between client and server.
*/
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;
}; };
@ -517,27 +503,6 @@ struct guac_user {
*/ */
guac_user_argv_handler* argv_handler; guac_user_argv_handler* argv_handler;
/**
* Handler for touch events sent by the Guacamole web-client.
*
* The handler takes the integer X and Y coordinates representing the
* center of the touch contact, as well as several parameters describing
* the general shape of the contact area. The force parameter indicates the
* amount of force exerted by the contact, including whether the contact
* has been lifted.
*
* Example:
* @code
* int touch_handler(guac_user* user, int id, int x, int y,
* int x_radius, int y_radius, double angle, double force);
*
* int guac_user_init(guac_user* user, int argc, char** argv) {
* user->touch_handler = touch_handler;
* }
* @endcode
*/
guac_user_touch_handler* touch_handler;
}; };
/** /**
@ -858,28 +823,6 @@ 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.
*
* @param user
* The Guacamole user to check for support of the "required" instruction.
*
* @return
* Non-zero if the user supports the "required" instruction, otherwise zero.
*/
int guac_user_supports_required(guac_user* user);
/** /**
* Returns whether the given user supports WebP. If the user does not * Returns whether the given user supports WebP. If the user does not
* support WebP, or the server cannot encode WebP images, zero is returned. * support WebP, or the server cannot encode WebP images, zero is returned.

View File

@ -1,51 +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_WOL_CONSTANTS_H
#define GUAC_WOL_CONSTANTS_H
/**
* Header file that provides constants and defaults related to libguac
* Wake-on-LAN support.
*
* @file wol-constants.h
*/
/**
* The value for the local IPv4 broadcast address.
*/
#define GUAC_WOL_LOCAL_IPV4_BROADCAST "255.255.255.255"
/**
* The size of the magic Wake-on-LAN packet to send to wake a remote host. This
* consists of 6 bytes of 0xFF, and then the MAC address repeated 16 times.
* https://en.wikipedia.org/wiki/Wake-on-LAN#Magic_packet
*/
#define GUAC_WOL_PACKET_SIZE 102
/**
* The port number that the magic packet should contain as the destination. In
* reality this doesn't matter all that much, since the packet is not usually
* processed by a full IP stack, but defining one is considered a standard
* practice.
*/
#define GUAC_WOL_PORT 9
#endif /* GUAC_WOL_CONSTANTS_H */

View File

@ -1,56 +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_WOL_H
#define GUAC_WOL_H
/**
* Header that provides functions and structures related to Wake-on-LAN
* support in libguac.
*
* @file wol.h
*/
#include "wol-constants.h"
/**
* Send the wake-up packet to the specified destination, returning zero if the
* wake was sent successfully, or non-zero if an error occurs sending the
* wake packet. Note that the return value does not specify whether the
* system actually wakes up successfully, only whether or not the packet
* is transmitted.
*
* @param mac_addr
* The MAC address to place in the magic Wake-on-LAN packet.
*
* @param broadcast_addr
* The broadcast address to which to send the magic Wake-on-LAN packet.
*
* @param udp_port
* The UDP port to use when sending the WoL packet.
*
* @return
* Zero if the packet is successfully sent to the destination; non-zero
* if the packet cannot be sent.
*/
int guac_wol_wake(const char* mac_addr, const char* broadcast_addr,
const unsigned short udp_port);
#endif /* GUAC_WOL_H */

View File

@ -22,9 +22,7 @@
#include "guacamole/error.h" #include "guacamole/error.h"
#include "id.h" #include "id.h"
#if defined(HAVE_LIBUUID) #ifdef HAVE_OSSP_UUID_H
#include <uuid/uuid.h>
#elif defined(HAVE_OSSP_UUID_H)
#include <ossp/uuid.h> #include <ossp/uuid.h>
#else #else
#include <uuid.h> #include <uuid.h>
@ -32,73 +30,54 @@
#include <stdlib.h> #include <stdlib.h>
/**
* The length of a UUID in bytes. All UUIDs are guaranteed to be 36 1-byte
* characters long.
*/
#define GUAC_UUID_LEN 36
char* guac_generate_id(char prefix) { char* guac_generate_id(char prefix) {
char* buffer; char* buffer;
char* identifier; char* identifier;
size_t identifier_length;
/* Prepare object to receive generated UUID */
#ifdef HAVE_LIBUUID
uuid_t uuid;
#else
uuid_t* uuid; uuid_t* uuid;
/* Attempt to create UUID object */
if (uuid_create(&uuid) != UUID_RC_OK) { if (uuid_create(&uuid) != UUID_RC_OK) {
guac_error = GUAC_STATUS_NO_MEMORY; guac_error = GUAC_STATUS_NO_MEMORY;
guac_error_message = "Could not allocate memory for UUID"; guac_error_message = "Could not allocate memory for UUID";
return NULL; return NULL;
} }
#endif
/* Generate unique identifier */ /* Generate random UUID */
#ifdef HAVE_LIBUUID
uuid_generate(uuid);
#else
if (uuid_make(uuid, UUID_MAKE_V4) != UUID_RC_OK) { if (uuid_make(uuid, UUID_MAKE_V4) != UUID_RC_OK) {
uuid_destroy(uuid); uuid_destroy(uuid);
guac_error = GUAC_STATUS_NO_MEMORY; guac_error = GUAC_STATUS_NO_MEMORY;
guac_error_message = "UUID generation failed"; guac_error_message = "UUID generation failed";
return NULL; return NULL;
} }
#endif
/* Allocate buffer for future formatted ID */ /* Allocate buffer for future formatted ID */
buffer = malloc(GUAC_UUID_LEN + 2); buffer = malloc(UUID_LEN_STR + 2);
if (buffer == NULL) { if (buffer == NULL) {
#ifndef HAVE_LIBUUID
uuid_destroy(uuid); uuid_destroy(uuid);
#endif
guac_error = GUAC_STATUS_NO_MEMORY; guac_error = GUAC_STATUS_NO_MEMORY;
guac_error_message = "Could not allocate memory for unique ID"; guac_error_message = "Could not allocate memory for connection ID";
return NULL; return NULL;
} }
identifier = &(buffer[1]); identifier = &(buffer[1]);
identifier_length = UUID_LEN_STR + 1;
/* Convert UUID to string to produce unique identifier */ /* Build connection ID from UUID */
#ifdef HAVE_LIBUUID
uuid_unparse_lower(uuid, identifier);
#else
size_t identifier_length = GUAC_UUID_LEN + 1;
if (uuid_export(uuid, UUID_FMT_STR, &identifier, &identifier_length) != UUID_RC_OK) { if (uuid_export(uuid, UUID_FMT_STR, &identifier, &identifier_length) != UUID_RC_OK) {
free(buffer); free(buffer);
uuid_destroy(uuid); uuid_destroy(uuid);
guac_error = GUAC_STATUS_INTERNAL_ERROR; guac_error = GUAC_STATUS_INTERNAL_ERROR;
guac_error_message = "Conversion of UUID to unique ID failed"; guac_error_message = "Conversion of UUID to connection ID failed";
return NULL; return NULL;
} }
/* Clean up generated UUID */
uuid_destroy(uuid); uuid_destroy(uuid);
#endif
buffer[0] = prefix; buffer[0] = prefix;
buffer[GUAC_UUID_LEN + 1] = '\0'; buffer[UUID_LEN_STR + 1] = '\0';
return buffer; return buffer;
} }

View File

@ -23,7 +23,6 @@
#include "guacamole/layer.h" #include "guacamole/layer.h"
#include "guacamole/object.h" #include "guacamole/object.h"
#include "guacamole/protocol.h" #include "guacamole/protocol.h"
#include "guacamole/protocol-types.h"
#include "guacamole/socket.h" #include "guacamole/socket.h"
#include "guacamole/stream.h" #include "guacamole/stream.h"
#include "guacamole/unicode.h" #include "guacamole/unicode.h"
@ -40,35 +39,6 @@
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
/**
* A structure mapping the enum value of a Guacamole protocol version to the
* string representation of the version.
*/
typedef struct guac_protocol_version_mapping {
/**
* The enum value of the protocol version.
*/
guac_protocol_version version;
/**
* The string value representing the protocol version.
*/
char* version_string;
} guac_protocol_version_mapping;
/**
* The map of known protocol versions to the corresponding string value.
*/
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 }
};
/* Output formatting functions */ /* Output formatting functions */
ssize_t __guac_socket_write_length_string(guac_socket* socket, const char* str) { ssize_t __guac_socket_write_length_string(guac_socket* socket, const char* str) {
@ -96,38 +66,6 @@ ssize_t __guac_socket_write_length_double(guac_socket* socket, double d) {
} }
/**
* Loop through the provided NULL-terminated array, writing the values in the
* array to the given socket. Values are written as a series of Guacamole
* protocol elements, including the leading comma and the value length in
* addition to the value itself. Returns zero on success, non-zero on error.
*
* @param socket
* The socket to which the data should be written.
*
* @param array
* The NULL-terminated array of values to write.
*
* @return
* Zero on success, non-zero on error.
*/
static int guac_socket_write_array(guac_socket* socket, const char** array) {
/* Loop through array, writing provided values to the socket. */
for (int i=0; array[i] != NULL; i++) {
if (guac_socket_write_string(socket, ","))
return -1;
if (__guac_socket_write_length_string(socket, array[i]))
return -1;
}
return 0;
}
/* Protocol functions */ /* Protocol functions */
int guac_protocol_send_ack(guac_socket* socket, guac_stream* stream, int guac_protocol_send_ack(guac_socket* socket, guac_stream* stream,
@ -152,6 +90,8 @@ int guac_protocol_send_ack(guac_socket* socket, guac_stream* stream,
static int __guac_protocol_send_args(guac_socket* socket, const char** args) { static int __guac_protocol_send_args(guac_socket* socket, const char** args) {
int i;
if (guac_socket_write_string(socket, "4.args")) return -1; if (guac_socket_write_string(socket, "4.args")) return -1;
/* Send protocol version ahead of other args. */ /* Send protocol version ahead of other args. */
@ -159,8 +99,15 @@ static int __guac_protocol_send_args(guac_socket* socket, const char** args) {
|| __guac_socket_write_length_string(socket, GUACAMOLE_PROTOCOL_VERSION)) || __guac_socket_write_length_string(socket, GUACAMOLE_PROTOCOL_VERSION))
return -1; return -1;
if (guac_socket_write_array(socket, args)) for (i=0; args[i] != NULL; i++) {
return -1;
if (guac_socket_write_string(socket, ","))
return -1;
if (__guac_socket_write_length_string(socket, args[i]))
return -1;
}
return guac_socket_write_string(socket, ";"); return guac_socket_write_string(socket, ";");
@ -361,11 +308,19 @@ int guac_protocol_send_close(guac_socket* socket, const guac_layer* layer) {
static int __guac_protocol_send_connect(guac_socket* socket, const char** args) { static int __guac_protocol_send_connect(guac_socket* socket, const char** args) {
if (guac_socket_write_string(socket, "7.connect")) int i;
return -1;
if (guac_socket_write_array(socket, args)) if (guac_socket_write_string(socket, "7.connect")) return -1;
return -1;
for (i=0; args[i] != NULL; i++) {
if (guac_socket_write_string(socket, ","))
return -1;
if (__guac_socket_write_length_string(socket, args[i]))
return -1;
}
return guac_socket_write_string(socket, ";"); return guac_socket_write_string(socket, ";");
@ -659,23 +614,6 @@ 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) {
@ -838,37 +776,6 @@ int guac_protocol_send_mouse(guac_socket* socket, int x, int y,
} }
int guac_protocol_send_touch(guac_socket* socket, int id, int x, int y,
int x_radius, int y_radius, double angle, double force,
guac_timestamp timestamp) {
int ret_val;
guac_socket_instruction_begin(socket);
ret_val =
guac_socket_write_string(socket, "5.touch,")
|| __guac_socket_write_length_int(socket, id)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_int(socket, x)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_int(socket, y)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_int(socket, x_radius)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_int(socket, y_radius)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_double(socket, angle)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_double(socket, force)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_int(socket, timestamp)
|| guac_socket_write_string(socket, ";");
guac_socket_instruction_end(socket);
return ret_val;
}
int guac_protocol_send_move(guac_socket* socket, const guac_layer* layer, int guac_protocol_send_move(guac_socket* socket, const guac_layer* layer,
const guac_layer* parent, int x, int y, int z) { const guac_layer* parent, int x, int y, int z) {
@ -1054,23 +961,6 @@ int guac_protocol_send_rect(guac_socket* socket,
} }
int guac_protocol_send_required(guac_socket* socket, const char** required) {
int ret_val;
guac_socket_instruction_begin(socket);
ret_val = guac_socket_write_string(socket, "8.required")
|| guac_socket_write_array(socket, required)
|| guac_socket_write_string(socket, ";")
|| guac_socket_flush(socket);
guac_socket_instruction_end(socket);
return ret_val;
}
int guac_protocol_send_reset(guac_socket* socket, const guac_layer* layer) { int guac_protocol_send_reset(guac_socket* socket, const guac_layer* layer) {
int ret_val; int ret_val;
@ -1106,26 +996,6 @@ int guac_protocol_send_set(guac_socket* socket, const guac_layer* layer,
} }
int guac_protocol_send_set_int(guac_socket* socket, const guac_layer* layer,
const char* name, int value) {
int ret_val;
guac_socket_instruction_begin(socket);
ret_val =
guac_socket_write_string(socket, "3.set,")
|| __guac_socket_write_length_int(socket, layer->index)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_string(socket, name)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_int(socket, value)
|| guac_socket_write_string(socket, ";");
guac_socket_instruction_end(socket);
return ret_val;
}
int guac_protocol_send_select(guac_socket* socket, const char* protocol) { int guac_protocol_send_select(guac_socket* socket, const char* protocol) {
int ret_val; int ret_val;
@ -1199,8 +1069,7 @@ 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; int ret_val;
@ -1208,8 +1077,6 @@ int guac_protocol_send_sync(guac_socket* socket, guac_timestamp timestamp,
ret_val = ret_val =
guac_socket_write_string(socket, "4.sync,") guac_socket_write_string(socket, "4.sync,")
|| __guac_socket_write_length_int(socket, timestamp) || __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_write_string(socket, ";");
guac_socket_instruction_end(socket); guac_socket_instruction_end(socket);
@ -1374,35 +1241,3 @@ int guac_protocol_decode_base64(char* base64) {
} }
guac_protocol_version guac_protocol_string_to_version(const char* version_string) {
guac_protocol_version_mapping* current = guac_protocol_version_table;
while (current->version != GUAC_PROTOCOL_VERSION_UNKNOWN) {
if (strcmp(current->version_string, version_string) == 0)
return current->version;
current++;
}
return GUAC_PROTOCOL_VERSION_UNKNOWN;
}
const char* guac_protocol_version_to_string(guac_protocol_version version) {
guac_protocol_version_mapping* current = guac_protocol_version_table;
while (current->version != GUAC_PROTOCOL_VERSION_UNKNOWN) {
if (current->version == version)
return (const char*) current->version_string;
current++;
}
return NULL;
}

View File

@ -44,8 +44,6 @@ char __guac_socket_BASE64_CHARACTERS[64] = {
static void* __guac_socket_keep_alive_thread(void* data) { static void* __guac_socket_keep_alive_thread(void* data) {
int old_cancelstate;
/* Calculate sleep interval */ /* Calculate sleep interval */
struct timespec interval; struct timespec interval;
interval.tv_sec = GUAC_SOCKET_KEEP_ALIVE_INTERVAL / 1000; interval.tv_sec = GUAC_SOCKET_KEEP_ALIVE_INTERVAL / 1000;
@ -67,11 +65,8 @@ static void* __guac_socket_keep_alive_thread(void* data) {
} }
/* Sleep until next keep-alive check, but allow thread cancellation /* Sleep until next keep-alive check */
* during that sleep */
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_cancelstate);
nanosleep(&interval, NULL); nanosleep(&interval, NULL);
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelstate);
} }
@ -207,11 +202,9 @@ void guac_socket_free(guac_socket* socket) {
/* Mark as closed */ /* Mark as closed */
socket->state = GUAC_SOCKET_CLOSED; socket->state = GUAC_SOCKET_CLOSED;
/* Stop keep-alive thread, if enabled */ /* Wait for keep-alive, if enabled */
if (socket->__keep_alive_enabled) { if (socket->__keep_alive_enabled)
pthread_cancel(socket->__keep_alive_thread);
pthread_join(socket->__keep_alive_thread, NULL); pthread_join(socket->__keep_alive_thread, NULL);
}
free(socket); free(socket);
} }

View File

@ -81,49 +81,6 @@ 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 */
if (str == NULL)
return NULL;
/* Otherwise just invoke strdup() */
return strdup(str);
}
size_t guac_strljoin(char* restrict dest, const char* restrict const* elements, size_t guac_strljoin(char* restrict dest, const char* restrict const* elements,
int nmemb, const char* restrict delim, size_t n) { int nmemb, const char* restrict delim, size_t n) {

View File

@ -36,19 +36,15 @@ TESTS = $(check_PROGRAMS)
test_libguac_SOURCES = \ test_libguac_SOURCES = \
client/buffer_pool.c \ client/buffer_pool.c \
client/layer_pool.c \ client/layer_pool.c \
id/generate.c \
parser/append.c \ parser/append.c \
parser/read.c \ parser/read.c \
pool/next_free.c \ pool/next_free.c \
protocol/base64_decode.c \ protocol/base64_decode.c \
protocol/guac_protocol_version.c \
socket/fd_send_instruction.c \ socket/fd_send_instruction.c \
socket/nested_send_instruction.c \ socket/nested_send_instruction.c \
string/strdup.c \
string/strlcat.c \ string/strlcat.c \
string/strlcpy.c \ string/strlcpy.c \
string/strljoin.c \ string/strljoin.c \
string/strnstr.c \
unicode/charsize.c \ unicode/charsize.c \
unicode/read.c \ unicode/read.c \
unicode/strlen.c \ unicode/strlen.c \

View File

@ -1,88 +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 "id.h"
#include <CUnit/CUnit.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/**
* Test which verifies that each call to guac_generate_id() produces a
* different string.
*/
void test_id__unique() {
char* id1 = guac_generate_id('x');
char* id2 = guac_generate_id('x');
/* Neither string may be NULL */
CU_ASSERT_PTR_NOT_NULL_FATAL(id1);
CU_ASSERT_PTR_NOT_NULL_FATAL(id2);
/* Both strings should be different */
CU_ASSERT_STRING_NOT_EQUAL(id1, id2);
free(id1);
free(id2);
}
/**
* Test which verifies that guac_generate_id() produces strings are in the
* correc UUID-based format.
*/
void test_id__format() {
unsigned int ignore;
char* id = guac_generate_id('x');
CU_ASSERT_PTR_NOT_NULL_FATAL(id);
int items_read = sscanf(id, "x%08x-%04x-%04x-%04x-%08x%04x",
&ignore, &ignore, &ignore, &ignore, &ignore, &ignore);
CU_ASSERT_EQUAL(items_read, 6);
CU_ASSERT_EQUAL(strlen(id), 37);
free(id);
}
/**
* Test which verifies that guac_generate_id() takes the specified prefix
* character into account when generating the ID string.
*/
void test_id__prefix() {
char* id;
id = guac_generate_id('a');
CU_ASSERT_PTR_NOT_NULL_FATAL(id);
CU_ASSERT_EQUAL(id[0], 'a');
free(id);
id = guac_generate_id('b');
CU_ASSERT_PTR_NOT_NULL_FATAL(id);
CU_ASSERT_EQUAL(id[0], 'b');
free(id);
}

View File

@ -72,7 +72,7 @@ static void write_instructions(int fd) {
/** /**
* Reads and parses instructions from the given file descriptor using a * Reads and parses instructions from the given file descriptor using a
* guac_socket and guac_parser, verifying that those instructions match the * guac_socket and guac_parser, verfying that those instructions match the
* series of Guacamole instructions expected to be written by * series of Guacamole instructions expected to be written by
* write_instructions(). The given file descriptor is automatically closed as a * write_instructions(). The given file descriptor is automatically closed as a
* result of calling this function. * result of calling this function.

View File

@ -1,69 +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 <CUnit/CUnit.h>
#include <guacamole/protocol.h>
#include <guacamole/protocol-types.h>
/**
* Test which verifies that conversion of the guac_protocol_version enum to
* string values succeeds and produces the expected results.
*/
void test_guac_protocol__version_to_string() {
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_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));
}
/**
* Test which verifies that the version of String representations of Guacamole
* protocol versions are successfully converted into their matching
* guac_protocol_version enum values, and that versions that do not match
* any version get the correct unknown value.
*/
void test_guac_protocol__string_to_version() {
char* str_version_a = "VERSION_1_3_0";
char* str_version_b = "VERSION_1_1_0";
char* str_version_c = "AVACADO";
char* str_version_d = "VERSION_31_4_1";
CU_ASSERT_EQUAL(guac_protocol_string_to_version(str_version_a), GUAC_PROTOCOL_VERSION_1_3_0);
CU_ASSERT_EQUAL(guac_protocol_string_to_version(str_version_b), GUAC_PROTOCOL_VERSION_1_1_0);
CU_ASSERT_EQUAL(guac_protocol_string_to_version(str_version_c), GUAC_PROTOCOL_VERSION_UNKNOWN);
CU_ASSERT_EQUAL(guac_protocol_string_to_version(str_version_d), GUAC_PROTOCOL_VERSION_UNKNOWN);
}
/**
* Test which verifies that the comparisons between guac_protocol_version enum
* values produces the expected results.
*/
void test_gauc_protocol__version_comparison() {
CU_ASSERT_TRUE(GUAC_PROTOCOL_VERSION_1_3_0 > GUAC_PROTOCOL_VERSION_1_0_0);
CU_ASSERT_TRUE(GUAC_PROTOCOL_VERSION_UNKNOWN < GUAC_PROTOCOL_VERSION_1_1_0);
}

View File

@ -54,7 +54,7 @@ static void write_instructions(int fd) {
/* Write instructions */ /* Write instructions */
guac_protocol_send_name(socket, "a" UTF8_4 "b" UTF8_4 "c"); guac_protocol_send_name(socket, "a" UTF8_4 "b" UTF8_4 "c");
guac_protocol_send_sync(socket, 12345, 1); guac_protocol_send_sync(socket, 12345);
guac_socket_flush(socket); guac_socket_flush(socket);
/* Close and free socket */ /* Close and free socket */
@ -64,7 +64,7 @@ static void write_instructions(int fd) {
/** /**
* Reads raw bytes from the given file descriptor until no further bytes * Reads raw bytes from the given file descriptor until no further bytes
* remain, verifying that those bytes represent the series of Guacamole * remain, verfying that those bytes represent the series of Guacamole
* instructions expected to be written by write_instructions(). The given * instructions expected to be written by write_instructions(). The given
* file descriptor is automatically closed as a result of calling this * file descriptor is automatically closed as a result of calling this
* function. * function.
@ -76,7 +76,7 @@ static void read_expected_instructions(int fd) {
char expected[] = char expected[] =
"4.name,11.a" UTF8_4 "b" UTF8_4 "c;" "4.name,11.a" UTF8_4 "b" UTF8_4 "c;"
"4.sync,5.12345,1.1;"; "4.sync,5.12345;";
int numread; int numread;
char buffer[1024]; char buffer[1024];

View File

@ -65,7 +65,7 @@ static void write_instructions(int fd) {
/* Write instructions */ /* Write instructions */
guac_protocol_send_name(nested_socket, "a" UTF8_4 "b" UTF8_4 "c"); guac_protocol_send_name(nested_socket, "a" UTF8_4 "b" UTF8_4 "c");
guac_protocol_send_sync(nested_socket, 12345, 1); guac_protocol_send_sync(nested_socket, 12345);
/* Close and free sockets */ /* Close and free sockets */
guac_socket_free(nested_socket); guac_socket_free(nested_socket);
@ -75,7 +75,7 @@ static void write_instructions(int fd) {
/** /**
* Reads raw bytes from the given file descriptor until no further bytes * Reads raw bytes from the given file descriptor until no further bytes
* remain, verifying that those bytes represent the series of Guacamole * remain, verfying that those bytes represent the series of Guacamole
* instructions expected to be written by write_instructions(). The given * instructions expected to be written by write_instructions(). The given
* file descriptor is automatically closed as a result of calling this * file descriptor is automatically closed as a result of calling this
* function. * function.
@ -86,9 +86,9 @@ static void write_instructions(int fd) {
static void read_expected_instructions(int fd) { static void read_expected_instructions(int fd) {
char expected[] = char expected[] =
"4.nest,3.123,41." "4.nest,3.123,37."
"4.name,11.a" UTF8_4 "b" UTF8_4 "c;" "4.name,11.a" UTF8_4 "b" UTF8_4 "c;"
"4.sync,5.12345,1.1;" "4.sync,5.12345;"
";"; ";";
int numread; int numread;

View File

@ -1,49 +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 <CUnit/CUnit.h>
#include <guacamole/string.h>
#include <stdlib.h>
#include <string.h>
/**
* Source test string for copying.
*/
const char* source_string = "Mashing avocados.";
/**
* A NULL string variable for copying to insure that NULL is copied properly.
*/
const char* null_string = NULL;
/**
* Verify guac_strdup() behavior when the string is both NULL and not NULL.
*/
void test_string__strdup() {
/* Copy the strings. */
char* dest_string = guac_strdup(source_string);
char* null_copy = guac_strdup(null_string);
/* Run the tests. */
CU_ASSERT_STRING_EQUAL(dest_string, "Mashing avocados.");
CU_ASSERT_PTR_NULL(null_copy);
}

View File

@ -1,73 +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 <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

@ -37,7 +37,6 @@
__guac_instruction_handler_mapping __guac_instruction_handler_map[] = { __guac_instruction_handler_mapping __guac_instruction_handler_map[] = {
{"sync", __guac_handle_sync}, {"sync", __guac_handle_sync},
{"touch", __guac_handle_touch},
{"mouse", __guac_handle_mouse}, {"mouse", __guac_handle_mouse},
{"key", __guac_handle_key}, {"key", __guac_handle_key},
{"clipboard", __guac_handle_clipboard}, {"clipboard", __guac_handle_clipboard},
@ -64,7 +63,6 @@ __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}
}; };
@ -121,60 +119,37 @@ int __guac_handle_sync(guac_user* user, int argc, char** argv) {
/* Calculate length of frame, including network and processing lag */ /* Calculate length of frame, including network and processing lag */
frame_duration = current - timestamp; frame_duration = current - timestamp;
/* Calculate processing lag portion of length of frame */ /* Update lag statistics if at least one frame has been rendered */
int frame_processing_lag = 0;
if (user->last_frame_duration != 0) { if (user->last_frame_duration != 0) {
/* Calculate lag using the previous frame as a baseline */ /* Calculate lag using the previous frame as a baseline */
frame_processing_lag = frame_duration - user->last_frame_duration; int processing_lag = frame_duration - user->last_frame_duration;
/* Adjust back to zero if cumulative error leads to a negative /* Adjust back to zero if cumulative error leads to a negative
* value */ * value */
if (frame_processing_lag < 0) if (processing_lag < 0)
frame_processing_lag = 0; processing_lag = 0;
user->processing_lag = processing_lag;
} }
/* Record baseline duration of frame by excluding lag (this is the /* Record baseline duration of frame by excluding lag */
* network round-trip time) */ user->last_frame_duration = frame_duration - user->processing_lag;
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) */ /* Log received timestamp and calculated lag (at TRACE level only) */
guac_user_log(user, GUAC_LOG_TRACE, guac_user_log(user, GUAC_LOG_TRACE,
"User confirmation of frame %" PRIu64 "ms received " "User confirmation of frame %" PRIu64 "ms received "
"at %" PRIu64 "ms (processing_lag=%ims, estimated_rtt=%ims)", "at %" PRIu64 "ms (processing_lag=%ims)",
timestamp, current, user->processing_lag, user->last_frame_duration); timestamp, current, user->processing_lag);
if (user->sync_handler) if (user->sync_handler)
return user->sync_handler(user, timestamp); return user->sync_handler(user, timestamp);
return 0; return 0;
} }
int __guac_handle_touch(guac_user* user, int argc, char** argv) {
if (user->touch_handler)
return user->touch_handler(
user,
atoi(argv[0]), /* id */
atoi(argv[1]), /* x */
atoi(argv[2]), /* y */
atoi(argv[3]), /* x_radius */
atoi(argv[4]), /* y_radius */
atof(argv[5]), /* angle */
atof(argv[6]) /* force */
);
return 0;
}
int __guac_handle_mouse(guac_user* user, int argc, char** argv) { int __guac_handle_mouse(guac_user* user, int argc, char** argv) {
if (user->mouse_handler) if (user->mouse_handler)
return user->mouse_handler( return user->mouse_handler(
@ -484,6 +459,10 @@ int __guac_handle_ack(guac_user* user, int argc, char** argv) {
int __guac_handle_blob(guac_user* user, int argc, char** argv) { int __guac_handle_blob(guac_user* user, int argc, char** argv) {
/* Fail if we have less than two arguments. */
if (argc < 2)
return 0;
int stream_index = atoi(argv[0]); int stream_index = atoi(argv[0]);
guac_stream* stream = __get_open_input_stream(user, stream_index); guac_stream* stream = __get_open_input_stream(user, stream_index);
@ -685,23 +664,6 @@ 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

@ -85,13 +85,6 @@ __guac_instruction_handler __guac_handle_sync;
*/ */
__guac_instruction_handler __guac_handle_mouse; __guac_instruction_handler __guac_handle_mouse;
/**
* Internal initial handler for the touch instruction. When a touch instruction
* is received, this handler will be called. The client's touch handler will
* be invoked if defined.
*/
__guac_instruction_handler __guac_handle_touch;
/** /**
* Internal initial handler for the key instruction. When a key instruction * Internal initial handler for the key instruction. When a key instruction
* is received, this handler will be called. The client's key handler will * is received, this handler will be called. The client's key handler will
@ -218,13 +211,6 @@ __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

@ -100,7 +100,7 @@ static void guac_user_log_guac_error(guac_user* user,
static void guac_user_log_handshake_failure(guac_user* user) { static void guac_user_log_handshake_failure(guac_user* user) {
if (guac_error == GUAC_STATUS_CLOSED) if (guac_error == GUAC_STATUS_CLOSED)
guac_user_log(user, GUAC_LOG_DEBUG, guac_user_log(user, GUAC_LOG_INFO,
"Guacamole connection closed during handshake"); "Guacamole connection closed during handshake");
else if (guac_error == GUAC_STATUS_PROTOCOL_ERROR) else if (guac_error == GUAC_STATUS_PROTOCOL_ERROR)
guac_user_log(user, GUAC_LOG_ERROR, guac_user_log(user, GUAC_LOG_ERROR,
@ -296,7 +296,6 @@ 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. */
@ -345,16 +344,12 @@ int guac_user_handle_connection(guac_user* user, int usec_timeout) {
guac_client_log(client, GUAC_LOG_INFO, "User \"%s\" joined connection " guac_client_log(client, GUAC_LOG_INFO, "User \"%s\" joined connection "
"\"%s\" (%i users now present)", user->user_id, "\"%s\" (%i users now present)", user->user_id,
client->connection_id, client->connected_users); client->connection_id, client->connected_users);
if (strcmp(parser->argv[0],"") != 0) { if (strcmp(parser->argv[0],"") != 0)
guac_client_log(client, GUAC_LOG_DEBUG, "Client is using protocol " guac_client_log(client, GUAC_LOG_DEBUG, "Client is using protocol "
"version \"%s\"", parser->argv[0]); "version \"%s\"", parser->argv[0]);
user->info.protocol_version = guac_protocol_string_to_version(parser->argv[0]); else
}
else {
guac_client_log(client, GUAC_LOG_DEBUG, "Client has not defined " guac_client_log(client, GUAC_LOG_DEBUG, "Client has not defined "
"its protocol version."); "its protocol version.");
user->info.protocol_version = GUAC_PROTOCOL_VERSION_1_0_0;
}
/* Handle user I/O, wait for connection to terminate */ /* Handle user I/O, wait for connection to terminate */
guac_user_start(parser, user, usec_timeout); guac_user_start(parser, user, usec_timeout);
@ -371,8 +366,7 @@ 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 name and timezone info. */ /* Free 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,24 +316,6 @@ 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)
return 0;
return (user->info.protocol_version >= GUAC_PROTOCOL_VERSION_1_3_0);
}
int guac_user_supports_webp(guac_user* user) { int guac_user_supports_webp(guac_user* user) {
#ifdef ENABLE_WEBP #ifdef ENABLE_WEBP

View File

@ -1,198 +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 "guacamole/error.h"
#include "guacamole/wol.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
/**
* Generate the magic Wake-on-LAN (WoL) packet for the specified MAC address
* and place it in the character array.
*
* @param packet
* The character array that will contain the generated packet.
*
* @param mac_address
* The unsigned int representation of the MAC address to place in the packet.
*/
static void __guac_wol_create_magic_packet(unsigned char packet[],
unsigned int mac_address[]) {
int i;
unsigned char mac[6];
/* Concurrently fill the first part of the packet with 0xFF, and copy the
MAC address from the int array to the char array. */
for (i = 0; i < 6; i++) {
packet[i] = 0xFF;
mac[i] = mac_address[i];
}
/* Copy the MAC address contents into the char array that is storing
the rest of the packet. */
for (i = 1; i <= 16; i++) {
memcpy(&packet[i * 6], &mac, 6 * sizeof(unsigned char));
}
}
/**
* Send the magic Wake-on-LAN (WoL) packet to the specified broadcast address,
* returning the number of bytes sent, or zero if any error occurred and nothing
* was sent.
*
* @param broadcast_addr
* The broadcast address to which to send the magic WoL packet.
*
* @param udp_port
* The UDP port to use when sending the WoL packet.
*
* @param packet
* The magic WoL packet to send.
*
* @return
* The number of bytes sent, or zero if nothing could be sent.
*/
static ssize_t __guac_wol_send_packet(const char* broadcast_addr,
const unsigned short udp_port, unsigned char packet[]) {
struct sockaddr_in wol_dest;
int wol_socket;
/* Determine the IP version, starting with IPv4. */
wol_dest.sin_port = htons(udp_port);
wol_dest.sin_family = AF_INET;
int retval = inet_pton(wol_dest.sin_family, broadcast_addr, &(wol_dest.sin_addr));
/* If return value is less than zero, this system doesn't know about IPv4. */
if (retval < 0) {
guac_error = GUAC_STATUS_SEE_ERRNO;
guac_error_message = "IPv4 address family is not supported";
return 0;
}
/* If return value is zero, address doesn't match the IPv4, so try IPv6. */
else if (retval == 0) {
wol_dest.sin_family = AF_INET6;
retval = inet_pton(wol_dest.sin_family, broadcast_addr, &(wol_dest.sin_addr));
/* System does not support IPv6. */
if (retval < 0) {
guac_error = GUAC_STATUS_SEE_ERRNO;
guac_error_message = "IPv6 address family is not supported";
return 0;
}
/* Address didn't match IPv6. */
else if (retval == 0) {
guac_error = GUAC_STATUS_INVALID_ARGUMENT;
guac_error_message = "Invalid broadcast or multicast address specified for Wake-on-LAN";
return 0;
}
}
/* Set up the socket */
wol_socket = socket(wol_dest.sin_family, SOCK_DGRAM, 0);
/* If socket open fails, bail out. */
if (wol_socket < 0) {
guac_error = GUAC_STATUS_SEE_ERRNO;
guac_error_message = "Failed to open socket to send Wake-on-LAN packet";
return 0;
}
/* Set up socket for IPv4 broadcast. */
if (wol_dest.sin_family == AF_INET) {
/* For configuring socket broadcast */
int wol_bcast = 1;
/* Attempt to set IPv4 broadcast; exit with error if this fails. */
if (setsockopt(wol_socket, SOL_SOCKET, SO_BROADCAST, &wol_bcast,
sizeof(wol_bcast)) < 0) {
close(wol_socket);
guac_error = GUAC_STATUS_SEE_ERRNO;
guac_error_message = "Failed to set IPv4 broadcast for Wake-on-LAN socket";
return 0;
}
}
/* Set up socket for IPv6 multicast. */
else {
/* Stick to a single hop for now. */
int hops = 1;
/* Attempt to set IPv6 multicast; exit with error if this fails. */
if (setsockopt(wol_socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops,
sizeof(hops)) < 0) {
close(wol_socket);
guac_error = GUAC_STATUS_SEE_ERRNO;
guac_error_message = "Failed to set IPv6 multicast for Wake-on-LAN socket";
return 0;
}
}
/* Send the packet and return number of bytes sent. */
int bytes = sendto(wol_socket, packet, GUAC_WOL_PACKET_SIZE, 0,
(struct sockaddr*) &wol_dest, sizeof(wol_dest));
close(wol_socket);
return bytes;
}
int guac_wol_wake(const char* mac_addr, const char* broadcast_addr,
const unsigned short udp_port) {
unsigned char wol_packet[GUAC_WOL_PACKET_SIZE];
unsigned int dest_mac[6];
/* Parse mac address and return with error if parsing fails. */
if (sscanf(mac_addr, "%x:%x:%x:%x:%x:%x",
&(dest_mac[0]), &(dest_mac[1]), &(dest_mac[2]),
&(dest_mac[3]), &(dest_mac[4]), &(dest_mac[5])) != 6) {
guac_error = GUAC_STATUS_INVALID_ARGUMENT;
guac_error_message = "Invalid argument for Wake-on-LAN MAC address";
return -1;
}
/* Generate the magic packet. */
__guac_wol_create_magic_packet(wol_packet, dest_mac);
/* Send the packet and record bytes sent. */
int bytes_sent = __guac_wol_send_packet(broadcast_addr, udp_port,
wol_packet);
/* Return 0 if bytes were sent, otherwise return an error. */
if (bytes_sent)
return 0;
return -1;
}

View File

@ -1,5 +0,0 @@
# Auto-generated test runner and binary
_generated_runner.c
test_kubernetes

View File

@ -27,7 +27,6 @@ AUTOMAKE_OPTIONS = foreign
ACLOCAL_AMFLAGS = -I m4 ACLOCAL_AMFLAGS = -I m4
lib_LTLIBRARIES = libguac-client-kubernetes.la lib_LTLIBRARIES = libguac-client-kubernetes.la
SUBDIRS = . tests
libguac_client_kubernetes_la_SOURCES = \ libguac_client_kubernetes_la_SOURCES = \
argv.c \ argv.c \

View File

@ -29,34 +29,172 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
int guac_kubernetes_argv_callback(guac_user* user, const char* mimetype, /**
const char* name, const char* value, void* data) { * All Kubernetes connection settings which may be updated by unprivileged
* users through "argv" streams.
*/
typedef enum guac_kubernetes_argv_setting {
/**
* The color scheme of the terminal.
*/
GUAC_KUBERNETES_ARGV_SETTING_COLOR_SCHEME,
/**
* The name of the font family used by the terminal.
*/
GUAC_KUBERNETES_ARGV_SETTING_FONT_NAME,
/**
* The size of the font used by the terminal, in points.
*/
GUAC_KUBERNETES_ARGV_SETTING_FONT_SIZE
} guac_kubernetes_argv_setting;
/**
* The value or current status of a connection parameter received over an
* "argv" stream.
*/
typedef struct guac_kubernetes_argv {
/**
* The specific setting being updated.
*/
guac_kubernetes_argv_setting setting;
/**
* Buffer space for containing the received argument value.
*/
char buffer[GUAC_KUBERNETES_ARGV_MAX_LENGTH];
/**
* The number of bytes received so far.
*/
int length;
} guac_kubernetes_argv;
/**
* Handler for "blob" instructions which appends the data from received blobs
* to the end of the in-progress argument value buffer.
*
* @see guac_user_blob_handler
*/
static int guac_kubernetes_argv_blob_handler(guac_user* user,
guac_stream* stream, void* data, int length) {
guac_kubernetes_argv* argv = (guac_kubernetes_argv*) stream->data;
/* Calculate buffer size remaining, including space for null terminator,
* adjusting received length accordingly */
int remaining = sizeof(argv->buffer) - argv->length - 1;
if (length > remaining)
length = remaining;
/* Append received data to end of buffer */
memcpy(argv->buffer + argv->length, data, length);
argv->length += length;
return 0;
}
/**
* Handler for "end" instructions which applies the changes specified by the
* argument value buffer associated with the stream.
*
* @see guac_user_end_handler
*/
static int guac_kubernetes_argv_end_handler(guac_user* user,
guac_stream* stream) {
int size;
guac_client* client = user->client; guac_client* client = user->client;
guac_kubernetes_client* kubernetes_client = (guac_kubernetes_client*) client->data; guac_kubernetes_client* kubernetes_client = (guac_kubernetes_client*) client->data;
guac_terminal* terminal = kubernetes_client->term; guac_terminal* terminal = kubernetes_client->term;
/* Update color scheme */ /* Append null terminator to value */
if (strcmp(name, GUAC_KUBERNETES_ARGV_COLOR_SCHEME) == 0) guac_kubernetes_argv* argv = (guac_kubernetes_argv*) stream->data;
guac_terminal_apply_color_scheme(terminal, value); argv->buffer[argv->length] = '\0';
/* Update font name */ /* Apply changes to chosen setting */
else if (strcmp(name, GUAC_KUBERNETES_ARGV_FONT_NAME) == 0) switch (argv->setting) {
guac_terminal_apply_font(terminal, value, -1, 0);
/* Update color scheme */
case GUAC_KUBERNETES_ARGV_SETTING_COLOR_SCHEME:
guac_terminal_apply_color_scheme(terminal, argv->buffer);
guac_client_stream_argv(client, client->socket, "text/plain",
"color-scheme", argv->buffer);
break;
/* Update font name */
case GUAC_KUBERNETES_ARGV_SETTING_FONT_NAME:
guac_terminal_apply_font(terminal, argv->buffer, -1, 0);
guac_client_stream_argv(client, client->socket, "text/plain",
"font-name", argv->buffer);
break;
/* Update font size */
case GUAC_KUBERNETES_ARGV_SETTING_FONT_SIZE:
/* Update only if font size is sane */
size = atoi(argv->buffer);
if (size > 0) {
guac_terminal_apply_font(terminal, NULL, size,
kubernetes_client->settings->resolution);
guac_client_stream_argv(client, client->socket, "text/plain",
"font-size", argv->buffer);
}
break;
/* Update only if font size is sane */
else if (strcmp(name, GUAC_KUBERNETES_ARGV_FONT_SIZE) == 0) {
int size = atoi(value);
if (size > 0)
guac_terminal_apply_font(terminal, NULL, size,
kubernetes_client->settings->resolution);
} }
/* Update Kubernetes terminal size */ /* Update Kubernetes terminal size */
guac_kubernetes_resize(client, guac_kubernetes_resize(client, terminal->term_height,
guac_terminal_get_rows(terminal), terminal->term_width);
guac_terminal_get_columns(terminal));
free(argv);
return 0;
}
int guac_kubernetes_argv_handler(guac_user* user, guac_stream* stream,
char* mimetype, char* name) {
guac_kubernetes_argv_setting setting;
/* Allow users to update the color scheme and font details */
if (strcmp(name, "color-scheme") == 0)
setting = GUAC_KUBERNETES_ARGV_SETTING_COLOR_SCHEME;
else if (strcmp(name, "font-name") == 0)
setting = GUAC_KUBERNETES_ARGV_SETTING_FONT_NAME;
else if (strcmp(name, "font-size") == 0)
setting = GUAC_KUBERNETES_ARGV_SETTING_FONT_SIZE;
/* No other connection parameters may be updated */
else {
guac_protocol_send_ack(user->socket, stream, "Not allowed.",
GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
guac_socket_flush(user->socket);
return 0;
}
guac_kubernetes_argv* argv = malloc(sizeof(guac_kubernetes_argv));
argv->setting = setting;
argv->length = 0;
/* Prepare stream to receive argument value */
stream->blob_handler = guac_kubernetes_argv_blob_handler;
stream->end_handler = guac_kubernetes_argv_end_handler;
stream->data = argv;
/* Signal stream is ready */
guac_protocol_send_ack(user->socket, stream, "Ready for updated "
"parameter.", GUAC_PROTOCOL_STATUS_SUCCESS);
guac_socket_flush(user->socket);
return 0; return 0;
} }
@ -67,21 +205,20 @@ void* guac_kubernetes_send_current_argv(guac_user* user, void* data) {
guac_terminal* terminal = kubernetes_client->term; guac_terminal* terminal = kubernetes_client->term;
/* Send current color scheme */ /* Send current color scheme */
guac_user_stream_argv(user, user->socket, "text/plain", guac_user_stream_argv(user, user->socket, "text/plain", "color-scheme",
GUAC_KUBERNETES_ARGV_COLOR_SCHEME, terminal->color_scheme);
guac_terminal_get_color_scheme(terminal));
/* Send current font name */ /* Send current font name */
guac_user_stream_argv(user, user->socket, "text/plain", guac_user_stream_argv(user, user->socket, "text/plain", "font-name",
GUAC_KUBERNETES_ARGV_FONT_NAME, terminal->font_name);
guac_terminal_get_font_name(terminal));
/* Send current font size */ /* Send current font size */
char font_size[64]; char font_size[64];
sprintf(font_size, "%i", guac_terminal_get_font_size(terminal)); sprintf(font_size, "%i", terminal->font_size);
guac_user_stream_argv(user, user->socket, "text/plain", guac_user_stream_argv(user, user->socket, "text/plain", "font-size",
GUAC_KUBERNETES_ARGV_FONT_SIZE, font_size); font_size);
return NULL; return NULL;
} }

View File

@ -23,32 +23,19 @@
#include "config.h" #include "config.h"
#include <guacamole/argv.h>
#include <guacamole/user.h> #include <guacamole/user.h>
/** /**
* The name of the parameter that specifies/updates the color scheme used by * The maximum number of bytes to allow for any argument value received via an
* the terminal emulator. * argv stream, including null terminator.
*/ */
#define GUAC_KUBERNETES_ARGV_COLOR_SCHEME "color-scheme" #define GUAC_KUBERNETES_ARGV_MAX_LENGTH 16384
/** /**
* The name of the parameter that specifies/updates the name of the font used * Handles an incoming stream from a Guacamole "argv" instruction, updating the
* by the terminal emulator. * given connection parameter if that parameter is allowed to be updated.
*/ */
#define GUAC_KUBERNETES_ARGV_FONT_NAME "font-name" guac_user_argv_handler guac_kubernetes_argv_handler;
/**
* The name of the parameter that specifies/updates the font size used by the
* terminal emulator.
*/
#define GUAC_KUBERNETES_ARGV_FONT_SIZE "font-size"
/**
* Handles a received argument value from a Guacamole "argv" instruction,
* updating the given connection parameter.
*/
guac_argv_callback guac_kubernetes_argv_callback;
/** /**
* Sends the current values of all non-sensitive parameters which may be set * Sends the current values of all non-sensitive parameters which may be set

View File

@ -17,13 +17,12 @@
* under the License. * under the License.
*/ */
#include "argv.h"
#include "client.h" #include "client.h"
#include "common/clipboard.h"
#include "kubernetes.h" #include "kubernetes.h"
#include "settings.h" #include "settings.h"
#include "user.h" #include "user.h"
#include <guacamole/argv.h>
#include <guacamole/client.h> #include <guacamole/client.h>
#include <libwebsockets.h> #include <libwebsockets.h>
@ -94,15 +93,12 @@ int guac_client_init(guac_client* client) {
guac_kubernetes_client* kubernetes_client = calloc(1, sizeof(guac_kubernetes_client)); guac_kubernetes_client* kubernetes_client = calloc(1, sizeof(guac_kubernetes_client));
client->data = kubernetes_client; client->data = kubernetes_client;
/* Init clipboard */
kubernetes_client->clipboard = guac_common_clipboard_alloc(GUAC_KUBERNETES_CLIPBOARD_MAX_LENGTH);
/* Set handlers */ /* Set handlers */
client->join_handler = guac_kubernetes_user_join_handler; client->join_handler = guac_kubernetes_user_join_handler;
client->free_handler = guac_kubernetes_client_free_handler; client->free_handler = guac_kubernetes_client_free_handler;
client->leave_handler = guac_kubernetes_user_leave_handler;
/* Register handlers for argument values that may be sent after the handshake */
guac_argv_register(GUAC_KUBERNETES_ARGV_COLOR_SCHEME, guac_kubernetes_argv_callback, NULL, GUAC_ARGV_OPTION_ECHO);
guac_argv_register(GUAC_KUBERNETES_ARGV_FONT_NAME, guac_kubernetes_argv_callback, NULL, GUAC_ARGV_OPTION_ECHO);
guac_argv_register(GUAC_KUBERNETES_ARGV_FONT_SIZE, guac_kubernetes_argv_callback, NULL, GUAC_ARGV_OPTION_ECHO);
/* Set locale and warn if not UTF-8 */ /* Set locale and warn if not UTF-8 */
setlocale(LC_CTYPE, ""); setlocale(LC_CTYPE, "");
@ -129,6 +125,7 @@ int guac_kubernetes_client_free_handler(guac_client* client) {
if (kubernetes_client->settings != NULL) if (kubernetes_client->settings != NULL)
guac_kubernetes_settings_free(kubernetes_client->settings); guac_kubernetes_settings_free(kubernetes_client->settings);
guac_common_clipboard_free(kubernetes_client->clipboard);
free(kubernetes_client); free(kubernetes_client);
return 0; return 0;

View File

@ -22,6 +22,11 @@
#include <guacamole/client.h> #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 * Static reference to the guac_client associated with the active Kubernetes
* connection. While libwebsockets provides some means of storing and * connection. While libwebsockets provides some means of storing and

View File

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

View File

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

View File

@ -21,6 +21,7 @@
#include "argv.h" #include "argv.h"
#include "client.h" #include "client.h"
#include "common/recording.h"
#include "io.h" #include "io.h"
#include "kubernetes.h" #include "kubernetes.h"
#include "ssl.h" #include "ssl.h"
@ -29,7 +30,6 @@
#include <guacamole/client.h> #include <guacamole/client.h>
#include <guacamole/protocol.h> #include <guacamole/protocol.h>
#include <guacamole/recording.h>
#include <libwebsockets.h> #include <libwebsockets.h>
#include <pthread.h> #include <pthread.h>
@ -213,11 +213,10 @@ void* guac_kubernetes_client_thread(void* data) {
} }
/* Generate endpoint for attachment URL */ /* Generate endpoint for attachment URL */
if (guac_kubernetes_endpoint_uri(endpoint_path, sizeof(endpoint_path), if (guac_kubernetes_endpoint_attach(endpoint_path, sizeof(endpoint_path),
settings->kubernetes_namespace, settings->kubernetes_namespace,
settings->kubernetes_pod, settings->kubernetes_pod,
settings->kubernetes_container, settings->kubernetes_container)) {
settings->exec_command)) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
"Unable to generate path for Kubernetes API endpoint: " "Unable to generate path for Kubernetes API endpoint: "
"Resulting path too long"); "Resulting path too long");
@ -229,33 +228,21 @@ void* guac_kubernetes_client_thread(void* data) {
/* Set up screen recording, if requested */ /* Set up screen recording, if requested */
if (settings->recording_path != NULL) { if (settings->recording_path != NULL) {
kubernetes_client->recording = guac_recording_create(client, kubernetes_client->recording = guac_common_recording_create(client,
settings->recording_path, settings->recording_path,
settings->recording_name, settings->recording_name,
settings->create_recording_path, settings->create_recording_path,
!settings->recording_exclude_output, !settings->recording_exclude_output,
!settings->recording_exclude_mouse, !settings->recording_exclude_mouse,
0, /* Touch events not supported */
settings->recording_include_keys); 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 */ /* Create terminal */
kubernetes_client->term = guac_terminal_create(client, options); kubernetes_client->term = guac_terminal_create(client,
kubernetes_client->clipboard, settings->disable_copy,
/* Free options struct now that it's been used */ settings->max_scrollback, settings->font_name, settings->font_size,
free(options); settings->resolution, settings->width, settings->height,
settings->color_scheme, settings->backspace);
/* Fail if terminal init failed */ /* Fail if terminal init failed */
if (kubernetes_client->term == NULL) { if (kubernetes_client->term == NULL) {
@ -369,7 +356,7 @@ fail:
/* Clean up recording, if in progress */ /* Clean up recording, if in progress */
if (kubernetes_client->recording != NULL) if (kubernetes_client->recording != NULL)
guac_recording_free(kubernetes_client->recording); guac_common_recording_free(kubernetes_client->recording);
/* Free WebSocket context if successfully allocated */ /* Free WebSocket context if successfully allocated */
if (kubernetes_client->context != NULL) if (kubernetes_client->context != NULL)
@ -413,8 +400,8 @@ void guac_kubernetes_force_redraw(guac_client* client) {
/* Get current terminal dimensions */ /* Get current terminal dimensions */
guac_terminal* term = kubernetes_client->term; guac_terminal* term = kubernetes_client->term;
int rows = guac_terminal_get_rows(term); int rows = term->term_height;
int columns = guac_terminal_get_columns(term); int columns = term->term_width;
/* Force a redraw by increasing the terminal size by one character in /* Force a redraw by increasing the terminal size by one character in
* each dimension and then resizing it back to normal (the same technique * each dimension and then resizing it back to normal (the same technique

View File

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

View File

@ -17,9 +17,7 @@
* under the License. * under the License.
*/ */
#include "argv.h"
#include "settings.h" #include "settings.h"
#include "terminal/terminal.h"
#include <guacamole/user.h> #include <guacamole/user.h>
@ -32,15 +30,14 @@ const char* GUAC_KUBERNETES_CLIENT_ARGS[] = {
"namespace", "namespace",
"pod", "pod",
"container", "container",
"exec-command",
"use-ssl", "use-ssl",
"client-cert", "client-cert",
"client-key", "client-key",
"ca-cert", "ca-cert",
"ignore-cert", "ignore-cert",
GUAC_KUBERNETES_ARGV_FONT_NAME, "font-name",
GUAC_KUBERNETES_ARGV_FONT_SIZE, "font-size",
GUAC_KUBERNETES_ARGV_COLOR_SCHEME, "color-scheme",
"typescript-path", "typescript-path",
"typescript-name", "typescript-name",
"create-typescript-path", "create-typescript-path",
@ -88,11 +85,6 @@ enum KUBERNETES_ARGS_IDX {
*/ */
IDX_CONTAINER, IDX_CONTAINER,
/**
* The command used by exec call. If omitted, attach call will be used.
*/
IDX_EXEC_COMMAND,
/** /**
* Whether SSL/TLS should be used. If omitted, SSL/TLS will not be used. * Whether SSL/TLS should be used. If omitted, SSL/TLS will not be used.
*/ */
@ -216,8 +208,8 @@ enum KUBERNETES_ARGS_IDX {
IDX_READ_ONLY, IDX_READ_ONLY,
/** /**
* ASCII code, as an integer to use for the backspace key, or * ASCII code, as an integer to use for the backspace key, or 127
* GUAC_TERMINAL_DEFAULT_BACKSPACE if not specified. * if not specified.
*/ */
IDX_BACKSPACE, IDX_BACKSPACE,
@ -282,11 +274,6 @@ guac_kubernetes_settings* guac_kubernetes_parse_args(guac_user* user,
guac_user_parse_args_string(user, GUAC_KUBERNETES_CLIENT_ARGS, argv, guac_user_parse_args_string(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
IDX_CONTAINER, NULL); IDX_CONTAINER, NULL);
/* Read exec command (optional) */
settings->exec_command =
guac_user_parse_args_string(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
IDX_EXEC_COMMAND, NULL);
/* Parse whether SSL should be used */ /* Parse whether SSL should be used */
settings->use_ssl = settings->use_ssl =
guac_user_parse_args_boolean(user, GUAC_KUBERNETES_CLIENT_ARGS, argv, guac_user_parse_args_boolean(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
@ -321,22 +308,22 @@ guac_kubernetes_settings* guac_kubernetes_parse_args(guac_user* user,
/* Read maximum scrollback size */ /* Read maximum scrollback size */
settings->max_scrollback = settings->max_scrollback =
guac_user_parse_args_int(user, GUAC_KUBERNETES_CLIENT_ARGS, argv, guac_user_parse_args_int(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
IDX_SCROLLBACK, GUAC_TERMINAL_DEFAULT_MAX_SCROLLBACK); IDX_SCROLLBACK, GUAC_KUBERNETES_DEFAULT_MAX_SCROLLBACK);
/* Read font name */ /* Read font name */
settings->font_name = settings->font_name =
guac_user_parse_args_string(user, GUAC_KUBERNETES_CLIENT_ARGS, argv, guac_user_parse_args_string(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
IDX_FONT_NAME, GUAC_TERMINAL_DEFAULT_FONT_NAME); IDX_FONT_NAME, GUAC_KUBERNETES_DEFAULT_FONT_NAME);
/* Read font size */ /* Read font size */
settings->font_size = settings->font_size =
guac_user_parse_args_int(user, GUAC_KUBERNETES_CLIENT_ARGS, argv, guac_user_parse_args_int(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
IDX_FONT_SIZE, GUAC_TERMINAL_DEFAULT_FONT_SIZE); IDX_FONT_SIZE, GUAC_KUBERNETES_DEFAULT_FONT_SIZE);
/* Copy requested color scheme */ /* Copy requested color scheme */
settings->color_scheme = settings->color_scheme =
guac_user_parse_args_string(user, GUAC_KUBERNETES_CLIENT_ARGS, argv, guac_user_parse_args_string(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
IDX_COLOR_SCHEME, GUAC_TERMINAL_DEFAULT_COLOR_SCHEME); IDX_COLOR_SCHEME, "");
/* Pull width/height/resolution directly from user */ /* Pull width/height/resolution directly from user */
settings->width = user->info.optimal_width; settings->width = user->info.optimal_width;
@ -391,7 +378,7 @@ guac_kubernetes_settings* guac_kubernetes_parse_args(guac_user* user,
/* Parse backspace key code */ /* Parse backspace key code */
settings->backspace = settings->backspace =
guac_user_parse_args_int(user, GUAC_KUBERNETES_CLIENT_ARGS, argv, guac_user_parse_args_int(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
IDX_BACKSPACE, GUAC_TERMINAL_DEFAULT_BACKSPACE); IDX_BACKSPACE, 127);
/* Parse clipboard copy disable flag */ /* Parse clipboard copy disable flag */
settings->disable_copy = settings->disable_copy =
@ -418,9 +405,6 @@ void guac_kubernetes_settings_free(guac_kubernetes_settings* settings) {
free(settings->kubernetes_pod); free(settings->kubernetes_pod);
free(settings->kubernetes_container); free(settings->kubernetes_container);
/* Free Kubernetes exec command */
free(settings->exec_command);
/* Free SSL/TLS details */ /* Free SSL/TLS details */
free(settings->client_cert); free(settings->client_cert);
free(settings->client_key); free(settings->client_key);

View File

@ -24,6 +24,17 @@
#include <stdbool.h> #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 * The port to connect to when initiating any Kubernetes connection, if no
* other port is specified. * other port is specified.
@ -46,6 +57,11 @@
*/ */
#define GUAC_KUBERNETES_DEFAULT_RECORDING_NAME "recording" #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 * Settings for the Kubernetes connection. The values for this structure are
* parsed from the arguments given during the Guacamole protocol handshake * parsed from the arguments given during the Guacamole protocol handshake
@ -81,12 +97,6 @@ typedef struct guac_kubernetes_settings {
*/ */
char* kubernetes_container; char* kubernetes_container;
/**
* The command to generate api endpoint for call exec.
* If omitted call attach will be used.
*/
char* exec_command;
/** /**
* Whether SSL/TLS should be used. * Whether SSL/TLS should be used.
*/ */

View File

@ -1,66 +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.
#
# NOTE: Parts of this file (Makefile.am) are automatically transcluded verbatim
# into Makefile.in. Though the build system (GNU Autotools) automatically adds
# its own license boilerplate to the generated Makefile.in, that boilerplate
# does not apply to the transcluded portions of Makefile.am which are licensed
# to you by the ASF under the Apache License, Version 2.0, as described above.
#
AUTOMAKE_OPTIONS = foreign
ACLOCAL_AMFLAGS = -I m4
#
# Unit tests for Kubernetes support
#
check_PROGRAMS = test_kubernetes
TESTS = $(check_PROGRAMS)
test_kubernetes_SOURCES = \
url/append.c \
url/escape.c
test_kubernetes_CFLAGS = \
-Werror -Wall -pedantic \
@LIBGUAC_CLIENT_KUBERNETES_INCLUDE@ \
@LIBGUAC_INCLUDE@
test_kubernetes_LDADD = \
@CUNIT_LIBS@ \
@LIBGUAC_CLIENT_KUBERNETES_LTLIB@
#
# Autogenerate test runner
#
GEN_RUNNER = $(top_srcdir)/util/generate-test-runner.pl
CLEANFILES = _generated_runner.c
_generated_runner.c: $(test_kubernetes_SOURCES)
$(AM_V_GEN) $(GEN_RUNNER) $(test_kubernetes_SOURCES) > $@
nodist_test_kubernetes_SOURCES = \
_generated_runner.c
# Use automake's TAP test driver for running any tests
LOG_DRIVER = \
env AM_TAP_AWK='$(AWK)' \
$(SHELL) $(top_srcdir)/build-aux/tap-driver.sh

View File

@ -1,74 +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 "url.h"
#include <CUnit/CUnit.h>
#include <stdio.h>
#include <stdlib.h>
/**
* Verifies that guac_kubernetes_append_endpoint_param() correctly appends
* parameters to URLs that do not already have a query string.
*/
void test_url__append_no_query() {
char url[256] = "http://example.net";
CU_ASSERT(!guac_kubernetes_append_endpoint_param(url, sizeof(url), "foo", "100% test value"));
CU_ASSERT_STRING_EQUAL(url, "http://example.net?foo=100%25%20test%20value");
}
/**
* Verifies that guac_kubernetes_append_endpoint_param() correctly appends
* parameters to URLs that already have a query string.
*/
void test_url__append_existing_query() {
char url[256] = "http://example.net?foo=test%20value";
CU_ASSERT(!guac_kubernetes_append_endpoint_param(url, sizeof(url), "foo2", "yet&another/test\\value"));
CU_ASSERT_STRING_EQUAL(url, "http://example.net?foo=test%20value&foo2=yet%26another%2Ftest%5Cvalue");
}
/**
* Verifies that guac_kubernetes_append_endpoint_param() refuses to overflow
* the bounds of the provided buffer.
*/
void test_url__append_bounds() {
char url[256];
/* Appending "?a=1" to the 18-character string "http://example.net" should
* fail for all buffer sizes with 22 bytes or less, with a 22-byte buffer
* lacking space for the null terminator */
for (int length = 18; length <= 22; length++) {
strcpy(url, "http://example.net");
printf("Testing buffer with length %i ...\n", length);
CU_ASSERT(guac_kubernetes_append_endpoint_param(url, length, "a", "1"));
}
/* A 23-byte buffer should be sufficient */
strcpy(url, "http://example.net");
CU_ASSERT(!guac_kubernetes_append_endpoint_param(url, 23, "a", "1"));
}

View File

@ -1,72 +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 "url.h"
#include <CUnit/CUnit.h>
#include <stdlib.h>
/**
* Verifies that guac_kubernetes_escape_url_component() correctly escapes
* characters that would otherwise have special meaning within URLs.
*/
void test_url__escape_special() {
char value[256];
CU_ASSERT(!guac_kubernetes_escape_url_component(value, sizeof(value), "?foo%20bar\\1/2&3=4"));
CU_ASSERT_STRING_EQUAL(value, "%3Ffoo%2520bar%5C1%2F2%263%3D4");
}
/**
* Verifies that guac_kubernetes_escape_url_component() leaves strings
* untouched if they contain no characters requiring escaping.
*/
void test_url__escape_nospecial() {
char value[256];
CU_ASSERT(!guac_kubernetes_escape_url_component(value, sizeof(value), "potato"));
CU_ASSERT_STRING_EQUAL(value, "potato");
}
/**
* Verifies that guac_kubernetes_escape_url_component() refuses to overflow the
* bounds of the provided buffer.
*/
void test_url__escape_bounds() {
char value[256];
/* Escaping "?potato" (or "potato?") should fail for all buffer sizes with
* 9 bytes or less, with a 9-byte buffer lacking space for the null
* terminator */
for (int length = 0; length <= 9; length++) {
printf("Testing buffer with length %i ...\n", length);
CU_ASSERT(guac_kubernetes_escape_url_component(value, length, "?potato"));
CU_ASSERT(guac_kubernetes_escape_url_component(value, length, "potato?"));
}
/* A 10-byte buffer should be sufficient */
CU_ASSERT(!guac_kubernetes_escape_url_component(value, 10, "?potato"));
}

View File

@ -89,60 +89,15 @@ int guac_kubernetes_escape_url_component(char* output, int length,
} }
int guac_kubernetes_append_endpoint_param(char* buffer, int length, int guac_kubernetes_endpoint_attach(char* buffer, int length,
const char* param_name, const char* param_value) {
char escaped_param_value[GUAC_KUBERNETES_MAX_ENDPOINT_LENGTH];
/* Escape value */
if (guac_kubernetes_escape_url_component(escaped_param_value,
sizeof(escaped_param_value), param_value))
return 1;
char* str = buffer;
int str_len = 0;
int qmark = 0;
while (*str != '\0') {
/* Look for a question mark */
if (*str=='?') qmark = 1;
/* Compute the buffer string length */
str_len++;
/* Verify the buffer null terminated */
if (str_len >= length) return 1;
/* Next character */
str++;
}
/* Determine the parameter delimiter */
char delimiter = '?';
if (qmark) delimiter = '&';
/* Advance to end of buffer, where the new parameter and delimiter need to
* be appended */
buffer += str_len;
length -= str_len;
/* Write the parameter and delimiter to the buffer */
int written = snprintf(buffer, length, "%c%s=%s", delimiter,
param_name, escaped_param_value);
/* The parameter was successfully added if it was written to the given
* buffer without truncation */
return (written < 0 || written >= length);
}
int guac_kubernetes_endpoint_uri(char* buffer, int length,
const char* kubernetes_namespace, const char* kubernetes_pod, const char* kubernetes_namespace, const char* kubernetes_pod,
const char* kubernetes_container, const char* exec_command) { const char* kubernetes_container) {
int written;
char escaped_namespace[GUAC_KUBERNETES_MAX_ENDPOINT_LENGTH]; char escaped_namespace[GUAC_KUBERNETES_MAX_ENDPOINT_LENGTH];
char escaped_pod[GUAC_KUBERNETES_MAX_ENDPOINT_LENGTH]; char escaped_pod[GUAC_KUBERNETES_MAX_ENDPOINT_LENGTH];
char escaped_container[GUAC_KUBERNETES_MAX_ENDPOINT_LENGTH];
/* Escape Kubernetes namespace */ /* Escape Kubernetes namespace */
if (guac_kubernetes_escape_url_component(escaped_namespace, if (guac_kubernetes_escape_url_component(escaped_namespace,
@ -154,38 +109,29 @@ int guac_kubernetes_endpoint_uri(char* buffer, int length,
sizeof(escaped_pod), kubernetes_pod)) sizeof(escaped_pod), kubernetes_pod))
return 1; return 1;
/* Determine the call type */ /* Generate attachment endpoint URL */
char* call = "attach";
if (exec_command != NULL)
call = "exec";
int written;
/* Generate the endpoint path and write to the buffer */
written = snprintf(buffer, length,
"/api/v1/namespaces/%s/pods/%s/%s", escaped_namespace, escaped_pod, call);
/* Operation successful if the endpoint path was written to the given
* buffer without truncation */
if (written < 0 || written >= length)
return 1;
/* Append exec command parameter */
if (exec_command != NULL) {
if (guac_kubernetes_append_endpoint_param(buffer,
length, "command", exec_command))
return 1;
}
/* Append kubernetes container parameter */
if (kubernetes_container != NULL) { if (kubernetes_container != NULL) {
if (guac_kubernetes_append_endpoint_param(buffer,
length, "container", kubernetes_container)) /* Escape container name */
if (guac_kubernetes_escape_url_component(escaped_container,
sizeof(escaped_container), kubernetes_container))
return 1; return 1;
written = snprintf(buffer, length,
"/api/v1/namespaces/%s/pods/%s/attach"
"?container=%s&stdin=true&stdout=true&tty=true",
escaped_namespace, escaped_pod, escaped_container);
}
else {
written = snprintf(buffer, length,
"/api/v1/namespaces/%s/pods/%s/attach"
"?stdin=true&stdout=true&tty=true",
escaped_namespace, escaped_pod);
} }
/* Append stdin, stdout and tty parameters */ /* Endpoint URL was successfully generated if it was written to the given
return (guac_kubernetes_append_endpoint_param(buffer, length, "stdin", "true")) * buffer without truncation */
|| (guac_kubernetes_append_endpoint_param(buffer, length, "stdout", "true")) return !(written < length - 1);
|| (guac_kubernetes_append_endpoint_param(buffer, length, "tty", "true"));
} }

View File

@ -49,35 +49,6 @@
int guac_kubernetes_escape_url_component(char* output, int length, int guac_kubernetes_escape_url_component(char* output, int length,
const char* str); const char* str);
/**
* Appends the given query parameter and value to the given buffer. If the
* buffer does not already contain the '?' character denoting the start of the
* query string, it will be added. If the buffer already contains a query
* string, a '&' character will be added before the new parameter. The
* parameter value will automatically be URL-escaped as necessary.
*
* @param buffer
* The buffer which should receive the parameter. It could contain the endpoint path.
* The parameter will be written to the end of the buffer.
*
* @param length
* The number of bytes available in the given buffer.
*
* @param param_name
* The name of the parameter. If the parameter name contains characters
* with special meaning to URLs, it must already be URL-escaped.
*
* @param param_value
* The value of the parameter.
*
* @return
* Zero if the parameter was successfully attached to the buffer,
* non-zero if insufficient space exists within the buffer or
* buffer not null terminated.
*/
int guac_kubernetes_append_endpoint_param(char* buffer, int length,
const char* param_name, const char* param_value);
/** /**
* Generates the full path to the Kubernetes API endpoint which handles * Generates the full path to the Kubernetes API endpoint which handles
* attaching to running containers within specific pods. Values within the path * attaching to running containers within specific pods. Values within the path
@ -101,18 +72,14 @@ int guac_kubernetes_append_endpoint_param(char* buffer, int length,
* @param kubernetes_container * @param kubernetes_container
* The name of the container to attach to, or NULL to arbitrarily attach * The name of the container to attach to, or NULL to arbitrarily attach
* to the first container in the pod. * to the first container in the pod.
*
* @param exec_command
* The command used to run a new process and attach to it,
* instead of the main container process.
* *
* @return * @return
* Zero if the endpoint path was successfully written to the provided * Zero if the endpoint path was successfully written to the provided
* buffer, non-zero if insufficient space exists within the buffer. * buffer, non-zero if insufficient space exists within the buffer.
*/ */
int guac_kubernetes_endpoint_uri(char* buffer, int length, int guac_kubernetes_endpoint_attach(char* buffer, int length,
const char* kubernetes_namespace, const char* kubernetes_pod, const char* kubernetes_namespace, const char* kubernetes_pod,
const char* kubernetes_container, const char* exec_command); const char* kubernetes_container);
#endif #endif

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