Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
9ece4b7a5b | ||
|
39a1c25e9b | ||
|
e1d28d7dee | ||
|
19899756a0 | ||
|
30ec3be924 | ||
|
22fe81913f |
@ -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
4
.gitignore
vendored
@ -44,7 +44,7 @@ configure
|
|||||||
stamp-h1
|
stamp-h1
|
||||||
|
|
||||||
# Generated docs
|
# Generated docs
|
||||||
doc/*/doxygen-output
|
doc/doxygen-output
|
||||||
|
|
||||||
# IDE metadata
|
# IDE Configuration
|
||||||
nbproject/
|
nbproject/
|
197
Dockerfile
197
Dockerfile
@ -21,173 +21,94 @@
|
|||||||
# 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+)+'
|
gcc \
|
||||||
ARG WITH_LIBSSH2='libssh2-\d+(\.\d+)+'
|
libcairo2-dev \
|
||||||
ARG WITH_LIBTELNET='\d+(\.\d+)+'
|
libfreerdp-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/freerdp/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 \
|
||||||
|
libfreerdp-plugins-standard \
|
||||||
|
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/freerdp/guac*.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 +118,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
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ DIST_SUBDIRS = \
|
|||||||
src/protocols/rdp \
|
src/protocols/rdp \
|
||||||
src/protocols/ssh \
|
src/protocols/ssh \
|
||||||
src/protocols/telnet \
|
src/protocols/telnet \
|
||||||
|
src/protocols/tn5250 \
|
||||||
src/protocols/vnc
|
src/protocols/vnc
|
||||||
|
|
||||||
SUBDIRS = \
|
SUBDIRS = \
|
||||||
@ -71,6 +72,7 @@ endif
|
|||||||
|
|
||||||
if ENABLE_TELNET
|
if ENABLE_TELNET
|
||||||
SUBDIRS += src/protocols/telnet
|
SUBDIRS += src/protocols/telnet
|
||||||
|
SUBDIRS += src/protocols/tn5250
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if ENABLE_VNC
|
if ENABLE_VNC
|
||||||
@ -96,8 +98,7 @@ EXTRA_DIST = \
|
|||||||
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
|
||||||
|
|
||||||
|
2
NOTICE
2
NOTICE
@ -1,5 +1,5 @@
|
|||||||
Apache Guacamole
|
Apache Guacamole
|
||||||
Copyright 2020 The Apache Software Foundation
|
Copyright 2019 The Apache Software Foundation
|
||||||
|
|
||||||
This product includes software developed at
|
This product includes software developed at
|
||||||
The Apache Software Foundation (http://www.apache.org/).
|
The Apache Software Foundation (http://www.apache.org/).
|
||||||
|
@ -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.1.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.
|
||||||
|
882
configure.ac
882
configure.ac
File diff suppressed because it is too large
Load Diff
@ -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
|
||||||
|
|
@ -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 \
|
||||||
|
61
src/common-ssh/common-ssh/dsa-compat.h
Normal file
61
src/common-ssh/common-ssh/dsa-compat.h
Normal 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
|
||||||
|
|
@ -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
|
||||||
|
@ -17,25 +17,24 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "keymap.h"
|
#ifndef GUAC_COMMON_SSH_RSA_COMPAT_H
|
||||||
|
#define GUAC_COMMON_SSH_RSA_COMPAT_H
|
||||||
|
|
||||||
#include <string.h>
|
#include "config.h"
|
||||||
|
|
||||||
const guac_rdp_keymap* guac_rdp_keymap_find(const char* name) {
|
#include <openssl/bn.h>
|
||||||
|
#include <openssl/rsa.h>
|
||||||
|
|
||||||
/* For each keymap */
|
#ifndef HAVE_RSA_GET0_KEY
|
||||||
const guac_rdp_keymap** current = GUAC_KEYMAPS;
|
/**
|
||||||
while (*current != NULL) {
|
* RSA_get0_key() implementation for versions of OpenSSL which lack this
|
||||||
|
* function (pre 1.1).
|
||||||
|
*
|
||||||
|
* See: https://www.openssl.org/docs/man1.1.0/crypto/RSA_get0_key.html
|
||||||
|
*/
|
||||||
|
void RSA_get0_key(const RSA* rsa_key, const BIGNUM** n,
|
||||||
|
const BIGNUM** e, const BIGNUM**d);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* If name matches, done */
|
#endif
|
||||||
if (strcmp((*current)->name, name) == 0)
|
|
||||||
return *current;
|
|
||||||
|
|
||||||
current++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Failure */
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -70,16 +70,6 @@ typedef struct guac_common_ssh_sftp_filesystem {
|
|||||||
*/
|
*/
|
||||||
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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -133,19 +123,12 @@ typedef struct guac_common_ssh_sftp_ls_state {
|
|||||||
* 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
|
||||||
|
59
src/common-ssh/dsa-compat.c
Normal file
59
src/common-ssh/dsa-compat.c
Normal 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
|
||||||
|
|
@ -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;
|
return NULL;
|
||||||
|
|
||||||
guac_common_ssh_key* key = malloc(sizeof(guac_common_ssh_key));
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
/* 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) {
|
||||||
|
@ -17,19 +17,22 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef GUAC_RDP_LOG_H
|
#include "config.h"
|
||||||
#define GUAC_RDP_LOG_H
|
|
||||||
|
|
||||||
#include <guacamole/client.h>
|
#include <openssl/bn.h>
|
||||||
|
#include <openssl/rsa.h>
|
||||||
|
|
||||||
/**
|
#include <stdlib.h>
|
||||||
* Redirects the core FreeRDP logging facility, wLog, such that it logs all
|
|
||||||
* messages at the debug level using guac_client_log().
|
|
||||||
*
|
|
||||||
* @param client
|
|
||||||
* The guac_client that should receive all log messages.
|
|
||||||
*/
|
|
||||||
void guac_rdp_redirect_wlog(guac_client* client);
|
|
||||||
|
|
||||||
|
#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
|
#endif
|
||||||
|
|
@ -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);
|
||||||
@ -607,6 +586,7 @@ static int guac_common_ssh_sftp_ls_ack_handler(guac_user* user,
|
|||||||
guac_stream* stream, char* message, guac_protocol_status status) {
|
guac_stream* stream, char* message, guac_protocol_status status) {
|
||||||
|
|
||||||
int bytes_read;
|
int bytes_read;
|
||||||
|
int blob_written = 0;
|
||||||
|
|
||||||
char filename[GUAC_COMMON_SSH_SFTP_MAX_PATH];
|
char filename[GUAC_COMMON_SSH_SFTP_MAX_PATH];
|
||||||
LIBSSH2_SFTP_ATTRIBUTES attributes;
|
LIBSSH2_SFTP_ATTRIBUTES attributes;
|
||||||
@ -628,7 +608,8 @@ static int guac_common_ssh_sftp_ls_ack_handler(guac_user* user,
|
|||||||
|
|
||||||
/* While directory entries remain */
|
/* While directory entries remain */
|
||||||
while ((bytes_read = libssh2_sftp_readdir(list_state->directory,
|
while ((bytes_read = libssh2_sftp_readdir(list_state->directory,
|
||||||
filename, sizeof(filename), &attributes)) > 0) {
|
filename, sizeof(filename), &attributes)) > 0
|
||||||
|
&& !blob_written) {
|
||||||
|
|
||||||
char absolute_path[GUAC_COMMON_SSH_SFTP_MAX_PATH];
|
char absolute_path[GUAC_COMMON_SSH_SFTP_MAX_PATH];
|
||||||
|
|
||||||
@ -658,10 +639,9 @@ static int guac_common_ssh_sftp_ls_ack_handler(guac_user* user,
|
|||||||
else
|
else
|
||||||
mimetype = "application/octet-stream";
|
mimetype = "application/octet-stream";
|
||||||
|
|
||||||
/* Write entry, waiting for next ack if a blob is written */
|
/* Write entry */
|
||||||
if (guac_common_json_write_property(user, stream,
|
blob_written |= guac_common_json_write_property(user, stream,
|
||||||
&list_state->json_state, absolute_path, mimetype))
|
&list_state->json_state, absolute_path, mimetype);
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -807,14 +787,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 +843,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 +903,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;
|
||||||
|
|
||||||
/* Only handle uploads if not disabled. */
|
|
||||||
if (!filesystem->disable_upload)
|
|
||||||
fs_object->put_handler = guac_common_ssh_sftp_put_handler;
|
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 +916,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);
|
||||||
@ -975,10 +931,6 @@ guac_common_ssh_sftp_filesystem* guac_common_ssh_create_sftp_filesystem(
|
|||||||
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,
|
||||||
root_path)) {
|
root_path)) {
|
||||||
|
@ -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,22 +140,12 @@ 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
|
||||||
|
|
||||||
if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) {
|
|
||||||
|
|
||||||
/* Init threadsafety in libgcrypt */
|
/* Init threadsafety in libgcrypt */
|
||||||
gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
|
gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
|
||||||
|
|
||||||
/* Initialize GCrypt */
|
|
||||||
if (!gcry_check_version(GCRYPT_VERSION)) {
|
if (!gcry_check_version(GCRYPT_VERSION)) {
|
||||||
guac_client_log(client, GUAC_LOG_ERROR, "libgcrypt version mismatch.");
|
guac_client_log(client, GUAC_LOG_ERROR, "libgcrypt version mismatch.");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mark initialization as completed. */
|
|
||||||
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
|
|
||||||
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef OPENSSL_REQUIRES_THREADING_CALLBACKS
|
#ifdef OPENSSL_REQUIRES_THREADING_CALLBACKS
|
||||||
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
@ -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
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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 */
|
@ -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);
|
||||||
@ -282,31 +260,18 @@ static int __guac_common_surface_is_opaque(guac_common_surface* surface,
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the given rectangle should be combined into the existing
|
* Returns whether the given rectangle should be combined into the existing
|
||||||
* dirty rectangle, to be eventually flushed as image data, or would be best
|
* dirty rectangle, to be eventually flushed as a "png" instruction.
|
||||||
* kept independent of the current rectangle.
|
|
||||||
*
|
*
|
||||||
* @param surface
|
* @param surface The surface to be queried.
|
||||||
* The surface being updated.
|
* @param rect The update rectangle.
|
||||||
*
|
* @param rect_only Non-zero if this update, by its nature, contains only
|
||||||
* @param rect
|
* metainformation about the update's rectangle, zero if
|
||||||
* The bounding rectangle of the update being made to the surface.
|
* the update also contains image data.
|
||||||
*
|
* @return Non-zero if the update should be combined with any existing update,
|
||||||
* @param rect_only
|
* zero otherwise.
|
||||||
* Non-zero if this update, by its nature, contains only metainformation
|
|
||||||
* about the update's bounding rectangle, zero if the update also contains
|
|
||||||
* image data.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* Non-zero if the update should be combined with any existing update, zero
|
|
||||||
* otherwise.
|
|
||||||
*/
|
*/
|
||||||
static int __guac_common_should_combine(guac_common_surface* surface, const guac_common_rect* rect, int rect_only) {
|
static int __guac_common_should_combine(guac_common_surface* surface, const guac_common_rect* rect, int rect_only) {
|
||||||
|
|
||||||
/* Always favor combining updates if surface is currently a purely
|
|
||||||
* server-side scratch area */
|
|
||||||
if (!surface->realized)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
if (surface->dirty) {
|
if (surface->dirty) {
|
||||||
|
|
||||||
int combined_cost, dirty_cost, update_cost;
|
int combined_cost, dirty_cost, update_cost;
|
||||||
@ -543,10 +508,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);
|
||||||
|
|
||||||
@ -1819,8 +1780,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 +1968,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 */
|
||||||
|
@ -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 \
|
||||||
|
@ -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"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
@ -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];
|
|
||||||
|
|
@ -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, ¤t_input, sizeof(input),
|
guac_iconv(reader, ¤t_input, sizeof(input),
|
||||||
writer, ¤t_output, sizeof(output));
|
writer, ¤t_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));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
|
||||||
|
|
59
doc/libguac-terminal/Doxyfile.in → src/guacd-docker/bin/build-guacd.sh
Normal file → Executable file
59
doc/libguac-terminal/Doxyfile.in → src/guacd-docker/bin/build-guacd.sh
Normal file → Executable 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
|
||||||
#
|
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
|
|
||||||
|
|
86
src/guacd-docker/bin/link-freerdp-plugins.sh
Executable file
86
src/guacd-docker/bin/link-freerdp-plugins.sh
Executable 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}/freerdp"
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
@ -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
|
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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);
|
||||||
|
@ -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",
|
||||||
(current_address->ai_family == AF_INET) ? "AF_INET" : "AF_INET6",
|
|
||||||
bound_address, bound_port, strerror(errno));
|
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) {
|
||||||
|
@ -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,
|
||||||
|
@ -267,24 +267,24 @@ static int guacd_timed_client_free(guac_client* client, int timeout) {
|
|||||||
.tv_nsec = current_time.tv_usec * 1000
|
.tv_nsec = current_time.tv_usec * 1000
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Free the client in a separate thread, so we can time the free operation */
|
||||||
|
if (pthread_create(&client_free_thread, NULL,
|
||||||
|
guacd_client_free_thread, &free_operation))
|
||||||
|
return 1;
|
||||||
|
|
||||||
/* The mutex associated with the pthread conditional and flag MUST be
|
/* The mutex associated with the pthread conditional and flag MUST be
|
||||||
* acquired before attempting to wait for the condition */
|
* acquired before attempting to wait for the condition */
|
||||||
if (pthread_mutex_lock(&free_operation.completed_mutex))
|
if (pthread_mutex_lock(&free_operation.completed_mutex))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/* Free the client in a separate thread, so we can time the free operation */
|
|
||||||
if (!pthread_create(&client_free_thread, NULL,
|
|
||||||
guacd_client_free_thread, &free_operation)) {
|
|
||||||
|
|
||||||
/* Wait a finite amount of time for the free operation to finish */
|
/* Wait a finite amount of time for the free operation to finish */
|
||||||
(void) pthread_cond_timedwait(&free_operation.completed_cond,
|
if (pthread_cond_timedwait(&free_operation.completed_cond,
|
||||||
&free_operation.completed_mutex, &deadline);
|
&free_operation.completed_mutex, &deadline))
|
||||||
}
|
return 1;
|
||||||
|
|
||||||
(void) pthread_mutex_unlock(&free_operation.completed_mutex);
|
|
||||||
|
|
||||||
/* Return status of free operation */
|
/* Return status of free operation */
|
||||||
return !free_operation.completed;
|
return !free_operation.completed;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -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) {
|
||||||
|
@ -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@
|
||||||
@ -100,7 +99,6 @@ guacenc_LDADD = \
|
|||||||
|
|
||||||
guacenc_LDFLAGS = \
|
guacenc_LDFLAGS = \
|
||||||
@AVCODEC_LIBS@ \
|
@AVCODEC_LIBS@ \
|
||||||
@AVFORMAT_LIBS@ \
|
|
||||||
@AVUTIL_LIBS@ \
|
@AVUTIL_LIBS@ \
|
||||||
@CAIRO_LIBS@ \
|
@CAIRO_LIBS@ \
|
||||||
@JPEG_LIBS@ \
|
@JPEG_LIBS@ \
|
||||||
|
@ -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;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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@ \
|
||||||
|
@ -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;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -117,12 +117,6 @@ guac_audio_stream* guac_audio_stream_alloc(guac_client* client,
|
|||||||
audio->client = client;
|
audio->client = client;
|
||||||
audio->stream = guac_client_alloc_stream(client);
|
audio->stream = guac_client_alloc_stream(client);
|
||||||
|
|
||||||
/* Abort allocation if underlying stream cannot be allocated */
|
|
||||||
if (audio->stream == NULL) {
|
|
||||||
free(audio);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Load PCM properties */
|
/* Load PCM properties */
|
||||||
audio->rate = rate;
|
audio->rate = rate;
|
||||||
audio->channels = channels;
|
audio->channels = channels;
|
||||||
@ -194,9 +188,6 @@ void guac_audio_stream_free(guac_audio_stream* audio) {
|
|||||||
if (audio->encoder != NULL && audio->encoder->end_handler)
|
if (audio->encoder != NULL && audio->encoder->end_handler)
|
||||||
audio->encoder->end_handler(audio);
|
audio->encoder->end_handler(audio);
|
||||||
|
|
||||||
/* Release stream back to client pool */
|
|
||||||
guac_client_free_stream(audio->client, audio->stream);
|
|
||||||
|
|
||||||
/* Free associated data */
|
/* Free associated data */
|
||||||
free(audio);
|
free(audio);
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
|
||||||
|
|
||||||
}
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -148,8 +148,7 @@ struct guac_audio_stream {
|
|||||||
* @return
|
* @return
|
||||||
* The newly allocated guac_audio_stream, or NULL if no audio stream could
|
* The newly allocated guac_audio_stream, or NULL if no audio stream could
|
||||||
* be allocated due to lack of support on the part of the connecting
|
* be allocated due to lack of support on the part of the connecting
|
||||||
* Guacamole client or due to reaching the maximum number of active
|
* Guacamole client.
|
||||||
* streams.
|
|
||||||
*/
|
*/
|
||||||
guac_audio_stream* guac_audio_stream_alloc(guac_client* client,
|
guac_audio_stream* guac_audio_stream_alloc(guac_client* client,
|
||||||
guac_audio_encoder* encoder, int rate, int channels, int bps);
|
guac_audio_encoder* encoder, int rate, int channels, int bps);
|
||||||
|
@ -383,8 +383,7 @@ void guac_client_free_layer(guac_client* client, guac_layer* layer);
|
|||||||
* The client to allocate the stream for.
|
* The client to allocate the stream for.
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* The next available stream, or a newly allocated stream, or NULL if the
|
* The next available stream, or a newly allocated stream.
|
||||||
* maximum number of active streams has been reached.
|
|
||||||
*/
|
*/
|
||||||
guac_stream* guac_client_alloc_stream(guac_client* client);
|
guac_stream* guac_client_alloc_stream(guac_client* client);
|
||||||
|
|
||||||
@ -509,47 +508,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 +548,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 +691,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.
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -96,20 +96,6 @@ struct guac_user_info {
|
|||||||
*/
|
*/
|
||||||
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;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct guac_user {
|
struct guac_user {
|
||||||
@ -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;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -609,12 +574,8 @@ int guac_user_handle_instruction(guac_user* user, const char* opcode,
|
|||||||
* Allocates a new stream. An arbitrary index is automatically assigned
|
* Allocates a new stream. An arbitrary index is automatically assigned
|
||||||
* if no previously-allocated stream is available for use.
|
* if no previously-allocated stream is available for use.
|
||||||
*
|
*
|
||||||
* @param user
|
* @param user The user to allocate the stream for.
|
||||||
* The user to allocate the stream for.
|
* @return The next available stream, or a newly allocated stream.
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* The next available stream, or a newly allocated stream, or NULL if the
|
|
||||||
* maximum number of active streams has been reached.
|
|
||||||
*/
|
*/
|
||||||
guac_stream* guac_user_alloc_stream(guac_user* user);
|
guac_stream* guac_user_alloc_stream(guac_user* user);
|
||||||
|
|
||||||
@ -858,28 +819,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.
|
||||||
|
@ -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 */
|
|
||||||
|
|
@ -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 */
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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,9 +99,16 @@ 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++) {
|
||||||
|
|
||||||
|
if (guac_socket_write_string(socket, ","))
|
||||||
return -1;
|
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,12 +308,20 @@ 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;
|
||||||
|
|
||||||
|
if (guac_socket_write_string(socket, "7.connect")) return -1;
|
||||||
|
|
||||||
|
for (i=0; args[i] != NULL; i++) {
|
||||||
|
|
||||||
|
if (guac_socket_write_string(socket, ","))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (guac_socket_write_array(socket, args))
|
if (__guac_socket_write_length_string(socket, args[i]))
|
||||||
return -1;
|
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;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
|
||||||
|
@ -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 \
|
||||||
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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.
|
||||||
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
@ -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];
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
@ -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,27 +119,14 @@ 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)
|
|
||||||
frame_processing_lag = 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Record baseline duration of frame by excluding lag (this is the
|
|
||||||
* network round-trip time) */
|
|
||||||
int estimated_rtt = frame_duration - frame_processing_lag;
|
|
||||||
user->last_frame_duration = estimated_rtt;
|
|
||||||
|
|
||||||
/* Calculate cumulative accumulated processing lag relative to server timeline */
|
|
||||||
int processing_lag = current - user->last_received_timestamp - estimated_rtt;
|
|
||||||
if (processing_lag < 0)
|
if (processing_lag < 0)
|
||||||
processing_lag = 0;
|
processing_lag = 0;
|
||||||
|
|
||||||
@ -149,32 +134,22 @@ int __guac_handle_sync(guac_user* user, int argc, char** argv) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Record baseline duration of frame by excluding lag */
|
||||||
|
user->last_frame_duration = frame_duration - user->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(
|
||||||
@ -685,23 +660,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 */
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
|
||||||
}
|
|
5
src/protocols/kubernetes/.gitignore
vendored
5
src/protocols/kubernetes/.gitignore
vendored
@ -1,5 +0,0 @@
|
|||||||
|
|
||||||
# Auto-generated test runner and binary
|
|
||||||
_generated_runner.c
|
|
||||||
test_kubernetes
|
|
||||||
|
|
@ -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 \
|
||||||
|
@ -29,59 +29,167 @@
|
|||||||
#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 {
|
||||||
|
|
||||||
guac_client* client = user->client;
|
/**
|
||||||
guac_kubernetes_client* kubernetes_client = (guac_kubernetes_client*) client->data;
|
* The color scheme of the terminal.
|
||||||
guac_terminal* terminal = kubernetes_client->term;
|
*/
|
||||||
|
GUAC_KUBERNETES_ARGV_SETTING_COLOR_SCHEME,
|
||||||
|
|
||||||
/* Update color scheme */
|
/**
|
||||||
if (strcmp(name, GUAC_KUBERNETES_ARGV_COLOR_SCHEME) == 0)
|
* The name of the font family used by the terminal.
|
||||||
guac_terminal_apply_color_scheme(terminal, value);
|
*/
|
||||||
|
GUAC_KUBERNETES_ARGV_SETTING_FONT_NAME,
|
||||||
|
|
||||||
/* Update font name */
|
/**
|
||||||
else if (strcmp(name, GUAC_KUBERNETES_ARGV_FONT_NAME) == 0)
|
* The size of the font used by the terminal, in points.
|
||||||
guac_terminal_apply_font(terminal, value, -1, 0);
|
*/
|
||||||
|
GUAC_KUBERNETES_ARGV_SETTING_FONT_SIZE
|
||||||
|
|
||||||
/* Update only if font size is sane */
|
} guac_kubernetes_argv_setting;
|
||||||
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 */
|
/**
|
||||||
guac_kubernetes_resize(client,
|
* The value or current status of a connection parameter received over an
|
||||||
guac_terminal_get_rows(terminal),
|
* "argv" stream.
|
||||||
guac_terminal_get_columns(terminal));
|
*/
|
||||||
|
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;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void* guac_kubernetes_send_current_argv(guac_user* user, void* data) {
|
/**
|
||||||
|
* 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) {
|
||||||
|
|
||||||
guac_kubernetes_client* kubernetes_client = (guac_kubernetes_client*) data;
|
int size;
|
||||||
|
|
||||||
|
guac_client* client = user->client;
|
||||||
|
guac_kubernetes_client* kubernetes_client = (guac_kubernetes_client*) client->data;
|
||||||
guac_terminal* terminal = kubernetes_client->term;
|
guac_terminal* terminal = kubernetes_client->term;
|
||||||
|
|
||||||
/* Send current color scheme */
|
/* Append null terminator to value */
|
||||||
guac_user_stream_argv(user, user->socket, "text/plain",
|
guac_kubernetes_argv* argv = (guac_kubernetes_argv*) stream->data;
|
||||||
GUAC_KUBERNETES_ARGV_COLOR_SCHEME,
|
argv->buffer[argv->length] = '\0';
|
||||||
guac_terminal_get_color_scheme(terminal));
|
|
||||||
|
|
||||||
/* Send current font name */
|
/* Apply changes to chosen setting */
|
||||||
guac_user_stream_argv(user, user->socket, "text/plain",
|
switch (argv->setting) {
|
||||||
GUAC_KUBERNETES_ARGV_FONT_NAME,
|
|
||||||
guac_terminal_get_font_name(terminal));
|
|
||||||
|
|
||||||
/* Send current font size */
|
/* Update color scheme */
|
||||||
char font_size[64];
|
case GUAC_KUBERNETES_ARGV_SETTING_COLOR_SCHEME:
|
||||||
sprintf(font_size, "%i", guac_terminal_get_font_size(terminal));
|
guac_terminal_apply_color_scheme(terminal, argv->buffer);
|
||||||
guac_user_stream_argv(user, user->socket, "text/plain",
|
break;
|
||||||
GUAC_KUBERNETES_ARGV_FONT_SIZE, font_size);
|
|
||||||
|
|
||||||
return NULL;
|
/* Update font name */
|
||||||
|
case GUAC_KUBERNETES_ARGV_SETTING_FONT_NAME:
|
||||||
|
guac_terminal_apply_font(terminal, argv->buffer, -1, 0);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update Kubernetes terminal size */
|
||||||
|
guac_kubernetes_resize(client, terminal->term_height,
|
||||||
|
terminal->term_width);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -23,52 +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
|
|
||||||
* while the connection is running to the given user. Note that the user
|
|
||||||
* receiving these values will not necessarily be able to set new values
|
|
||||||
* themselves if their connection is read-only. This function can be used as
|
|
||||||
* the callback for guac_client_foreach_user() and guac_client_for_owner()
|
|
||||||
*
|
|
||||||
* @param user
|
|
||||||
* The user that should receive the values of all non-sensitive parameters
|
|
||||||
* which may be set while the connection is running.
|
|
||||||
*
|
|
||||||
* @param data
|
|
||||||
* The guac_kubernetes_client instance associated with the current
|
|
||||||
* connection.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* Always NULL.
|
|
||||||
*/
|
|
||||||
void* guac_kubernetes_send_current_argv(guac_user* user, void* data);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -18,9 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.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 +28,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 +211,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 +226,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) {
|
||||||
@ -264,10 +249,6 @@ void* guac_kubernetes_client_thread(void* data) {
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send current values of exposed arguments to owner only */
|
|
||||||
guac_client_for_owner(client, guac_kubernetes_send_current_argv,
|
|
||||||
kubernetes_client);
|
|
||||||
|
|
||||||
/* Set up typescript, if requested */
|
/* Set up typescript, if requested */
|
||||||
if (settings->typescript_path != NULL) {
|
if (settings->typescript_path != NULL) {
|
||||||
guac_terminal_create_typescript(kubernetes_client->term,
|
guac_terminal_create_typescript(kubernetes_client->term,
|
||||||
@ -369,7 +350,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 +394,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
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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"));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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"));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user