Compare commits

..

16 Commits

Author SHA1 Message Date
matt
152821d77b KASM-1883 add manpage for kasmxproxy 2021-09-15 11:54:42 +00:00
matt
906fcc3e67 KASM-1883 update builds with new deps 2021-09-15 11:07:10 +00:00
Lauri Kasanen
ab32cbc53e Small cleanup, move selection supplying to a func 2021-08-31 17:31:38 +03:00
Lauri Kasanen
a48811b12f Browser to app paste support 2021-08-31 17:22:58 +03:00
Lauri Kasanen
4c4808d2bd Add support for app-to-vnc clipboard copy 2021-08-31 15:37:37 +03:00
Lauri Kasanen
e282851073 Resizing support for nvidia 2021-08-30 14:00:10 +03:00
Lauri Kasanen
40d263368d Pass cursors 2021-08-27 12:16:36 +03:00
Matt
377d3e5629 fix to mode name 2021-08-26 16:38:57 +00:00
Dmitry Maksyoma
5b516e84d9 Fix deb build 2021-08-26 21:26:30 +12:00
Matt
43c31d759b deb package dependency updates 2021-08-25 19:36:51 +00:00
Matt
aa7d1b5a30 Added missing build deps for CentOS 2021-08-25 16:17:33 +00:00
Matt
8b5600e8f3 Add xrandr-dev package to build images 2021-08-25 15:14:30 +00:00
Lauri Kasanen
bbd971e9f1 Resizing support 2021-08-25 14:37:40 +03:00
Lauri Kasanen
0467e938a2 Input 2021-08-25 13:08:42 +03:00
Lauri Kasanen
01623eaffd Image copies 2021-08-25 12:12:00 +03:00
Lauri Kasanen
3383ad52e4 Start on kasmxproxy 2021-08-25 10:58:41 +03:00
94 changed files with 1293 additions and 10761 deletions

View File

@@ -1,33 +1,16 @@
#!/bin/bash
is_kasmvnc() {
local package="$1";
echo "$package" | grep -q 'kasmvncserver_'
}
function prepare_upload_filename() {
local package="$1";
if ! is_kasmvnc "$package"; then
export upload_filename="$package"
return
fi
.ci/detect_os_arch_package_format "$package" > /tmp/os_arch_package_format;
source /tmp/os_arch_package_format;
detect_release_branch
detect_revision "$package" "$OS_ARCH"
if [ -n "$REVISION" ]; then
REVISION="_${REVISION}"
fi
if [ -n "$RELEASE_BRANCH" ]; then
export upload_filename="kasmvncserver_${PACKAGE_OS}_${RELEASE_VERSION}${REVISION}_${OS_ARCH}.${PACKAGE_FORMAT}";
export upload_filename="kasmvncserver_${PACKAGE_OS}_${RELEASE_VERSION}_${OS_ARCH}.${PACKAGE_FORMAT}";
else
export SANITIZED_BRANCH="$(echo $CI_COMMIT_REF_NAME | sed 's/\//_/g')";
export upload_filename="kasmvncserver_${PACKAGE_OS}_${RELEASE_VERSION}_${SANITIZED_BRANCH}_${CI_COMMIT_SHA:0:6}${REVISION}_${OS_ARCH}.${PACKAGE_FORMAT}";
export upload_filename="kasmvncserver_${PACKAGE_OS}_${RELEASE_VERSION}_${SANITIZED_BRANCH}_${CI_COMMIT_SHA:0:6}_${OS_ARCH}.${PACKAGE_FORMAT}";
fi
};
@@ -42,7 +25,6 @@ function upload_to_s3() {
export BUILD_STATUS="{\"key\":\"doc\", \"state\":\"SUCCESSFUL\", \"name\":\"${upload_filename}\", \"url\":\"${S3_URL}\"}";
curl --request POST --header "PRIVATE-TOKEN:${GITLAB_API_TOKEN}" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/statuses/${CI_COMMIT_SHA}?state=success&name=build-url&target_url=${S3_URL}";
};
function prepare_to_run_scripts_and_s3_uploads() {
export DEBIAN_FRONTEND=noninteractive;
apt-get update;
@@ -56,16 +38,3 @@ detect_release_branch() {
export RELEASE_BRANCH=1;
fi
}
detect_revision() {
local package="$1"
local arch="$2"
REVISION=
if ! echo "$package" | grep -q '+'; then
return
fi
REVISION=$(echo "$package" | sed "s/_${arch}.\+//" | sed 's/.\++//')
}

1
.dockerignore Normal file
View File

@@ -0,0 +1 @@
builder/build

View File

@@ -8,45 +8,24 @@ variables:
GIT_FETCH_EXTRA_FLAGS: --tags
stages:
- www
- build
- upload
.prepare_build: &prepare_build
- ls -l
- pwd
- apk add bash
- mkdir -p "$GITLAB_SHARED_DIND_DIR" && chmod 777 "$GITLAB_SHARED_DIND_DIR"
.prepare_www: &prepare_www
- tar -zxf output/www/kasm_www.tar.gz -C builder/
.prepare_artfacts: &prepare_artfacts
- mkdir output
- cp -r builder/build/* output/
- rm output/*.tar.gz
build_www:
stage: www
allow_failure: false
before_script:
- *prepare_build
script:
- webpacked_www=$PWD/builder/www
- src_www=kasmweb
- docker build -t kasmweb/www -f builder/dockerfile.www.build .
- docker run --rm -v $PWD/builder/www:/build kasmweb/www:latest
- mkdir -p output/www
- cd builder
- tar -zcvf ../output/www/kasm_www.tar.gz www
artifacts:
paths:
- output/
build_ubuntu_bionic:
stage: build
allow_failure: true
before_script:
- *prepare_build
- *prepare_www
after_script:
- *prepare_artfacts
script:
@@ -55,150 +34,46 @@ build_ubuntu_bionic:
paths:
- output/
build_ubuntu_bionic_arm:
stage: build
allow_failure: false
tags:
- arm
before_script:
- *prepare_build
- *prepare_www
after_script:
- *prepare_artfacts
script:
- bash builder/build-package ubuntu bionic
artifacts:
paths:
- output/
build_ubuntu_bionic_libjpeg_turbo:
stage: build
allow_failure: false
before_script:
- *prepare_build
- *prepare_www
after_script:
- *prepare_artfacts
script:
- bash builder/build-package ubuntu bionic +libjpeg-turbo_latest
artifacts:
paths:
- output/
build_ubuntu_focal:
stage: build
allow_failure: true
before_script:
- *prepare_build
- *prepare_www
after_script:
- *prepare_artfacts
script:
- bash builder/build-package ubuntu focal;
only:
variables:
- $CI_COMMIT_MESSAGE =~ /\[full [Cc][Ii]\]/
artifacts:
paths:
- output/
build_ubuntu_focal_arm:
stage: build
allow_failure: true
tags:
- arm
before_script:
- *prepare_build
- *prepare_www
after_script:
- *prepare_artfacts
script:
- bash builder/build-package ubuntu focal;
only:
variables:
- $CI_COMMIT_MESSAGE =~ /\[full [Cc][Ii]\]/
artifacts:
paths:
- output/
build_debian_buster:
stage: build
allow_failure: true
before_script:
- *prepare_build
- *prepare_www
after_script:
- *prepare_artfacts
script:
- bash builder/build-package debian buster;
only:
variables:
- $CI_COMMIT_MESSAGE =~ /\[full [cC][Ii]\]/
artifacts:
paths:
- output/
build_debian_buster_arm:
stage: build
allow_failure: true
tags:
- arm
before_script:
- *prepare_build
- *prepare_www
after_script:
- *prepare_artfacts
script:
- bash builder/build-package debian buster;
only:
variables:
- $CI_COMMIT_MESSAGE =~ /\[full [cC][Ii]\]/
artifacts:
paths:
- output/
build_debian_bullseye:
stage: build
allow_failure: true
before_script:
- *prepare_build
- *prepare_www
after_script:
- *prepare_artfacts
script:
- bash builder/build-package debian bullseye;
only:
variables:
- $CI_COMMIT_MESSAGE =~ /\[full [cC][Ii]\]/
artifacts:
paths:
- output/
build_debian_bullseye_arm:
stage: build
allow_failure: true
tags:
- arm
before_script:
- *prepare_build
- *prepare_www
after_script:
- *prepare_artfacts
script:
- bash builder/build-package debian bullseye;
only:
variables:
- $CI_COMMIT_MESSAGE =~ /\[full [cC][Ii]\]/
artifacts:
paths:
- output/
build_kali_rolling:
stage: build
allow_failure: true
before_script:
- *prepare_build
- *prepare_www
after_script:
- *prepare_artfacts
script:
@@ -209,10 +84,8 @@ build_kali_rolling:
build_centos7:
stage: build
allow_failure: true
before_script:
- *prepare_build
- *prepare_www
after_script:
- *prepare_artfacts
script:
@@ -230,11 +103,9 @@ upload:
- export S3_BUILD_DIRECTORY="kasmvnc/${CI_COMMIT_SHA}"
- prepare_to_run_scripts_and_s3_uploads
- export RELEASE_VERSION=$(.ci/next_release_version "$CI_COMMIT_REF_NAME")
- for package in `find output/ -type f -name '*.deb' -or -name '*.rpm'`; do
- for package in `find output/ -type f -name 'kasmvncserver_*.deb' -or -name '*.rpm'`; do
prepare_upload_filename "$package";
echo;
echo "File to upload $upload_filename";
upload_to_s3 "$package" "$upload_filename";
UPLOAD_NAME=$(echo $upload_filename | sed 's#kasmvncserver_##' | sed -r 's#_([0-9]{1,3}\.){2}[0-9]{1,2}_\S+?([a-f0-9]{6})##' | sed -r 's#\.(deb|rpm)##');
curl --request POST --header "PRIVATE-TOKEN:${GITLAB_API_TOKEN}" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/statuses/${CI_COMMIT_SHA}?state=success&name=${UPLOAD_NAME}&target_url=${S3_URL}";
done

View File

@@ -16,7 +16,6 @@ include(CheckLibraryExists)
include(CheckTypeSize)
include(CheckCSourceCompiles)
include(CheckCXXSourceCompiles)
include(CheckCXXCompilerFlag)
include(CheckCSourceRuns)
include(CMakeMacroLibtoolFile)
@@ -209,15 +208,6 @@ if(ENABLE_PAM)
endif()
set(HAVE_PAM ${ENABLE_PAM})
# Check for SSE2
# Arm is not SSE2 but say it is and use sse2neon.h to convert to neon
check_cxx_compiler_flag("-march=armv8-a" COMPILER_ARM)
if(COMPILER_ARM)
set(COMPILER_SUPPORTS_SSE2 1)
else()
check_cxx_compiler_flag(-msse2 COMPILER_SUPPORTS_SSE2)
endif()
# Generate config.h and make sure the source finds it
configure_file(config.h.in config.h)
add_definitions(-DHAVE_CONFIG_H)

View File

@@ -42,9 +42,9 @@ Future Goals:
#### Debian-based
```sh
wget https://github.com/kasmtech/KasmVNC/releases/download/v0.9.2-beta/kasmvncserver_ubuntu_bionic_0.9.2_amd64.deb
wget -qO- https://github.com/kasmtech/KasmVNC/releases/download/v0.9.1-beta/kasmvncserver_0.9.1~beta-1_amd64.deb
sudo dpkg -i kasmvncserver_*.deb
sudo dpkg -i kasmvncserver_0.9.1~beta-1_amd64.deb
sudo apt-get -f install
# We provide an example script to run KasmVNC at #

View File

@@ -3,17 +3,8 @@ Docker CE
# Build a deb/rpm package
```
# builder/build-package <os> <os_codename> <build_tag>
# builder/build-package <os> <os_codename>
# os_codename is what "lsb_release -c" outputs, e.g. buster, focal.
#
# build_tag allows building multiple versions of deb package (rpm not supported)
# targeting a single distro release (e.g. Ubuntu Bionic). If build_tag is given,
# the package name will include build_tag as part of Debian revision. For
# example:
# * with build_tag: kasmvncserver_0.9.1~beta-1+libjpeg-turbo-latest_amd64.deb
# * without build_tag: kasmvncserver_0.9.1~beta-1_amd64.deb
# You need to have .build and .deb.build for the build_tag.
#
# Packages will be placed under builder/build/
builder/build-package ubuntu bionic
@@ -79,64 +70,3 @@ packages installed with XFCE.
```
builder/test-deb-barebones ubuntu focal
```
# Preparing a release
Deb and rpm packages need their versions bumped to the new release version. It
can be done with:
```
builder/bump-package-version 0.9.4-beta
```
This will update corresponding package files, use `git diff` to see changes.
If you've ran the command and curious about Debian version specifics, here's an
explanation:
Deb version will be `0.9.4~beta-1`. `~` (and not `-`) is required by packaging
guidelines, and `-1` is Debian package revision for `0.9.4` upstream release. If
a Debian-specific patch was later added on top of `0.9.4`, it'd be `-2` for the
next Debian version. Rpm has a corresponding revision in its .spec file.
# CI development
S3 upload code is extracted to various files in `.ci`. It's possible to iterate
locally by doing stuff like this:
```
bash -c '
. .ci/upload.sh;
prepare_upload_filename "bionic/kasmvncserver_0.9.1~beta-1+libjpeg-turbo-latest_amd64.deb";
echo $upload_filename;'
```
# ARM
KasmVNC is supported on ARM, however, the build process needs to be broken into two parts with one occuring on a x64 system and the other on an ARM system. All our testing and official builds are done on AWS Graviton instances.
### Build www code on x86 System
The www code is webpacked for performance and thus requires building. There are NPM packages, phantomjs, which do not have an ARM build. Therefore, this must be built on x86 and then copied over to the ARM system for final packaging.
```
cd ~/KasmVNC
mkdir builder/www
sudo docker build -t kasmweb/www -f builder/dockerfile.www.build .
sudo docker run --rm -v $PWD/builder/www:/build kasmweb/www:latest
cd builder
tar -zcvf /tmp/kasm_www.tar.gz www
```
Now transfer ```kasm_www.tar.gz``` to the ARM system.
### Build KasmVNC ARM
These instructions assume KasmVNC has been cloned at $HOME and ```kasm_www.tar.gz``` has been placed at $HOME as well, adjust for your environment.
```
cd ~
tar -zxf kasm_www.tar.gz -C KasmVNC/builder/
cd KasmVNC
sed -i 's/^build_www_dir$/#build_www_dir/' builder/build-tarball
sudo builder/build-package ubuntu bionic
```
The resulting deb package can be found under ~/KasmVNC/builder/build/bionic
Replace ```bionic``` with ```focal``` to build for Ubuntu 20.04LTS. At this time, only Ubuntu Bionic has been tested, however, other Debian based builds we support should also work.

View File

@@ -5,17 +5,12 @@ set -e
cd "$(dirname "$0")/.."
. builder/os_ver_cli.sh
L_UID=$(id -u) #Ubuntu already has UID env var, but this should work on all Linix systems
L_GID=$(id -g)
builder_image=debbuilder_${os}:${os_codename}${build_tag_for_images}
docker build --build-arg KASMVNC_PACKAGE_DIR="builder/build/${os_codename}" \
--build-arg L_UID="$L_UID" \
-t "$builder_image" -f \
builder/dockerfile.${os}_${os_codename}${build_tag}.deb.build .
docker build -t debbuilder_${os}:${os_codename} -f \
builder/dockerfile.${os}_${os_codename}.deb.build .
deb_output_dir=$(cd .. && echo $PWD)
docker run --rm -v "$deb_output_dir":/src -e BUILD_TAG="$build_tag" \
--user "$L_UID:$L_GID" \
"$builder_image" /bin/bash -c \
L_UID=$(id -u) #Ubuntu already has UID env var, but this should work on all Linix systems
L_GID=$(id -g)
docker run --rm -v "$deb_output_dir":/src --user $L_UID:$L_GID \
debbuilder_${os}:${os_codename} /bin/bash -c \
'/src/*/builder/build-deb-inside-docker'

View File

@@ -2,31 +2,12 @@
set -e
prepare_docker_copy_of_debian_dir_and_cd_to_id() {
cd "$(dirname "$0")/.."
local mounted_src_dir="$PWD"
local docker_src_dir="$HOME/src"
mkdir "$docker_src_dir"
cd "$docker_src_dir"
cp -a "$mounted_src_dir/debian/" .
ln -s "$mounted_src_dir/builder" .
}
prepare_docker_copy_of_debian_dir_and_cd_to_id
cd "$(dirname "$0")/.."
os=$(lsb_release -is | tr '[:upper:]' '[:lower:]')
os_codename=$(lsb_release -cs)
os_dir="builder/build/${os_codename}"
if [ "$BUILD_TAG" = "+libjpeg-turbo_latest" ]; then
echo 'libjpeg 62 libjpeg-turbo (>= 2.1.1)' > debian/shlibs.local
debian_revision="$(echo $BUILD_TAG | tr _ -)"
sed -i -e "1 s/)/$debian_revision)/" debian/changelog
fi
dpkg-buildpackage -us -uc -b
mkdir -p "$os_dir"
cp ../*.deb "$os_dir"

View File

@@ -4,7 +4,6 @@ set -e
os="$1"
codename="$2"
build_tag="$3"
detect_package_format() {
package_format=rpm
@@ -13,17 +12,8 @@ detect_package_format() {
fi
}
warn_build_tag_not_supported_for_rpm_and_exit() {
if [[ "$build_tag" && "$package_format" = "rpm" ]]; then
echo >&2 "<build_tag> isn't supported for rpm"
exit 1
fi
}
cd "$(dirname "$0")/.."
detect_package_format
warn_build_tag_not_supported_for_rpm_and_exit
builder/build-tarball "$os" "$codename" "$build_tag"
builder/build-${package_format} "$os" "$codename" "$build_tag"
builder/build-tarball "$os" "$codename"
builder/build-${package_format} "$os" "$codename"

View File

@@ -2,18 +2,8 @@
set -e
warn_build_tag_not_supported_for_rpm_and_exit() {
local build_tag="$1"
if [[ -n "$build_tag" ]]; then
echo >&2 "<build_tag> isn't supported for rpm"
exit 1
fi
}
cd "$(dirname "$0")/.."
. builder/os_ver_cli.sh
warn_build_tag_not_supported_for_rpm_and_exit "$build_tag"
docker build -t kasmvnc_rpmbuilder_${os}:${os_codename} -f \
builder/dockerfile.${os}_${os_codename}.rpm.build .

View File

@@ -1,6 +1,7 @@
#!/bin/bash
set -e
set -x
build_www_dir() {
local webpacked_www=$PWD/builder/www
@@ -12,13 +13,6 @@ build_www_dir() {
fi
}
move_libjpeg_turbo_to_os_specific_dir() {
chown $L_UID:$L_GID $shared_with_docker_dir/${os_codename}/*
mkdir -p $PWD/builder/build/${os_codename}/
mv $shared_with_docker_dir/${os_codename}/libjpeg-turbo*.deb \
$PWD/builder/build/${os_codename}/
}
shared_with_docker_dir=${GITLAB_SHARED_DIND_DIR:-/tmp}
cd "$(dirname "$0")/.."
@@ -26,21 +20,13 @@ cd "$(dirname "$0")/.."
build_www_dir
builder_image=kasmvncbuilder:$os_codename${build_tag_for_images}
docker build -t "$builder_image" \
-f builder/dockerfile.${os}_${os_codename}${build_tag}.build .
docker build -t kasmvncbuilder:$os_codename \
-f builder/dockerfile.${os}_${os_codename}.build .
mkdir -p builder/build
docker run -v $shared_with_docker_dir:/build -e BUILD_TAG="$build_tag" \
--rm "$builder_image"
docker run -v $shared_with_docker_dir:/build --rm kasmvncbuilder:$os_codename
L_GID=$(id -g)
L_UID=$(id -u)
tarball_name="kasmvnc.${os}_${os_codename}.tar.gz"
tarball_name_with_build_tag="kasmvnc.${os}_${os_codename}${build_tag}.tar.gz"
chown $L_UID:$L_GID $shared_with_docker_dir/$tarball_name
mv $shared_with_docker_dir/$tarball_name \
$PWD/builder/build/"$tarball_name_with_build_tag"
if [ "$build_tag" = "+libjpeg-turbo_latest" ]; then
move_libjpeg_turbo_to_os_specific_dir
fi
mv $shared_with_docker_dir/$tarball_name $PWD/builder/build/

View File

@@ -1,5 +1,7 @@
#!/bin/sh -e
set -x
detect_quilt() {
if which quilt 1>/dev/null; then
QUILT_PRESENT=1
@@ -30,19 +32,16 @@ sed -i -e '/find_package(FLTK/s@^@#@' \
-e '/add_subdirectory(tests/s@^@#@' \
CMakeLists.txt
cmake -D CMAKE_BUILD_TYPE=RelWithDebInfo . -DBUILD_VIEWER:BOOL=OFF
cmake -D CMAKE_BUILD_TYPE=RelWithDebInfo .
make -j5
tar -C unix/xserver -xf /tmp/xorg-server-${XORG_VER}.tar.bz2 --strip-components=1
tar -C unix/xserver -xvf /tmp/xorg-server-${XORG_VER}.tar.bz2 --strip-components=1
cd unix/xserver
patch -Np1 -i ../xserver${XORG_PATCH}.patch
case "$XORG_VER" in
1.20.*)
if [ -f ../xserver120.7.patch ]; then
patch -Np1 -i ../xserver120.7.patch
fi ;;
esac
if [[ $XORG_VER =~ ^1\.20\..*$ ]]; then
patch -Np1 -i ../xserver120.7.patch
fi
autoreconf -i
# Configuring Xorg is long and has many distro-specific paths.
@@ -75,8 +74,6 @@ mkdir lib
cd lib
if [ -d /usr/lib/x86_64-linux-gnu/dri ]; then
ln -s /usr/lib/x86_64-linux-gnu/dri dri
elif [ -d /usr/lib/aarch64-linux-gnu/dri ]; then
ln -s /usr/lib/aarch64-linux-gnu/dri dri
else
ln -s /usr/lib64/dri dri
fi
@@ -85,11 +82,9 @@ cd /src
detect_quilt
if [ -n "$QUILT_PRESENT" ]; then
quilt push -a
echo 'Patches applied!'
fi
make servertarball
cp kasmvnc*.tar.gz /build/kasmvnc.${KASMVNC_BUILD_OS}_${KASMVNC_BUILD_OS_CODENAME}.tar.gz
if [ "$BUILD_TAG" = "+libjpeg-turbo_latest" ]; then
mkdir -p /build/${KASMVNC_BUILD_OS_CODENAME}/
cp /libjpeg-turbo/libjpeg*.deb /build/${KASMVNC_BUILD_OS_CODENAME}/
fi

View File

@@ -1,41 +0,0 @@
#!/usr/bin/env bash
set -eo pipefail
update_version_to_meet_packaging_standards() {
new_version=$(echo "$new_version" |
sed -e 's/\([0-9]\)-\([a-zA-Z]\)/\1~\2/')
}
add_debian_revision_to_new_version() {
echo "$new_version-1"
}
bump_rpm() {
sed -i "s/^Version:.\+/Version: $new_version/" centos/kasmvncserver.spec
}
bump_deb() {
local image="debbump_package_version:dev"
local L_UID=$(id -u)
local L_GID=$(id -g)
local debian_version=$(add_debian_revision_to_new_version)
docker build -t "$image" -f builder/dockerfile.bump-package-version .
docker run --rm -v "$PWD":/src --user "$L_UID:$L_GID" \
"$image" /bin/bash -c \
"cd /src && builder/bump-package-version-inside-docker-deb $debian_version"
}
new_version="$1"
if [[ -z "$new_version" ]]; then
echo >&2 "Usage: $(basename "$0") <new_version>"
exit 1
fi
cd "$(dirname "$0")/.."
update_version_to_meet_packaging_standards
bump_rpm
bump_deb

View File

@@ -1,15 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
new_version="$1"
update_version() {
dch --newversion $new_version 'New upstream release.'
}
mark_as_released() {
dch --release ""
}
update_version
mark_as_released

View File

@@ -1,6 +0,0 @@
FROM debian:buster
ENV DEBEMAIL="Kasm Technologies LLC <info@kasmweb.com>"
RUN apt-get update && \
apt-get -y install vim devscripts

View File

@@ -3,7 +3,6 @@ FROM centos:centos7
ENV KASMVNC_BUILD_OS centos
ENV KASMVNC_BUILD_OS_CODENAME core
RUN yum update -y ca-certificates
RUN yum install -y build-dep xorg-server libxfont-dev sudo
RUN yum install -y gcc cmake git libjpeg-dev libgnutls28-dev vim wget tightvncserver
RUN yum install -y libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev openssl-devel
@@ -11,12 +10,13 @@ RUN yum install -y make
RUN yum group install -y "Development Tools"
RUN yum install -y xorg-x11-server-devel zlib-devel libjpeg-turbo-devel
RUN yum install -y libxkbfile-devel libXfont2-devel xorg-x11-font-utils \
xorg-x11-xtrans-devel xorg-x11-xkb-utils-devel
xorg-x11-xtrans-devel xorg-x11-xkb-utils-devel libXrandr-devel pam-devel \
gnutls-devel libX11-devel libXtst-devel libXcursor-devel
RUN yum install -y mesa-dri-drivers
# Additions for webp
RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
RUN cd /tmp && tar -xzf /tmp/libwebp-*
RUN cd /tmp && tar -xzvf /tmp/libwebp-*
RUN cd /tmp/libwebp-1.0.2 && \
./configure --enable-static --disable-shared && \
make && make install

View File

@@ -13,11 +13,11 @@ RUN apt-get update && \
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata
RUN apt-get update && apt-get -y build-dep xorg-server libxfont-dev
RUN apt-get update && apt-get -y install cmake git libjpeg-dev libgnutls28-dev vim wget tightvncserver
RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev
RUN apt-get update && apt-get -y install libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libxrandr-dev libxcursor-dev
# Additions for webp
RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
RUN cd /tmp && tar -xzf /tmp/libwebp-*
RUN cd /tmp && tar -xzvf /tmp/libwebp-*
RUN cd /tmp/libwebp-1.0.2 && \
./configure --enable-static --disable-shared && \
make && make install

View File

@@ -9,11 +9,4 @@ RUN apt-get update && \
COPY ./debian/control /tmp
RUN apt-get update && echo YYY | mk-build-deps --install --remove /tmp/control
ARG L_UID
RUN if [ "$L_UID" -eq 0 ]; then \
useradd -m docker; \
else \
useradd -m docker -u $L_UID;\
fi
USER docker
USER 1000

View File

@@ -42,8 +42,8 @@ COPY startup/ $STARTUPDIR
### START CUSTOM STUFF ####
ARG KASMVNC_PACKAGE_DIR
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp/
RUN rm -f /tmp/kasmvncserver_*+*.deb; dpkg -i /tmp/*.deb; apt-get -yf install
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp
RUN dpkg -i /tmp/*.deb; apt-get -yf install
### END CUSTOM STUFF ###

View File

@@ -1,8 +1,8 @@
FROM debian:buster-slim
ARG KASMVNC_PACKAGE_DIR
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp/
RUN rm -f /tmp/kasmvncserver_*+*.deb; apt-get update && dpkg -i /tmp/*.deb; apt-get -yf install
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp
RUN apt-get update && dpkg -i /tmp/*.deb; apt-get -yf install
RUN apt-get update && apt-get -y install xterm
COPY startup/deb/kasmvncserver-easy-start /usr/local/bin

View File

@@ -13,11 +13,11 @@ RUN apt-get update && \
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata
RUN apt-get update && apt-get -y build-dep xorg-server libxfont-dev
RUN apt-get update && apt-get -y install cmake git libjpeg-dev libgnutls28-dev vim wget tightvncserver
RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev
RUN apt-get update && apt-get -y install libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libxrandr-dev libxcursor-dev
# Additions for webp
RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
RUN cd /tmp && tar -xzf /tmp/libwebp-*
RUN cd /tmp && tar -xzvf /tmp/libwebp-*
RUN cd /tmp/libwebp-1.0.2 && \
./configure --enable-static --disable-shared && \
make && make install

View File

@@ -9,11 +9,4 @@ RUN apt-get update && \
COPY ./debian/control /tmp
RUN apt-get update && echo YYY | mk-build-deps --install --remove /tmp/control
ARG L_UID
RUN if [ "$L_UID" -eq 0 ]; then \
useradd -m docker; \
else \
useradd -m docker -u $L_UID;\
fi
USER docker
USER 1000

View File

@@ -42,8 +42,8 @@ COPY startup/ $STARTUPDIR
### START CUSTOM STUFF ####
ARG KASMVNC_PACKAGE_DIR
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp/
RUN rm -f /tmp/kasmvncserver_*+*.deb; dpkg -i /tmp/*.deb; apt-get -yf install
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp
RUN dpkg -i /tmp/*.deb; apt-get -yf install
### END CUSTOM STUFF ###

View File

@@ -18,13 +18,13 @@ RUN dnf install -y make
RUN dnf group install -y "Development Tools"
RUN dnf install -y xorg-x11-server-devel zlib-devel libjpeg-turbo-devel
RUN dnf install -y libxkbfile-devel libXfont2-devel xorg-x11-font-utils \
xorg-x11-xtrans-devel xorg-x11-xkb-utils-devel
xorg-x11-xtrans-devel xorg-x11-xkb-utils-devel libXrandr-devel libXcursor-devel
RUN dnf install -y mesa-dri-drivers
RUN dnf install -y bzip2 redhat-lsb-core
# Additions for webp
RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
RUN cd /tmp && tar -xzf /tmp/libwebp-*
RUN cd /tmp && tar -xzvf /tmp/libwebp-*
RUN cd /tmp/libwebp-1.0.2 && \
./configure --enable-static --disable-shared && \
make && make install

View File

@@ -13,11 +13,11 @@ RUN apt-get update && \
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata
RUN apt-get update && apt-get -y build-dep xorg-server libxfont-dev
RUN apt-get update && apt-get -y install cmake git libjpeg-dev libgnutls28-dev vim wget tightvncserver
RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev
RUN apt-get update && apt-get -y install libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libxrandr-dev libxcursor-dev
# Additions for webp
RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
RUN cd /tmp && tar -xzf /tmp/libwebp-*
RUN cd /tmp && tar -xzvf /tmp/libwebp-*
RUN cd /tmp/libwebp-1.0.2 && \
./configure --enable-static --disable-shared && \
make && make install

View File

@@ -9,11 +9,4 @@ RUN apt-get update && \
COPY ./debian/control /tmp
RUN apt-get update && echo YYY | mk-build-deps --install --remove /tmp/control
ARG L_UID
RUN if [ "$L_UID" -eq 0 ]; then \
useradd -m docker; \
else \
useradd -m docker -u $L_UID;\
fi
USER docker
USER 1000

View File

@@ -42,8 +42,8 @@ COPY startup/ $STARTUPDIR
### START CUSTOM STUFF ####
ARG KASMVNC_PACKAGE_DIR
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp/
RUN rm -f /tmp/kasmvncserver_*+*.deb; dpkg -i /tmp/*.deb; apt-get -yf install
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp
RUN dpkg -i /tmp/*.deb; apt-get -yf install
### END CUSTOM STUFF ###

View File

@@ -1,39 +0,0 @@
FROM ubuntu:18.04
ENV KASMVNC_BUILD_OS ubuntu
ENV KASMVNC_BUILD_OS_CODENAME bionic
ENV XORG_VER 1.20.10
RUN sed -i 's$# deb-src$deb-src$' /etc/apt/sources.list
RUN apt-get update && \
apt-get -y install sudo
RUN apt-get update && apt-get -y build-dep xorg-server libxfont-dev
RUN apt-get update && apt-get -y install cmake git libgnutls28-dev vim wget tightvncserver
RUN apt-get update && apt-get -y install libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev
RUN apt-get update && apt-get install -y cmake nasm gcc
RUN git clone https://github.com/libjpeg-turbo/libjpeg-turbo.git
RUN export MAKEFLAGS=-j`nproc`; cd libjpeg-turbo && cmake -G"Unix Makefiles" && make deb
RUN export MAKEFLAGS=-j`nproc`; cd libjpeg-turbo && cmake -DCMAKE_INSTALL_PREFIX=/usr/local -G"Unix Makefiles" && make && make install
# Additions for webp
RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
RUN cd /tmp && tar -xzf /tmp/libwebp-*
RUN cd /tmp/libwebp-1.0.2 && \
./configure --enable-static --disable-shared && \
make && make install
# Fix for older required libs
#RUN cd /tmp && wget http://launchpadlibrarian.net/347526424/libxfont1-dev_1.5.2-4ubuntu2_amd64.deb && \
# wget http://launchpadlibrarian.net/347526425/libxfont1_1.5.2-4ubuntu2_amd64.deb && \
# dpkg -i libxfont1_1.5.2-4ubuntu2_amd64.deb && \
# dpkg -i libxfont1-dev_1.5.2-4ubuntu2_amd64.deb
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
COPY --chown=docker:docker . /src
USER docker
ENTRYPOINT ["/src/builder/build.sh"]

View File

@@ -1,23 +0,0 @@
FROM ubuntu:bionic
RUN apt-get update && \
apt-get -y install vim build-essential devscripts equivs
ARG KASMVNC_PACKAGE_DIR
COPY $KASMVNC_PACKAGE_DIR/libjpeg-turbo*deb /tmp
RUN apt-get install /tmp/libjpeg-turbo*deb
# Install build-deps for the package.
COPY ./debian/control /tmp
RUN apt-get update && echo YYY | mk-build-deps --install --remove /tmp/control
ENV LD_LIBRARY_PATH="/opt/libjpeg-turbo/lib64/:$LD_LIBRARY_PATH"
ARG L_UID
RUN if [ "$L_UID" -eq 0 ]; then \
useradd -m docker; \
else \
useradd -m docker -u $L_UID;\
fi
USER docker

View File

@@ -1,58 +0,0 @@
FROM ubuntu:bionic
ENV DISPLAY=:1 \
VNC_PORT=8443 \
VNC_RESOLUTION=1280x720 \
MAX_FRAME_RATE=24 \
VNCOPTIONS="-PreferBandwidth -DynamicQualityMin=4 -DynamicQualityMax=7" \
HOME=/home/user \
TERM=xterm \
STARTUPDIR=/dockerstartup \
INST_SCRIPTS=/dockerstartup/install \
KASM_RX_HOME=/dockerstartup/kasmrx \
DEBIAN_FRONTEND=noninteractive \
VNC_COL_DEPTH=24 \
VNC_RESOLUTION=1280x1024 \
VNC_PW=vncpassword \
VNC_USER=user \
VNC_VIEW_ONLY_PW=vncviewonlypassword \
LD_LIBRARY_PATH=/opt/libjpeg-turbo/lib64/:/usr/local/lib/ \
OMP_WAIT_POLICY=PASSIVE \
SHELL=/bin/bash \
SINGLE_APPLICATION=0 \
KASMVNC_BUILD_OS=ubuntu \
KASMVNC_BUILD_OS_CODENAME=bionic
EXPOSE $VNC_PORT
WORKDIR $HOME
### REQUIRED STUFF ###
RUN apt-get update && apt-get install -y supervisor xfce4 xfce4-terminal xterm libnss-wrapper gettext wget
RUN apt-get purge -y pm-utils xscreensaver*
RUN apt-get update && apt-get install -y vim less
RUN apt-get update && apt-get -y install lsb-release
RUN echo 'source $STARTUPDIR/generate_container_user' >> $HOME/.bashrc
RUN mkdir -p $STARTUPDIR
COPY startup/ $STARTUPDIR
### START CUSTOM STUFF ####
ARG KASMVNC_PACKAGE_DIR
COPY $KASMVNC_PACKAGE_DIR/libjpeg-turbo_*.deb /tmp
RUN apt-get install /tmp/libjpeg-turbo*deb
ARG BUILD_DEBIAN_REVISION
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*$BUILD_DEBIAN_REVISION*.deb /tmp
RUN dpkg -i /tmp/*.deb; apt-get -yf install
### END CUSTOM STUFF ###
RUN chown -R 1000:0 $HOME
USER 1000:ssl-cert
WORKDIR $HOME
ENTRYPOINT [ "/dockerstartup/vnc_startup.sh" ]

View File

@@ -11,11 +11,11 @@ RUN apt-get update && \
RUN apt-get update && apt-get -y build-dep xorg-server libxfont-dev
RUN apt-get update && apt-get -y install cmake git libjpeg-dev libgnutls28-dev vim wget tightvncserver
RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev
RUN apt-get update && apt-get -y install libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libxrandr-dev libxcursor-dev
# Additions for webp
RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
RUN cd /tmp && tar -xzf /tmp/libwebp-*
RUN cd /tmp && tar -xzvf /tmp/libwebp-*
RUN cd /tmp/libwebp-1.0.2 && \
./configure --enable-static --disable-shared && \
make && make install

View File

@@ -7,11 +7,4 @@ RUN apt-get update && \
COPY ./debian/control /tmp
RUN apt-get update && echo YYY | mk-build-deps --install --remove /tmp/control
ARG L_UID
RUN if [ "$L_UID" -eq 0 ]; then \
useradd -m docker; \
else \
useradd -m docker -u $L_UID;\
fi
USER docker
USER 1000

View File

@@ -42,8 +42,8 @@ COPY startup/ $STARTUPDIR
### START CUSTOM STUFF ####
ARG KASMVNC_PACKAGE_DIR
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp/
RUN rm -f /tmp/kasmvncserver_*+*.deb; dpkg -i /tmp/*.deb; apt-get -yf install
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp
RUN dpkg -i /tmp/*.deb; apt-get -yf install
### END CUSTOM STUFF ###

View File

@@ -1,8 +1,8 @@
FROM ubuntu:focal
ARG KASMVNC_PACKAGE_DIR
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp/
RUN rm -f /tmp/kasmvncserver_*+*.deb; apt-get update && dpkg -i /tmp/*.deb; apt-get -yf install
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp
RUN apt-get update && dpkg -i /tmp/*.deb; apt-get -yf install
RUN apt-get update && apt-get -y install xterm lsb-release
RUN useradd -m foo && addgroup foo ssl-cert

View File

@@ -13,11 +13,11 @@ RUN apt-get update && \
RUN apt-get update && apt-get install -y --no-install-recommends tzdata
RUN apt-get update && apt-get -y build-dep xorg-server libxfont-dev
RUN apt-get update && apt-get -y install cmake git libjpeg-dev libgnutls28-dev vim wget tightvncserver
RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev
RUN apt-get update && apt-get -y install libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libxrandr-dev libxcursor-dev
# Additions for webp
RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
RUN cd /tmp && tar -xzf /tmp/libwebp-*
RUN cd /tmp && tar -xzvf /tmp/libwebp-*
RUN cd /tmp/libwebp-1.0.2 && \
./configure --enable-static --disable-shared && \
make && make install

View File

@@ -9,11 +9,4 @@ RUN apt-get update && \
COPY ./debian/control /tmp
RUN apt-get update && echo YYY | mk-build-deps --install --remove /tmp/control
ARG L_UID
RUN if [ "$L_UID" -eq 0 ]; then \
useradd -m docker; \
else \
useradd -m docker -u $L_UID;\
fi
USER docker
USER 1000

View File

@@ -42,8 +42,8 @@ COPY startup/ $STARTUPDIR
### START CUSTOM STUFF ####
ARG KASMVNC_PACKAGE_DIR
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp/
RUN rm -f /tmp/kasmvncserver_*+*.deb; dpkg -i /tmp/*.deb; apt-get -yf install
COPY $KASMVNC_PACKAGE_DIR/kasmvncserver_*.deb /tmp
RUN dpkg -i /tmp/*.deb; apt-get -yf install
RUN mkdir ~/.vnc && echo '/usr/bin/xfce4-session &' >> ~/.vnc/xstartup && \
chmod +x ~/.vnc/xstartup

View File

@@ -3,11 +3,6 @@ default_os_codename=bionic
os=${1:-$default_os}
os_codename=${2:-$default_os_codename}
build_tag="${3:-}"
if [[ -n "$build_tag" ]]; then
build_tag_for_images="_${build_tag#+}"
build_debian_revision="$(echo $build_tag | tr _ -)"
fi
os_image="$os:$os_codename"
echo "Building for $os_image"

View File

@@ -5,12 +5,10 @@ set -e
cd "$(dirname "$0")"
. ./os_ver_cli.sh
tester_image=kasmvnctester_${os}:$os_codename${build_tag_for_images}
docker build --build-arg KASMVNC_PACKAGE_DIR="build/${os_codename}" \
--build-arg BUILD_DEBIAN_REVISION="$build_debian_revision" \
-t "$tester_image" \
-f dockerfile.${os}_${os_codename}${build_tag}.deb.test .
-t kasmvnctester_${os}:$os_codename \
-f dockerfile.${os}_${os_codename}.deb.test .
docker run -it -p 443:8443 --rm \
-e KASMVNC_VERBOSE_LOGGING=$KASMVNC_VERBOSE_LOGGING \
-e "VNC_USER=foo" -e "VNC_PW=foobar" \
"$tester_image"
kasmvnctester_${os}:$os_codename

View File

@@ -62,8 +62,10 @@ cp $SRC/man/man1/Xvnc.1 $DESTDIR/usr/share/man/man1/;
cp $SRC/share/man/man1/vncserver.1 $DST_MAN;
cp $SRC/share/man/man1/vncconfig.1 $DST_MAN;
cp $SRC/share/man/man1/vncpasswd.1 $DST_MAN;
cp $SRC/share/man/man1/kasmxpoxy.1 $DST_MAN;
cd $DST_MAN && ln -s vncpasswd.1 kasmvncpasswd.1;
%files
/usr/bin/*
/usr/share/man/man1/*

View File

@@ -21,7 +21,6 @@
#include <inttypes.h>
#include <network/GetAPI.h>
#include <rfb/ConnParams.h>
#include <rfb/EncodeManager.h>
#include <rfb/LogWriter.h>
#include <rfb/JpegCompressor.h>
#include <rfb/xxhash.h>
@@ -33,6 +32,10 @@ using namespace rfb;
static LogWriter vlog("GetAPIMessager");
PixelBuffer *progressiveBilinearScale(const PixelBuffer *pb,
const uint16_t tgtw, const uint16_t tgth,
const float tgtdiff);
struct TightJPEGConfiguration {
int quality;
int subsampling;

View File

@@ -111,7 +111,7 @@ static const char *parse_get(const char * const in, const char * const opt, unsi
return "";
}
static void percent_decode(const char *src, char *dst, const uint8_t filter) {
static void percent_decode(const char *src, char *dst) {
while (1) {
if (!*src)
break;
@@ -127,32 +127,7 @@ static void percent_decode(const char *src, char *dst, const uint8_t filter) {
hex[2] = '\0';
src += 2;
*dst = strtol(hex, NULL, 16);
if (filter) {
// Avoid directory traversal
if (*dst == '/')
*dst = '_';
}
dst++;
}
}
*dst = '\0';
}
static void percent_encode(const char *src, char *dst) {
while (1) {
if (!*src)
break;
if (isalnum(*src) || *src == '.' || *src == ',') {
*dst++ = *src++;
} else {
*dst++ = '%';
sprintf(dst, "%02X", *src);
dst += 2;
src++;
*dst++ = strtol(hex, NULL, 16);
}
}
@@ -771,7 +746,6 @@ def:
static void dirlisting(ws_ctx_t *ws_ctx, const char fullpath[], const char path[]) {
char buf[4096];
char enc[PATH_MAX * 3 + 1];
unsigned i;
// Redirect?
@@ -805,13 +779,11 @@ static void dirlisting(ws_ctx_t *ws_ctx, const char fullpath[], const char path[
if (!strcmp(names[i]->d_name, ".") || !strcmp(names[i]->d_name, ".."))
continue;
percent_encode(names[i]->d_name, enc);
if (names[i]->d_type == DT_DIR)
sprintf(buf, "<li><a href=\"%s/\">%s/</a></li>", enc,
sprintf(buf, "<li><a href=\"%s/\">%s/</a></li>", names[i]->d_name,
names[i]->d_name);
else
sprintf(buf, "<li><a href=\"%s\">%s</a></li>", enc,
sprintf(buf, "<li><a href=\"%s\">%s</a></li>", names[i]->d_name,
names[i]->d_name);
ws_send(ws_ctx, buf, strlen(buf));
@@ -850,15 +822,13 @@ static void servefile(ws_ctx_t *ws_ctx, const char *in) {
len = strlen(path);
}
percent_decode(path, buf, 1);
wserr("Requested file '%s'\n", buf);
sprintf(fullpath, "%s/%s", settings.httpdir, buf);
wserr("Requested file '%s'\n", path);
sprintf(fullpath, "%s/%s", settings.httpdir, path);
DIR *dir = opendir(fullpath);
if (dir) {
closedir(dir);
dirlisting(ws_ctx, fullpath, buf);
dirlisting(ws_ctx, fullpath, path);
return;
}
@@ -1006,14 +976,14 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) {
if (len) {
memcpy(buf, param, len);
buf[len] = '\0';
percent_decode(buf, decname, 0);
percent_decode(buf, decname);
}
param = parse_get(args, "password", &len);
if (len) {
memcpy(buf, param, len);
buf[len] = '\0';
percent_decode(buf, decpw, 0);
percent_decode(buf, decpw);
struct crypt_data cdata;
cdata.initialized = 0;
@@ -1053,7 +1023,7 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) {
if (len) {
memcpy(buf, param, len);
buf[len] = '\0';
percent_decode(buf, decname, 0);
percent_decode(buf, decname);
}
if (!decname[0])
@@ -1082,7 +1052,7 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) {
if (len) {
memcpy(buf, param, len);
buf[len] = '\0';
percent_decode(buf, decname, 0);
percent_decode(buf, decname);
}
if (!decname[0])
@@ -1127,7 +1097,7 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) {
if (len) {
memcpy(buf, param, len);
buf[len] = '\0';
percent_decode(buf, decname, 0);
percent_decode(buf, decname);
} else {
wserr("client param required\n");
goto nope;
@@ -1350,8 +1320,6 @@ ws_ctx_t *do_handshake(int sock) {
} else {
// Client tried an empty password, just fail them
response[0] = '\0';
authbuf[0] = 'a';
authbuf[1] = '\0';
}
}

View File

@@ -44,11 +44,11 @@ static void do_proxy(ws_ctx_t *ws_ctx, int target) {
int maxfd, client = ws_ctx->sockfd;
unsigned int opcode, left, ret;
unsigned int tout_start, tout_end, cout_start, cout_end;
unsigned int tin_end;
unsigned int tin_start, tin_end;
ssize_t len, bytes;
tout_start = tout_end = cout_start = cout_end =
tin_end = 0;
tin_start = tin_end = 0;
maxfd = client > target ? client+1 : target+1;
while (1) {
@@ -165,7 +165,7 @@ static void do_proxy(ws_ctx_t *ws_ctx, int target) {
}
if (FD_ISSET(client, &rlist)) {
bytes = ws_recv(ws_ctx, ws_ctx->tin_buf + tin_end, BUFSIZE-1-tin_end);
bytes = ws_recv(ws_ctx, ws_ctx->tin_buf + tin_end, BUFSIZE-1);
if (pipe_error) { break; }
if (bytes <= 0) {
handler_emsg("client closed connection\n");
@@ -180,13 +180,13 @@ static void do_proxy(ws_ctx_t *ws_ctx, int target) {
printf("\n");
*/
if (ws_ctx->hybi) {
len = decode_hybi(ws_ctx->tin_buf,
tin_end,
len = decode_hybi(ws_ctx->tin_buf + tin_start,
tin_end-tin_start,
ws_ctx->tout_buf, BUFSIZE-1,
&opcode, &left);
} else {
len = decode_hixie(ws_ctx->tin_buf,
tin_end,
len = decode_hixie(ws_ctx->tin_buf + tin_start,
tin_end-tin_start,
ws_ctx->tout_buf, BUFSIZE-1,
&opcode, &left);
}
@@ -208,13 +208,10 @@ static void do_proxy(ws_ctx_t *ws_ctx, int target) {
break;
}
if (left) {
const unsigned tin_start = tin_end - left;
//handler_emsg("partial frame from client, %u left, start %u end %u, lens %lu %lu\n",
// left, tin_start, tin_end, bytes, len);
memmove(ws_ctx->tin_buf, ws_ctx->tin_buf + tin_start, left);
tin_end = left;
tin_start = tin_end - left;
//printf("partial frame from client");
} else {
//handler_emsg("handled %lu/%lu bytes\n", bytes, len);
tin_start = 0;
tin_end = 0;
}

View File

@@ -48,7 +48,6 @@ set(RFB_SOURCES
Security.cxx
SecurityServer.cxx
SecurityClient.cxx
SelfBench.cxx
SSecurityPlain.cxx
SSecurityStack.cxx
SSecurityVncAuth.cxx
@@ -64,7 +63,6 @@ set(RFB_SOURCES
VNCServerST.cxx
ZRLEEncoder.cxx
ZRLEDecoder.cxx
cpuid.cxx
encodings.cxx
util.cxx
xxhash.c)
@@ -98,32 +96,6 @@ if(GNUTLS_FOUND)
)
endif()
# SSE2
set(SSE2_SOURCES
scale_sse2.cxx)
set(SCALE_DUMMY_SOURCES
scale_dummy.cxx)
if(COMPILER_SUPPORTS_SSE2)
if(COMPILER_ARM)
# This is for Graviton2, adjust for other CPUs: -march=armv8-a+crc+crypto
set_source_files_properties(${SSE2_SOURCES} PROPERTIES COMPILE_FLAGS ${COMPILE_FLAGS} -march=armv8.2-a+fp16+rcpc+dotprod+crypto)
else()
set_source_files_properties(${SSE2_SOURCES} PROPERTIES COMPILE_FLAGS ${COMPILE_FLAGS} -msse2)
endif()
set(RFB_SOURCES
${RFB_SOURCES}
${SSE2_SOURCES}
)
else()
set(RFB_SOURCES
${RFB_SOURCES}
${SCALE_DUMMY_SOURCES}
)
endif()
add_library(rfb STATIC ${RFB_SOURCES})
target_link_libraries(rfb ${RFB_LIBRARIES})

View File

@@ -22,12 +22,10 @@
#include <omp.h>
#include <stdlib.h>
#include <rfb/cpuid.h>
#include <rfb/EncCache.h>
#include <rfb/EncodeManager.h>
#include <rfb/Encoder.h>
#include <rfb/Palette.h>
#include <rfb/scale_sse2.h>
#include <rfb/SConnection.h>
#include <rfb/ServerCore.h>
#include <rfb/SMsgWriter.h>
@@ -897,7 +895,7 @@ void EncodeManager::updateVideoStats(const std::vector<Rect> &rects, const Pixel
}
}
PixelBuffer *rfb::nearestScale(const PixelBuffer *pb, const uint16_t w, const uint16_t h,
static PixelBuffer *nearestScale(const PixelBuffer *pb, const uint16_t w, const uint16_t h,
const float diff)
{
ManagedPixelBuffer *newpb = new ManagedPixelBuffer(pb->getPF(), w, h);
@@ -922,7 +920,7 @@ PixelBuffer *rfb::nearestScale(const PixelBuffer *pb, const uint16_t w, const ui
return newpb;
}
PixelBuffer *rfb::bilinearScale(const PixelBuffer *pb, const uint16_t w, const uint16_t h,
static PixelBuffer *bilinearScale(const PixelBuffer *pb, const uint16_t w, const uint16_t h,
const float diff)
{
ManagedPixelBuffer *newpb = new ManagedPixelBuffer(pb->getPF(), w, h);
@@ -970,68 +968,10 @@ PixelBuffer *rfb::bilinearScale(const PixelBuffer *pb, const uint16_t w, const u
return newpb;
}
PixelBuffer *rfb::progressiveBilinearScale(const PixelBuffer *pb,
PixelBuffer *progressiveBilinearScale(const PixelBuffer *pb,
const uint16_t tgtw, const uint16_t tgth,
const float tgtdiff)
{
if (supportsSSE2()) {
if (tgtdiff >= 0.5f) {
ManagedPixelBuffer *newpb = new ManagedPixelBuffer(pb->getPF(), tgtw, tgth);
int oldstride, newstride;
const rdr::U8 *oldpx = pb->getBuffer(pb->getRect(), &oldstride);
rdr::U8 *newpx = newpb->getBufferRW(newpb->getRect(), &newstride);
SSE2_scale(oldpx, tgtw, tgth, newpx, oldstride, newstride, tgtdiff);
return newpb;
}
PixelBuffer *newpb;
uint16_t neww, newh, oldw, oldh;
bool del = false;
do {
oldw = pb->getRect().width();
oldh = pb->getRect().height();
neww = oldw / 2;
newh = oldh / 2;
newpb = new ManagedPixelBuffer(pb->getPF(), neww, newh);
int oldstride, newstride;
const rdr::U8 *oldpx = pb->getBuffer(pb->getRect(), &oldstride);
rdr::U8 *newpx = ((ManagedPixelBuffer *) newpb)->getBufferRW(newpb->getRect(),
&newstride);
SSE2_halve(oldpx, neww, newh, newpx, oldstride, newstride);
if (del)
delete pb;
del = true;
pb = newpb;
} while (tgtw * 2 < neww);
// Final, non-halving step
if (tgtw != neww || tgth != newh) {
oldw = pb->getRect().width();
oldh = pb->getRect().height();
newpb = new ManagedPixelBuffer(pb->getPF(), tgtw, tgth);
int oldstride, newstride;
const rdr::U8 *oldpx = pb->getBuffer(pb->getRect(), &oldstride);
rdr::U8 *newpx = ((ManagedPixelBuffer *) newpb)->getBufferRW(newpb->getRect(),
&newstride);
SSE2_scale(oldpx, tgtw, tgth, newpx, oldstride, newstride, tgtw / (float) oldw);
if (del)
delete pb;
}
return newpb;
} // SSE2
if (tgtdiff >= 0.5f)
return bilinearScale(pb, tgtw, tgth, tgtdiff);

View File

@@ -215,13 +215,6 @@ namespace rfb {
virtual rdr::U8* getBufferRW(const Rect& r, int* stride);
};
};
PixelBuffer *nearestScale(const PixelBuffer *pb, const uint16_t w, const uint16_t h,
const float diff);
PixelBuffer *bilinearScale(const PixelBuffer *pb, const uint16_t w, const uint16_t h,
const float diff);
PixelBuffer *progressiveBilinearScale(const PixelBuffer *pb, const uint16_t w, const uint16_t h,
const float diff);
}
#endif

View File

@@ -38,9 +38,7 @@ namespace rfb {
virtual void pointerEvent(const Point& __unused_attr pos,
int __unused_attr buttonMask,
const bool __unused_attr skipClick,
const bool __unused_attr skipRelease,
int scrollX,
int scrollY) { }
const bool __unused_attr skipRelease) { }
virtual void clientCutText(const char* __unused_attr str,
int __unused_attr len) { }
};

View File

@@ -284,26 +284,74 @@ void SConnection::setEncodings(int nEncodings, const rdr::S32* encodings)
}
SMsgHandler::setEncodings(nEncodings, encodings);
if (cp.supportsExtendedClipboard) {
rdr::U32 sizes[] = { 0 };
writer()->writeClipboardCaps(rfb::clipboardUTF8 |
rfb::clipboardRequest |
rfb::clipboardPeek |
rfb::clipboardNotify |
rfb::clipboardProvide,
sizes);
}
}
void SConnection::clearBinaryClipboard()
void SConnection::clientCutText(const char* str, int len)
{
binaryClipboard.clear();
hasLocalClipboard = false;
strFree(clientClipboard);
clientClipboard = NULL;
clientClipboard = latin1ToUTF8(str);
handleClipboardAnnounce(true);
}
void SConnection::addBinaryClipboard(const char mime[], const rdr::U8 *data,
const rdr::U32 len)
void SConnection::handleClipboardRequest(rdr::U32 flags)
{
binaryClipboard_t bin;
strncpy(bin.mime, mime, sizeof(bin.mime));
bin.mime[sizeof(bin.mime) - 1] = '\0';
bin.data.resize(len);
memcpy(&bin.data[0], data, len);
binaryClipboard.push_back(bin);
if (!(flags & rfb::clipboardUTF8))
return;
if (!hasLocalClipboard)
return;
handleClipboardRequest();
}
void SConnection::handleClipboardPeek(rdr::U32 flags)
{
if (!hasLocalClipboard)
return;
if (cp.clipboardFlags() & rfb::clipboardNotify)
writer()->writeClipboardNotify(rfb::clipboardUTF8);
}
void SConnection::handleClipboardNotify(rdr::U32 flags)
{
strFree(clientClipboard);
clientClipboard = NULL;
if (flags & rfb::clipboardUTF8) {
handleClipboardAnnounce(true);
hasLocalClipboard = false;
} else {
handleClipboardAnnounce(false);
}
}
void SConnection::handleClipboardProvide(rdr::U32 flags,
const size_t* lengths,
const rdr::U8* const* data)
{
if (!(flags & rfb::clipboardUTF8))
return;
strFree(clientClipboard);
clientClipboard = NULL;
clientClipboard = convertLF((const char*)data[0], lengths[0]);
handleClipboardData(clientClipboard, strlen(clientClipboard));
}
void SConnection::supportsQEMUKeyEvent()
{
@@ -397,13 +445,56 @@ void SConnection::enableContinuousUpdates(bool enable,
{
}
void SConnection::handleClipboardRequest()
{
}
void SConnection::handleClipboardAnnounce(bool available)
{
}
void SConnection::handleClipboardData(const char* data, int len)
{
}
void SConnection::requestClipboard()
{
if (clientClipboard != NULL) {
handleClipboardData(clientClipboard, strlen(clientClipboard));
return;
}
if (cp.supportsExtendedClipboard &&
(cp.clipboardFlags() & rfb::clipboardRequest))
writer()->writeClipboardRequest(rfb::clipboardUTF8);
}
void SConnection::announceClipboard(bool available)
{
hasLocalClipboard = available;
if (cp.supportsExtendedClipboard &&
(cp.clipboardFlags() & rfb::clipboardNotify))
writer()->writeClipboardNotify(available ? rfb::clipboardUTF8 : 0);
else {
if (available)
handleClipboardRequest();
}
}
void SConnection::sendClipboardData(const char* data, int len)
{
if (cp.supportsExtendedClipboard &&
(cp.clipboardFlags() & rfb::clipboardProvide)) {
CharArray filtered(convertCRLF(data));
size_t sizes[1] = { strlen(filtered.buf) + 1 };
const rdr::U8* data[1] = { (const rdr::U8*)filtered.buf };
writer()->writeClipboardProvide(rfb::clipboardUTF8, sizes, data);
} else {
CharArray latin1(utf8ToLatin1(data));
writer()->writeServerCutText(latin1.buf, strlen(latin1.buf));
}
}
void SConnection::writeFakeColourMap(void)

View File

@@ -28,7 +28,6 @@
#include <rdr/OutStream.h>
#include <rfb/SMsgHandler.h>
#include <rfb/SecurityServer.h>
#include <vector>
namespace rfb {
@@ -74,9 +73,14 @@ namespace rfb {
virtual void setEncodings(int nEncodings, const rdr::S32* encodings);
virtual void clearBinaryClipboard();
virtual void addBinaryClipboard(const char mime[], const rdr::U8 *data,
const rdr::U32 len);
virtual void clientCutText(const char* str, int len);
virtual void handleClipboardRequest(rdr::U32 flags);
virtual void handleClipboardPeek(rdr::U32 flags);
virtual void handleClipboardNotify(rdr::U32 flags);
virtual void handleClipboardProvide(rdr::U32 flags,
const size_t* lengths,
const rdr::U8* const* data);
virtual void supportsQEMUKeyEvent();
@@ -123,11 +127,25 @@ namespace rfb {
virtual void enableContinuousUpdates(bool enable,
int x, int y, int w, int h);
// handleClipboardRequest() is called whenever the client requests
// the server to send over its clipboard data. It will only be
// called after the server has first announced a clipboard change
// via announceClipboard().
virtual void handleClipboardRequest();
// handleClipboardAnnounce() is called to indicate a change in the
// clipboard on the client. Call requestClipboard() to access the
// actual data.
virtual void handleClipboardAnnounce(bool available);
// handleClipboardData() is called when the client has sent over
// the clipboard data as a result of a previous call to
// requestClipboard(). Note that this function might never be
// called if the clipboard data was no longer available when the
// client received the request.
virtual void handleClipboardData(const char* data, int len);
virtual void add_changed_all() {}
// setAccessRights() allows a security package to limit the access rights
@@ -148,11 +166,22 @@ namespace rfb {
// Other methods
// requestClipboard() will result in a request to the client to
// transfer its clipboard data. A call to handleClipboardData()
// will be made once the data is available.
virtual void requestClipboard();
// announceClipboard() informs the client of changes to the
// clipboard on the server. The client may later request the
// clipboard data via handleClipboardRequest().
virtual void announceClipboard(bool available);
// sendClipboardData() transfers the clipboard data to the client
// and should be called whenever the client has requested the
// clipboard via handleClipboardRequest().
virtual void sendClipboardData(const char* data, int len);
// authenticated() returns true if the client has authenticated
// successfully.
bool authenticated() { return (state_ == RFBSTATE_INITIALISATION ||
@@ -192,19 +221,12 @@ namespace rfb {
rdr::S32 getPreferredEncoding() { return preferredEncoding; }
struct binaryClipboard_t {
char mime[32];
std::vector<unsigned char> data;
};
protected:
void setState(stateEnum s) { state_ = s; }
void setReader(SMsgReader *r) { reader_ = r; }
void setWriter(SMsgWriter *w) { writer_ = w; }
std::vector<binaryClipboard_t> binaryClipboard;
private:
void writeFakeColourMap(void);

View File

@@ -78,13 +78,23 @@ namespace rfb {
// the relevant RFB protocol messages from clients.
// See InputHandler for method signatures.
// handleClipboardRequest() is called whenever a client requests
// the server to send over its clipboard data. It will only be
// called after the server has first announced a clipboard change
// via VNCServer::announceClipboard().
virtual void handleClipboardRequest() {}
// handleClipboardAnnounce() is called to indicate a change in the
// clipboard on a client. Call VNCServer::requestClipboard() to
// access the actual data.
virtual void handleClipboardAnnounce(bool __unused_attr available) {}
virtual void handleClipboardAnnounceBinary(const unsigned __unused_attr num,
const char __unused_attr mimes[][32]) {}
// handleClipboardData() is called when a client has sent over
// the clipboard data as a result of a previous call to
// VNCServer::requestClipboard(). Note that this function might
// never be called if the clipboard data was no longer available
// when the client received the request.
virtual void handleClipboardData(const char* __unused_attr data, int len __unused_attr) {}
protected:
virtual ~SDesktop() {}

View File

@@ -64,16 +64,26 @@ void SMsgHandler::setEncodings(int nEncodings, const rdr::S32* encodings)
supportsQEMUKeyEvent();
}
void SMsgHandler::handleClipboardAnnounceBinary(const unsigned, const char mimes[][32])
void SMsgHandler::handleClipboardCaps(rdr::U32 flags, const rdr::U32* lengths)
{
cp.setClipboardCaps(flags, lengths);
}
void SMsgHandler::handleClipboardRequest(rdr::U32 flags)
{
}
void SMsgHandler::clearBinaryClipboard()
void SMsgHandler::handleClipboardPeek(rdr::U32 flags)
{
}
void SMsgHandler::addBinaryClipboard(const char mime[], const rdr::U8 *data,
const rdr::U32 len)
void SMsgHandler::handleClipboardNotify(rdr::U32 flags)
{
}
void SMsgHandler::handleClipboardProvide(rdr::U32 flags,
const size_t* lengths,
const rdr::U8* const* data)
{
}

View File

@@ -54,10 +54,14 @@ namespace rfb {
virtual void enableContinuousUpdates(bool enable,
int x, int y, int w, int h) = 0;
virtual void handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]);
virtual void clearBinaryClipboard();
virtual void addBinaryClipboard(const char mime[], const rdr::U8 *data,
const rdr::U32 len);
virtual void handleClipboardCaps(rdr::U32 flags,
const rdr::U32* lengths);
virtual void handleClipboardRequest(rdr::U32 flags);
virtual void handleClipboardPeek(rdr::U32 flags);
virtual void handleClipboardNotify(rdr::U32 flags);
virtual void handleClipboardProvide(rdr::U32 flags,
const size_t* lengths,
const rdr::U8* const* data);
virtual void sendStats(const bool toClient = true) = 0;
virtual void handleFrameStats(rdr::U32 all, rdr::U32 render) = 0;

View File

@@ -35,6 +35,8 @@ using namespace rfb;
static LogWriter vlog("SMsgReader");
static IntParameter maxCutText("MaxCutText", "Maximum permitted length of an incoming clipboard update", 256*1024);
SMsgReader::SMsgReader(SMsgHandler* handler_, rdr::InStream* is_)
: handler(handler_), is(is_)
{
@@ -81,9 +83,6 @@ void SMsgReader::readMsg()
case msgTypeFrameStats:
readFrameStats();
break;
case msgTypeBinaryClipboard:
readBinaryClipboard();
break;
case msgTypeKeyEvent:
readKeyEvent();
break;
@@ -224,9 +223,7 @@ void SMsgReader::readPointerEvent()
int mask = is->readU8();
int x = is->readU16();
int y = is->readU16();
int scrollX = is->readS16();
int scrollY = is->readS16();
handler->pointerEvent(Point(x, y), mask, false, false, scrollX, scrollY);
handler->pointerEvent(Point(x, y), mask, false, false);
}
@@ -241,54 +238,109 @@ void SMsgReader::readClientCutText()
readExtendedClipboard(slen);
return;
}
is->skip(len);
vlog.error("Client sent old cuttext msg, ignoring");
}
void SMsgReader::readBinaryClipboard()
{
const rdr::U8 num = is->readU8();
rdr::U8 i, valid = 0;
char tmpmimes[num][32];
handler->clearBinaryClipboard();
for (i = 0; i < num; i++) {
const rdr::U8 mimelen = is->readU8();
if (mimelen > 32 - 1) {
vlog.error("Mime too long (%u)", mimelen);
}
char mime[mimelen + 1];
mime[mimelen] = '\0';
is->readBytes(mime, mimelen);
strncpy(tmpmimes[valid], mime, 32);
tmpmimes[valid][31] = '\0';
const rdr::U32 len = is->readU32();
CharArray ca(len);
is->readBytes(ca.buf, len);
if (rfb::Server::DLP_ClipAcceptMax && len > (unsigned) rfb::Server::DLP_ClipAcceptMax) {
vlog.info("DLP: refused to receive binary clipboard, too large");
continue;
}
vlog.debug("Received binary clipboard, type %s, %u bytes", mime, len);
handler->addBinaryClipboard(mime, (rdr::U8 *) ca.buf, len);
valid++;
if (len > (size_t)maxCutText) {
is->skip(len);
vlog.error("Cut text too long (%d bytes) - ignoring", len);
return;
}
handler->handleClipboardAnnounceBinary(valid, tmpmimes);
CharArray ca(len+1);
ca.buf[len] = 0;
is->readBytes(ca.buf, len);
handler->clientCutText(ca.buf, len);
}
void SMsgReader::readExtendedClipboard(rdr::S32 len)
{
rdr::U32 flags;
rdr::U32 action;
if (len < 4)
throw Exception("Invalid extended clipboard message");
vlog.error("Client sent old cuttext msg, ignoring");
is->skip(len);
if (len > maxCutText) {
vlog.error("Extended clipboard message too long (%d bytes) - ignoring", len);
is->skip(len);
return;
}
flags = is->readU32();
action = flags & clipboardActionMask;
if (action & clipboardCaps) {
int i;
size_t num;
rdr::U32 lengths[16];
num = 0;
for (i = 0;i < 16;i++) {
if (flags & (1 << i))
num++;
}
if (len < (rdr::S32)(4 + 4*num))
throw Exception("Invalid extended clipboard message");
num = 0;
for (i = 0;i < 16;i++) {
if (flags & (1 << i))
lengths[num++] = is->readU32();
}
handler->handleClipboardCaps(flags, lengths);
} else if (action == clipboardProvide) {
rdr::ZlibInStream zis;
int i;
size_t num;
size_t lengths[16];
rdr::U8* buffers[16];
zis.setUnderlying(is, len - 4);
num = 0;
for (i = 0;i < 16;i++) {
if (!(flags & 1 << i))
continue;
lengths[num] = zis.readU32();
if (lengths[num] > (size_t)maxCutText) {
vlog.error("Extended clipboard data too long (%d bytes) - ignoring",
(unsigned)lengths[num]);
zis.skip(lengths[num]);
flags &= ~(1 << i);
continue;
}
buffers[num] = new rdr::U8[lengths[num]];
zis.readBytes(buffers[num], lengths[num]);
num++;
}
zis.flushUnderlying();
zis.setUnderlying(NULL, 0);
handler->handleClipboardProvide(flags, lengths, buffers);
num = 0;
for (i = 0;i < 16;i++) {
if (!(flags & 1 << i))
continue;
delete [] buffers[num++];
}
} else {
switch (action) {
case clipboardRequest:
handler->handleClipboardRequest(flags);
break;
case clipboardPeek:
handler->handleClipboardPeek(flags);
break;
case clipboardNotify:
handler->handleClipboardNotify(flags);
break;
default:
throw Exception("Invalid extended clipboard action");
}
}
}
void SMsgReader::readRequestStats()

View File

@@ -58,7 +58,6 @@ namespace rfb {
void readExtendedClipboard(rdr::S32 len);
void readRequestStats();
void readFrameStats();
void readBinaryClipboard();
void readQEMUMessage();
void readQEMUKeyEvent();

View File

@@ -85,21 +85,118 @@ void SMsgWriter::writeBell()
endMsg();
}
void SMsgWriter::writeBinaryClipboard(const std::vector<SConnection::binaryClipboard_t> &b)
void SMsgWriter::writeServerCutText(const char* str, int len)
{
startMsg(msgTypeBinaryClipboard);
startMsg(msgTypeServerCutText);
os->pad(3);
os->writeU32(len);
os->writeBytes(str, len);
endMsg();
}
os->writeU8(b.size());
rdr::U8 i;
for (i = 0; i < b.size(); i++) {
const rdr::U8 mimelen = strlen(b[i].mime);
os->writeU8(mimelen);
os->writeBytes(b[i].mime, mimelen);
void SMsgWriter::writeClipboardCaps(rdr::U32 caps,
const rdr::U32* lengths)
{
size_t i, count;
os->writeU32(b[i].data.size());
os->writeBytes(&b[i].data[0], b[i].data.size());
if (!cp->supportsExtendedClipboard)
throw Exception("Client does not support extended clipboard");
count = 0;
for (i = 0;i < 16;i++) {
if (caps & (1 << i))
count++;
}
startMsg(msgTypeServerCutText);
os->pad(3);
os->writeS32(-(4 + 4 * count));
os->writeU32(caps | clipboardCaps);
count = 0;
for (i = 0;i < 16;i++) {
if (caps & (1 << i))
os->writeU32(lengths[count++]);
}
endMsg();
}
void SMsgWriter::writeClipboardRequest(rdr::U32 flags)
{
if (!cp->supportsExtendedClipboard)
throw Exception("Client does not support extended clipboard");
if (!(cp->clipboardFlags() & clipboardRequest))
throw Exception("Client does not support clipboard \"request\" action");
startMsg(msgTypeServerCutText);
os->pad(3);
os->writeS32(-4);
os->writeU32(flags | clipboardRequest);
endMsg();
}
void SMsgWriter::writeClipboardPeek(rdr::U32 flags)
{
if (!cp->supportsExtendedClipboard)
throw Exception("Client does not support extended clipboard");
if (!(cp->clipboardFlags() & clipboardPeek))
throw Exception("Client does not support clipboard \"peek\" action");
startMsg(msgTypeServerCutText);
os->pad(3);
os->writeS32(-4);
os->writeU32(flags | clipboardPeek);
endMsg();
}
void SMsgWriter::writeClipboardNotify(rdr::U32 flags)
{
if (!cp->supportsExtendedClipboard)
throw Exception("Client does not support extended clipboard");
if (!(cp->clipboardFlags() & clipboardNotify))
throw Exception("Client does not support clipboard \"notify\" action");
startMsg(msgTypeServerCutText);
os->pad(3);
os->writeS32(-4);
os->writeU32(flags | clipboardNotify);
endMsg();
}
void SMsgWriter::writeClipboardProvide(rdr::U32 flags,
const size_t* lengths,
const rdr::U8* const* data)
{
rdr::MemOutStream mos;
rdr::ZlibOutStream zos;
int i, count;
if (!cp->supportsExtendedClipboard)
throw Exception("Client does not support extended clipboard");
if (!(cp->clipboardFlags() & clipboardProvide))
throw Exception("Client does not support clipboard \"provide\" action");
zos.setUnderlying(&mos);
count = 0;
for (i = 0;i < 16;i++) {
if (!(flags & (1 << i)))
continue;
zos.writeU32(lengths[count]);
zos.writeBytes(data[count], lengths[count]);
count++;
}
zos.flush();
startMsg(msgTypeServerCutText);
os->pad(3);
os->writeS32(-(4 + mos.length()));
os->writeU32(flags | clipboardProvide);
os->writeBytes(mos.data(), mos.length());
endMsg();
}

View File

@@ -26,8 +26,6 @@
#include <rdr/types.h>
#include <rfb/encodings.h>
#include <rfb/ScreenSet.h>
#include <rfb/SConnection.h>
#include <vector>
namespace rdr { class OutStream; }
@@ -56,8 +54,14 @@ namespace rfb {
// writeBell() and writeServerCutText() do the obvious thing.
void writeBell();
void writeServerCutText(const char* str, int len);
void writeBinaryClipboard(const std::vector<SConnection::binaryClipboard_t> &b);
void writeClipboardCaps(rdr::U32 caps, const rdr::U32* lengths);
void writeClipboardRequest(rdr::U32 flags);
void writeClipboardPeek(rdr::U32 flags);
void writeClipboardNotify(rdr::U32 flags);
void writeClipboardProvide(rdr::U32 flags, const size_t* lengths,
const rdr::U8* const* data);
void writeStats(const char* str, int len);

View File

@@ -1,197 +0,0 @@
/* Copyright (C) 2021 Kasm Web
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
#include <rfb/ComparingUpdateTracker.h>
#include <rfb/EncodeManager.h>
#include <rfb/LogWriter.h>
#include <rfb/SConnection.h>
#include <rfb/ServerCore.h>
#include <rfb/PixelBuffer.h>
#include <rfb/TightJPEGEncoder.h>
#include <rfb/TightWEBPEncoder.h>
#include <rfb/util.h>
#include <sys/time.h>
#include <stdint.h>
#include <stdlib.h>
using namespace rfb;
static LogWriter vlog("SelfBench");
static const PixelFormat pfRGBX(32, 24, false, true, 255, 255, 255, 0, 8, 16);
#define RUNS 64
#define W 1600
#define H 1200
void SelfBench() {
unsigned i, runs;
struct timeval start;
ManagedPixelBuffer f1(pfRGBX, W, H);
ManagedPixelBuffer f2(pfRGBX, W, H);
ManagedPixelBuffer screen(pfRGBX, W, H);
int stride;
rdr::U8 *f1ptr = f1.getBufferRW(f1.getRect(), &stride);
rdr::U8 *f2ptr = f2.getBufferRW(f2.getRect(), &stride);
rdr::U8 * const screenptr = screen.getBufferRW(screen.getRect(), &stride);
rdr::U8 * const f1orig = f1ptr;
rdr::U8 * const f2orig = f2ptr;
for (i = 0; i < W * H * 4; i += 4) {
f1ptr[0] = rand();
f1ptr[1] = rand();
f1ptr[2] = rand();
f2ptr[0] = rand();
f2ptr[1] = rand();
f2ptr[2] = rand();
f1ptr += 4;
f2ptr += 4;
}
vlog.info("Running micro-benchmarks (single-threaded, runs depending on task)");
// Encoding
std::vector<uint8_t> vec;
TightJPEGEncoder jpeg(NULL);
gettimeofday(&start, NULL);
runs = RUNS;
for (i = 0; i < runs; i++) {
jpeg.compressOnly(&f1, 8, vec, false);
}
vlog.info("Jpeg compression at quality 8 took %u ms (%u runs)", msSince(&start), runs);
gettimeofday(&start, NULL);
runs = RUNS;
for (i = 0; i < runs; i++) {
jpeg.compressOnly(&f1, 4, vec, false);
}
vlog.info("Jpeg compression at quality 4 took %u ms (%u runs)", msSince(&start), runs);
TightWEBPEncoder webp(NULL);
gettimeofday(&start, NULL);
runs = RUNS / 8;
for (i = 0; i < runs; i++) {
webp.compressOnly(&f1, 8, vec, false);
}
vlog.info("Webp compression at quality 8 took %u ms (%u runs)", msSince(&start), runs);
gettimeofday(&start, NULL);
runs = RUNS / 4;
for (i = 0; i < runs; i++) {
webp.compressOnly(&f1, 4, vec, false);
}
vlog.info("Webp compression at quality 4 took %u ms (%u runs)", msSince(&start), runs);
// Scaling
gettimeofday(&start, NULL);
runs = RUNS;
for (i = 0; i < runs; i++) {
PixelBuffer *pb = nearestScale(&f1, W * 0.8, H * 0.8, 0.8);
delete pb;
}
vlog.info("Nearest scaling to 80%% took %u ms (%u runs)", msSince(&start), runs);
gettimeofday(&start, NULL);
runs = RUNS;
for (i = 0; i < runs; i++) {
PixelBuffer *pb = nearestScale(&f1, W * 0.4, H * 0.4, 0.4);
delete pb;
}
vlog.info("Nearest scaling to 40%% took %u ms (%u runs)", msSince(&start), runs);
gettimeofday(&start, NULL);
runs = RUNS;
for (i = 0; i < runs; i++) {
PixelBuffer *pb = bilinearScale(&f1, W * 0.8, H * 0.8, 0.8);
delete pb;
}
vlog.info("Bilinear scaling to 80%% took %u ms (%u runs)", msSince(&start), runs);
gettimeofday(&start, NULL);
runs = RUNS;
for (i = 0; i < runs; i++) {
PixelBuffer *pb = bilinearScale(&f1, W * 0.4, H * 0.4, 0.4);
delete pb;
}
vlog.info("Bilinear scaling to 40%% took %u ms (%u runs)", msSince(&start), runs);
gettimeofday(&start, NULL);
runs = RUNS;
for (i = 0; i < runs; i++) {
PixelBuffer *pb = progressiveBilinearScale(&f1, W * 0.8, H * 0.8, 0.8);
delete pb;
}
vlog.info("Progressive bilinear scaling to 80%% took %u ms (%u runs)", msSince(&start), runs);
gettimeofday(&start, NULL);
runs = RUNS;
for (i = 0; i < runs; i++) {
PixelBuffer *pb = progressiveBilinearScale(&f1, W * 0.4, H * 0.4, 0.4);
delete pb;
}
vlog.info("Progressive bilinear scaling to 40%% took %u ms (%u runs)", msSince(&start), runs);
// Analysis
ComparingUpdateTracker *comparer = new ComparingUpdateTracker(&screen);
Region cursorReg;
Server::detectScrolling.setParam(false);
Server::detectHorizontal.setParam(false);
gettimeofday(&start, NULL);
runs = RUNS;
for (i = 0; i < runs; i++) {
memcpy(screenptr, i % 2 ? f1orig : f2orig, W * H * 4);
comparer->compare(true, cursorReg);
}
vlog.info("Analysis took %u ms (%u runs) (incl. memcpy overhead)", msSince(&start), runs);
Server::detectScrolling.setParam(true);
gettimeofday(&start, NULL);
runs = RUNS;
for (i = 0; i < runs; i++) {
memcpy(screenptr, i % 2 ? f1orig : f2orig, W * H * 4);
comparer->compare(false, cursorReg);
}
vlog.info("Analysis w/ scroll detection took %u ms (%u runs) (incl. memcpy overhead)", msSince(&start), runs);
Server::detectHorizontal.setParam(true);
delete comparer;
comparer = new ComparingUpdateTracker(&screen);
gettimeofday(&start, NULL);
runs = RUNS / 2;
for (i = 0; i < runs; i++) {
memcpy(screenptr, i % 2 ? f1orig : f2orig, W * H * 4);
comparer->compare(false, cursorReg);
}
vlog.info("Analysis w/ horizontal scroll detection took %u ms (%u runs) (incl. memcpy overhead)", msSince(&start), runs);
exit(0);
}

View File

@@ -113,10 +113,6 @@ rfb::BoolParameter rfb::Server::ignoreClientSettingsKasm
("IgnoreClientSettingsKasm",
"Ignore the additional client settings exposed in Kasm.",
false);
rfb::BoolParameter rfb::Server::selfBench
("SelfBench",
"Run self-benchmarks and exit.",
false);
rfb::IntParameter rfb::Server::dynamicQualityMin
("DynamicQualityMin",
"The minimum dynamic JPEG quality, 0 = low, 9 = high",
@@ -149,15 +145,15 @@ rfb::IntParameter rfb::Server::webpVideoQuality
rfb::IntParameter rfb::Server::DLP_ClipSendMax
("DLP_ClipSendMax",
"Limit clipboard bytes to send to clients in one transaction",
0, 0, INT_MAX);
10000, 0, INT_MAX);
rfb::IntParameter rfb::Server::DLP_ClipAcceptMax
("DLP_ClipAcceptMax",
"Limit clipboard bytes to receive from clients in one transaction",
0, 0, INT_MAX);
10000, 0, INT_MAX);
rfb::IntParameter rfb::Server::DLP_ClipDelay
("DLP_ClipDelay",
"This many milliseconds must pass between clipboard actions",
0, 0, INT_MAX);
1000, 0, INT_MAX);
rfb::IntParameter rfb::Server::DLP_KeyRateLimit
("DLP_KeyRateLimit",
"Reject keyboard presses over this many per second",
@@ -171,10 +167,6 @@ rfb::StringParameter rfb::Server::DLP_Region
("DLP_Region",
"Black out anything outside this region",
"");
rfb::StringParameter rfb::Server::DLP_Clip_Types
("DLP_ClipTypes",
"Allowed binary clipboard mimetypes",
"text/html,image/png");
rfb::BoolParameter rfb::Server::DLP_RegionAllowClick
("DLP_RegionAllowClick",

View File

@@ -50,7 +50,6 @@ namespace rfb {
static IntParameter DLP_KeyRateLimit;
static StringParameter DLP_ClipLog;
static StringParameter DLP_Region;
static StringParameter DLP_Clip_Types;
static BoolParameter DLP_RegionAllowClick;
static BoolParameter DLP_RegionAllowRelease;
static IntParameter jpegVideoQuality;
@@ -75,7 +74,6 @@ namespace rfb {
static BoolParameter detectScrolling;
static BoolParameter detectHorizontal;
static BoolParameter ignoreClientSettingsKasm;
static BoolParameter selfBench;
static PresetParameter preferBandwidth;
};

View File

@@ -71,12 +71,12 @@ static const struct TightWEBPConfiguration conf[10] = {
{ 24, 0 }, // 1
{ 30, 0 }, // 2
{ 37, 0 }, // 3
{ 42, 0 }, // 4
{ 65, 0 }, // 5
{ 78, 0 }, // 6
{ 85, 0 }, // 7
{ 88, 0 }, // 8
{ 100, 0 } // 9
{ 42, 1 }, // 4
{ 65, 1 }, // 5
{ 78, 1 }, // 6
{ 85, 2 }, // 7
{ 88, 3 }, // 8
{ 100, 4 } // 9
};
@@ -143,7 +143,7 @@ void TightWEBPEncoder::compressOnly(const PixelBuffer* pb, const uint8_t quality
method = conf[qualityIn].method;
} else {
quality = 8;
method = 0;
method = 4;
}
WebPConfigInit(&cfg);
@@ -214,7 +214,7 @@ void TightWEBPEncoder::writeRect(const PixelBuffer* pb, const Palette& palette)
method = conf[qualityLevel].method;
} else {
quality = 8;
method = 0;
method = 4;
}
WebPConfigInit(&cfg);
@@ -265,7 +265,7 @@ rdr::U32 TightWEBPEncoder::benchmark() const
rdr::U8* buffer;
struct timeval start;
int stride, i;
const uint8_t quality = 8, method = 2;
const uint8_t quality = 8, method = 4;
WebPConfig cfg;
WebPPicture pic;
WebPMemoryWriter wrt;

View File

@@ -57,8 +57,7 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
inProcessMessages(false),
pendingSyncFence(false), syncFence(false), fenceFlags(0),
fenceDataLen(0), fenceData(NULL), congestionTimer(this),
losslessTimer(this), kbdLogTimer(this), binclipTimer(this),
server(server_), updates(false),
losslessTimer(this), kbdLogTimer(this), server(server_), updates(false),
updateRenderedCursor(false), removeRenderedCursor(false),
continuousUpdates(false), encodeManager(this, &server_->encCache),
needsPermCheck(false), pointerEventTime(0),
@@ -414,6 +413,18 @@ static void keylog(unsigned keysym, const char *client) {
flushKeylog(client);
}
void VNCSConnectionST::requestClipboardOrClose()
{
try {
if (!(accessRights & AccessCutText)) return;
if (!rfb::Server::acceptCutText) return;
if (state() != RFBSTATE_NORMAL) return;
requestClipboard();
} catch(rdr::Exception& e) {
close(e.str());
}
}
void VNCSConnectionST::announceClipboardOrClose(bool available)
{
try {
@@ -426,51 +437,29 @@ void VNCSConnectionST::announceClipboardOrClose(bool available)
}
}
void VNCSConnectionST::clearBinaryClipboardData()
{
clearBinaryClipboard();
}
void VNCSConnectionST::sendBinaryClipboardDataOrClose(const char* mime,
const unsigned char *data,
const unsigned len)
void VNCSConnectionST::sendClipboardDataOrClose(const char* data)
{
try {
if (!(accessRights & AccessCutText)) return;
if (!rfb::Server::sendCutText) return;
if (rfb::Server::DLP_ClipSendMax && len > (unsigned) rfb::Server::DLP_ClipSendMax) {
vlog.info("DLP: client %s: refused to send binary clipboard, too large",
if (msSince(&lastClipboardOp) < (unsigned) rfb::Server::DLP_ClipDelay) {
vlog.info("DLP: client %s: refused to send clipboard, too soon",
sock->getPeerAddress());
return;
}
cliplog((const char *) data, len, len, "sent", sock->getPeerAddress());
int len = strlen(data);
const int origlen = len;
if (rfb::Server::DLP_ClipSendMax && len > rfb::Server::DLP_ClipSendMax)
len = rfb::Server::DLP_ClipSendMax;
cliplog(data, len, origlen, "sent", sock->getPeerAddress());
if (state() != RFBSTATE_NORMAL) return;
addBinaryClipboard(mime, data, len);
binclipTimer.start(100);
sendClipboardData(data, len);
gettimeofday(&lastClipboardOp, NULL);
} catch(rdr::Exception& e) {
close(e.str());
}
}
void VNCSConnectionST::getBinaryClipboardData(const char* mime, const unsigned char **data,
unsigned *len)
{
unsigned i;
for (i = 0; i < binaryClipboard.size(); i++) {
if (!strcmp(binaryClipboard[i].mime, mime)) {
*data = &binaryClipboard[i].data[0];
*len = binaryClipboard[i].data.size();
return;
}
}
vlog.error("Binary clipboard data for mime %s not found", mime);
*data = (const unsigned char *) "";
*len = 1;
}
void VNCSConnectionST::setDesktopNameOrClose(const char *name)
{
try {
@@ -703,7 +692,7 @@ void VNCSConnectionST::setPixelFormat(const PixelFormat& pf)
setCursor();
}
void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask, const bool skipClick, const bool skipRelease, int scrollX, int scrollY)
void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask, const bool skipClick, const bool skipRelease)
{
pointerEventTime = lastEventTime = time(0);
server->lastUserInputTime = lastEventTime;
@@ -731,7 +720,7 @@ void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask, const bool
}
}
server->desktop->pointerEvent(pointerEventPos, buttonMask, skipclick, skiprelease, scrollX, scrollY);
server->desktop->pointerEvent(pointerEventPos, buttonMask, skipclick, skiprelease);
}
}
@@ -1025,6 +1014,12 @@ void VNCSConnectionST::enableContinuousUpdates(bool enable,
}
}
void VNCSConnectionST::handleClipboardRequest()
{
if (!(accessRights & AccessCutText)) return;
server->handleClipboardRequest(this);
}
void VNCSConnectionST::handleClipboardAnnounce(bool available)
{
if (!(accessRights & AccessCutText)) return;
@@ -1032,13 +1027,25 @@ void VNCSConnectionST::handleClipboardAnnounce(bool available)
server->handleClipboardAnnounce(this, available);
}
void VNCSConnectionST::handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32])
void VNCSConnectionST::handleClipboardData(const char* data, int len)
{
if (!(accessRights & AccessCutText)) return;
if (!rfb::Server::acceptCutText) return;
server->handleClipboardAnnounceBinary(this, num, mimes);
if (msSince(&lastClipboardOp) < (unsigned) rfb::Server::DLP_ClipDelay) {
vlog.info("DLP: client %s: refused to receive clipboard, too soon",
sock->getPeerAddress());
return;
}
const int origlen = len;
if (rfb::Server::DLP_ClipAcceptMax && len > rfb::Server::DLP_ClipAcceptMax)
len = rfb::Server::DLP_ClipAcceptMax;
cliplog(data, len, origlen, "received", sock->getPeerAddress());
gettimeofday(&lastClipboardOp, NULL);
server->handleClipboardData(this, data, len);
}
// supportsLocalCursor() is called whenever the status of
// cp.supportsLocalCursor has changed. If the client does now support local
// cursor, we make sure that the old server-side rendered cursor is cleaned up
@@ -1082,8 +1089,6 @@ bool VNCSConnectionST::handleTimeout(Timer* t)
writeFramebufferUpdate();
else if (t == &kbdLogTimer)
flushKeylog(sock->getPeerAddress());
else if (t == &binclipTimer)
writeBinaryClipboard();
} catch (rdr::Exception& e) {
close(e.str());
}
@@ -1441,18 +1446,6 @@ void VNCSConnectionST::writeDataUpdate()
requested.clear();
}
void VNCSConnectionST::writeBinaryClipboard()
{
if (msSince(&lastClipboardOp) < (unsigned) rfb::Server::DLP_ClipDelay) {
vlog.info("DLP: client %s: refused to send binary clipboard, too soon",
sock->getPeerAddress());
return;
}
writer()->writeBinaryClipboard(binaryClipboard);
gettimeofday(&lastClipboardOp, NULL);
}
void VNCSConnectionST::screenLayoutChange(rdr::U16 reason)
{

View File

@@ -77,12 +77,9 @@ namespace rfb {
void bellOrClose();
void setDesktopNameOrClose(const char *name);
void setLEDStateOrClose(unsigned int state);
void requestClipboardOrClose();
void announceClipboardOrClose(bool available);
void clearBinaryClipboardData();
void sendBinaryClipboardDataOrClose(const char* mime, const unsigned char *data,
const unsigned len);
void getBinaryClipboardData(const char* mime, const unsigned char **data,
unsigned *len);
void sendClipboardDataOrClose(const char* data);
// checkIdleTimeout() returns the number of milliseconds left until the
// idle timeout expires. If it has expired, the connection is closed and
@@ -207,7 +204,7 @@ namespace rfb {
virtual void queryConnection(const char* userName);
virtual void clientInit(bool shared);
virtual void setPixelFormat(const PixelFormat& pf);
virtual void pointerEvent(const Point& pos, int buttonMask, const bool skipClick, const bool skipRelease, int scrollX, int scrollY);
virtual void pointerEvent(const Point& pos, int buttonMask, const bool skipClick, const bool skipRelease);
virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down);
virtual void framebufferUpdateRequest(const Rect& r, bool incremental);
virtual void setDesktopSize(int fb_width, int fb_height,
@@ -215,8 +212,9 @@ namespace rfb {
virtual void fence(rdr::U32 flags, unsigned len, const char data[]);
virtual void enableContinuousUpdates(bool enable,
int x, int y, int w, int h);
virtual void handleClipboardRequest();
virtual void handleClipboardAnnounce(bool available);
virtual void handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]);
virtual void handleClipboardData(const char* data, int len);
virtual void supportsLocalCursor();
virtual void supportsFence();
virtual void supportsContinuousUpdates();
@@ -262,8 +260,6 @@ namespace rfb {
void writeNoDataUpdate();
void writeDataUpdate();
void writeBinaryClipboard();
void screenLayoutChange(rdr::U16 reason);
void setCursor();
void setCursorPos();
@@ -286,7 +282,6 @@ namespace rfb {
Timer congestionTimer;
Timer losslessTimer;
Timer kbdLogTimer;
Timer binclipTimer;
VNCServerST* server;
SimpleUpdateTracker updates;

View File

@@ -52,11 +52,22 @@ namespace rfb {
// getPixelBuffer() returns a pointer to the PixelBuffer object.
virtual PixelBuffer* getPixelBuffer() const = 0;
// requestClipboard() will result in a request to a client to
// transfer its clipboard data. A call to
// SDesktop::handleClipboardData() will be made once the data is
// available.
virtual void requestClipboard() = 0;
// announceClipboard() informs all clients of changes to the
// clipboard on the server. A client may later request the
// clipboard data via SDesktop::handleClipboardRequest().
virtual void announceClipboard(bool available) = 0;
// sendClipboardData() transfers the clipboard data to a client
// and should be called whenever a client has requested the
// clipboard via SDesktop::handleClipboardRequest().
virtual void sendClipboardData(const char* data) = 0;
// bell() tells the server that it should make all clients make a bell sound.
virtual void bell() = 0;

View File

@@ -53,7 +53,6 @@
#include <network/GetAPI.h>
#include <rfb/cpuid.h>
#include <rfb/ComparingUpdateTracker.h>
#include <rfb/KeyRemapper.h>
#include <rfb/ListConnInfo.h>
@@ -77,8 +76,6 @@ static LogWriter slog("VNCServerST");
LogWriter VNCServerST::connectionsLog("Connections");
EncCache VNCServerST::encCache;
void SelfBench();
//
// -=- VNCServerST Implementation
//
@@ -135,9 +132,6 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
{
lastUserInputTime = lastDisconnectTime = time(0);
slog.debug("creating single-threaded server %s", name.buf);
slog.info("CPU capability: SSE2 %s, AVX512f %s",
supportsSSE2() ? "yes" : "no",
supportsAVX512f() ? "yes" : "no");
DLPRegion.enabled = DLPRegion.percents = false;
@@ -218,9 +212,6 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
}
trackingClient[0] = 0;
if (Server::selfBench)
SelfBench();
}
VNCServerST::~VNCServerST()
@@ -518,6 +509,14 @@ void VNCServerST::setScreenLayout(const ScreenSet& layout)
}
}
void VNCServerST::requestClipboard()
{
if (clipboardClient == NULL)
return;
clipboardClient->requestClipboard();
}
void VNCServerST::announceClipboard(bool available)
{
std::list<VNCSConnectionST*>::iterator ci, ci_next;
@@ -533,31 +532,20 @@ void VNCServerST::announceClipboard(bool available)
}
}
void VNCServerST::sendBinaryClipboardData(const char* mime, const unsigned char *data,
const unsigned len)
void VNCServerST::sendClipboardData(const char* data)
{
std::list<VNCSConnectionST*>::iterator ci, ci_next;
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
ci_next = ci; ci_next++;
(*ci)->sendBinaryClipboardDataOrClose(mime, data, len);
}
}
void VNCServerST::getBinaryClipboardData(const char* mime, const unsigned char **data,
unsigned *len)
{
if (!clipboardClient)
return;
clipboardClient->getBinaryClipboardData(mime, data, len);
}
if (strchr(data, '\r') != NULL)
throw Exception("Invalid carriage return in clipboard data");
void VNCServerST::clearBinaryClipboardData()
{
std::list<VNCSConnectionST*>::iterator ci, ci_next;
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
for (ci = clipboardRequestors.begin();
ci != clipboardRequestors.end(); ci = ci_next) {
ci_next = ci; ci_next++;
(*ci)->clearBinaryClipboardData();
(*ci)->sendClipboardDataOrClose(data);
}
clipboardRequestors.clear();
}
void VNCServerST::bell()
@@ -1201,6 +1189,13 @@ bool VNCServerST::getComparerState()
return false;
}
void VNCServerST::handleClipboardRequest(VNCSConnectionST* client)
{
clipboardRequestors.push_back(client);
if (clipboardRequestors.size() == 1)
desktop->handleClipboardRequest();
}
void VNCServerST::handleClipboardAnnounce(VNCSConnectionST* client,
bool available)
{
@@ -1214,10 +1209,11 @@ void VNCServerST::handleClipboardAnnounce(VNCSConnectionST* client,
desktop->handleClipboardAnnounce(available);
}
void VNCServerST::handleClipboardAnnounceBinary(VNCSConnectionST* client,
const unsigned num,
const char mimes[][32])
void VNCServerST::handleClipboardData(VNCSConnectionST* client,
const char* data, int len)
{
clipboardClient = client;
desktop->handleClipboardAnnounceBinary(num, mimes);
if (client != clipboardClient)
return;
desktop->handleClipboardData(data, len);
}

View File

@@ -96,12 +96,9 @@ namespace rfb {
virtual void setPixelBuffer(PixelBuffer* pb);
virtual void setScreenLayout(const ScreenSet& layout);
virtual PixelBuffer* getPixelBuffer() const { if (DLPRegion.enabled && blackedpb) return blackedpb; else return pb; }
virtual void requestClipboard();
virtual void announceClipboard(bool available);
virtual void clearBinaryClipboardData();
virtual void sendBinaryClipboardData(const char* mime, const unsigned char *data,
const unsigned len);
virtual void getBinaryClipboardData(const char *mime, const unsigned char **ptr,
unsigned *len);
virtual void sendClipboardData(const char* data);
virtual void add_changed(const Region &region);
virtual void add_copied(const Region &dest, const Point &delta);
virtual void setCursor(int width, int height, const Point& hotspot,
@@ -194,9 +191,9 @@ namespace rfb {
void setAPIMessager(network::GetAPIMessager *msgr) { apimessager = msgr; }
void handleClipboardRequest(VNCSConnectionST* client);
void handleClipboardAnnounce(VNCSConnectionST* client, bool available);
void handleClipboardAnnounceBinary(VNCSConnectionST* client, const unsigned num,
const char mimes[][32]);
void handleClipboardData(VNCSConnectionST* client, const char* data, int len);
protected:

View File

@@ -1,72 +0,0 @@
/* Copyright (C) 2021 Kasm Web
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
#include <stdint.h>
static uint32_t cpuid[4] = { 0 };
static uint32_t extcpuid[4] = { 0 };
static void getcpuid() {
if (cpuid[0])
return;
#if defined(__x86_64__) || defined(__i386__)
uint32_t eax, ecx = 0;
eax = 1; // normal feature bits
__asm__ __volatile__(
"cpuid\n\t"
: "=a"(cpuid[0]), "=b"(cpuid[1]), "=c"(cpuid[2]), "=d"(cpuid[3])
: "0"(eax), "2"(ecx)
);
eax = 7; // ext feature bits
ecx = 0;
__asm__ __volatile__(
"cpuid\n\t"
: "=a"(extcpuid[0]), "=b"(extcpuid[1]), "=c"(extcpuid[2]), "=d"(extcpuid[3])
: "0"(eax), "2"(ecx)
);
#endif
}
namespace rfb {
bool supportsSSE2() {
getcpuid();
#if defined(__aarch64__)
return true;
#elif defined(__x86_64__) || defined(__i386__)
#define bit_SSE2 (1 << 26)
return cpuid[3] & bit_SSE2;
#endif
return false;
}
bool supportsAVX512f() {
getcpuid();
#if defined(__x86_64__) || defined(__i386__)
#define bit_AVX512f (1 << 16)
return extcpuid[1] & bit_AVX512f;
#endif
return false;
}
}; // namespace rfb

View File

@@ -1,28 +0,0 @@
/* Copyright (C) 2021 Kasm Web
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
#ifndef __RFB_CPUID_H__
#define __RFB_CPUID_H__
namespace rfb {
bool supportsSSE2();
bool supportsAVX512f();
};
#endif

View File

@@ -31,7 +31,6 @@ namespace rfb {
// kasm
const int msgTypeStats = 178;
const int msgTypeRequestFrameStats = 179;
const int msgTypeBinaryClipboard = 180;
const int msgTypeServerFence = 248;
@@ -50,7 +49,6 @@ namespace rfb {
// kasm
const int msgTypeRequestStats = 178;
const int msgTypeFrameStats = 179;
//const int msgTypeBinaryClipboard = 180;
const int msgTypeClientFence = 248;

View File

@@ -1,37 +0,0 @@
/* Copyright (C) 2021 Kasm Web
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
#include <rfb/scale_sse2.h>
namespace rfb {
void SSE2_halve(const uint8_t *oldpx,
const uint16_t tgtw, const uint16_t tgth,
uint8_t *newpx,
const unsigned oldstride, const unsigned newstride) {
}
// Handles factors between 0.5 and 1.0
void SSE2_scale(const uint8_t *oldpx,
const uint16_t tgtw, const uint16_t tgth,
uint8_t *newpx,
const unsigned oldstride, const unsigned newstride,
const float tgtdiff) {
}
}; // namespace rfb

View File

@@ -1,261 +0,0 @@
/* Copyright (C) 2021 Kasm Web
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
#ifdef __aarch64__
#include "sse2neon.h"
#else
#include <emmintrin.h>
#endif
#include <rfb/scale_sse2.h>
namespace rfb {
/*
static void print128(const char msg[], const __m128i v) {
union {
__m128i v;
uint8_t c[16];
} u;
u.v = v;
printf("%s %02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x\n",
msg,
u.c[0],
u.c[1],
u.c[2],
u.c[3],
u.c[4],
u.c[5],
u.c[6],
u.c[7],
u.c[8],
u.c[9],
u.c[10],
u.c[11],
u.c[12],
u.c[13],
u.c[14],
u.c[15]);
}
*/
void SSE2_halve(const uint8_t *oldpx,
const uint16_t tgtw, const uint16_t tgth,
uint8_t *newpx,
const unsigned oldstride, const unsigned newstride) {
uint16_t x, y;
const uint16_t srcw = tgtw * 2, srch = tgth * 2;
const __m128i zero = _mm_setzero_si128();
const __m128i shift = _mm_set_epi32(0, 0, 0, 2);
const __m128i low = _mm_set_epi32(0, 0, 0xffffffff, 0xffffffff);
const __m128i high = _mm_set_epi32(0xffffffff, 0xffffffff, 0, 0);
for (y = 0; y < srch; y += 2) {
const uint8_t * const row0 = oldpx + oldstride * y * 4;
const uint8_t * const row1 = oldpx + oldstride * (y + 1) * 4;
uint8_t * const dst = newpx + newstride * (y / 2) * 4;
for (x = 0; x < srcw - 3; x += 4) {
__m128i lo, hi, a, b, c, d;
lo = _mm_loadu_si128((__m128i *) &row0[x * 4]);
hi = _mm_loadu_si128((__m128i *) &row1[x * 4]);
a = _mm_unpacklo_epi8(lo, zero);
b = _mm_unpackhi_epi8(lo, zero);
c = _mm_unpacklo_epi8(hi, zero);
d = _mm_unpackhi_epi8(hi, zero);
a = _mm_add_epi16(a, c);
b = _mm_add_epi16(b, d);
c = _mm_srli_si128(a, 8);
a = _mm_and_si128(a, low);
a = _mm_add_epi16(a, c);
d = _mm_slli_si128(b, 8);
b = _mm_and_si128(b, high);
b = _mm_add_epi16(b, d);
a = _mm_add_epi16(a, b);
a = _mm_srl_epi16(a, shift);
a = _mm_packus_epi16(a, zero);
_mm_storel_epi64((__m128i *) &dst[(x / 2) * 4], a);
}
for (; x < srcw; x += 2) {
// Remainder in C
uint8_t i;
for (i = 0; i < 4; i++) {
dst[(x / 2) * 4 + i] =
(row0[x * 4 + i] +
row0[(x + 1) * 4 + i] +
row1[x * 4 + i] +
row1[(x + 1) * 4 + i]) / 4;
}
}
}
}
// Handles factors between 0.5 and 1.0
void SSE2_scale(const uint8_t *oldpx,
const uint16_t tgtw, const uint16_t tgth,
uint8_t *newpx,
const unsigned oldstride, const unsigned newstride,
const float tgtdiff) {
uint16_t x, y;
const __m128i zero = _mm_setzero_si128();
const __m128i low = _mm_set_epi32(0, 0, 0xffffffff, 0xffffffff);
const __m128i high = _mm_set_epi32(0xffffffff, 0xffffffff, 0, 0);
const float invdiff = 1 / tgtdiff;
for (y = 0; y < tgth; y++) {
const float ny = y * invdiff;
const uint16_t lowy = ny;
const uint16_t highy = lowy + 1;
const uint16_t bot = (ny - lowy) * 256;
const uint16_t top = 256 - bot;
const uint32_t * const row0 = (uint32_t *) (oldpx + oldstride * lowy * 4);
const uint32_t * const row1 = (uint32_t *) (oldpx + oldstride * highy * 4);
const uint8_t * const brow0 = (uint8_t *) row0;
const uint8_t * const brow1 = (uint8_t *) row1;
uint8_t * const dst = newpx + newstride * y * 4;
const __m128i vertmul = _mm_set1_epi16(top);
const __m128i vertmul2 = _mm_set1_epi16(bot);
for (x = 0; x < tgtw - 1; x += 2) {
const float nx[2] = {
x * invdiff,
(x + 1) * invdiff,
};
const uint16_t lowx[2] = {
(uint16_t) nx[0],
(uint16_t) nx[1],
};
const uint16_t highx[2] = {
(uint16_t) (lowx[0] + 1),
(uint16_t) (lowx[1] + 1),
};
const uint16_t right[2] = {
(uint16_t) ((nx[0] - lowx[0]) * 256),
(uint16_t) ((nx[1] - lowx[1]) * 256),
};
const uint16_t left[2] = {
(uint16_t) (256 - right[0]),
(uint16_t) (256 - right[1]),
};
const __m128i horzmul = _mm_set_epi16(
right[0],
right[0],
right[0],
right[0],
left[0],
left[0],
left[0],
left[0]
);
const __m128i horzmul2 = _mm_set_epi16(
right[1],
right[1],
right[1],
right[1],
left[1],
left[1],
left[1],
left[1]
);
__m128i lo, hi, a, b, c, d;
lo = _mm_setr_epi32(row0[lowx[0]],
row0[highx[0]],
row0[lowx[1]],
row0[highx[1]]);
hi = _mm_setr_epi32(row1[lowx[0]],
row1[highx[0]],
row1[lowx[1]],
row1[highx[1]]);
a = _mm_unpacklo_epi8(lo, zero);
b = _mm_unpackhi_epi8(lo, zero);
c = _mm_unpacklo_epi8(hi, zero);
d = _mm_unpackhi_epi8(hi, zero);
a = _mm_mullo_epi16(a, vertmul);
b = _mm_mullo_epi16(b, vertmul);
c = _mm_mullo_epi16(c, vertmul2);
d = _mm_mullo_epi16(d, vertmul2);
a = _mm_add_epi16(a, c);
a = _mm_srli_epi16(a, 8);
b = _mm_add_epi16(b, d);
b = _mm_srli_epi16(b, 8);
a = _mm_mullo_epi16(a, horzmul);
b = _mm_mullo_epi16(b, horzmul2);
lo = _mm_srli_si128(a, 8);
a = _mm_and_si128(a, low);
a = _mm_add_epi16(a, lo);
hi = _mm_slli_si128(b, 8);
b = _mm_and_si128(b, high);
b = _mm_add_epi16(b, hi);
a = _mm_add_epi16(a, b);
a = _mm_srli_epi16(a, 8);
a = _mm_packus_epi16(a, zero);
_mm_storel_epi64((__m128i *) &dst[x * 4], a);
}
for (; x < tgtw; x++) {
// Remainder in C
const float nx = x * invdiff;
const uint16_t lowx = nx;
const uint16_t highx = lowx + 1;
const uint16_t right = (nx - lowx) * 256;
const uint16_t left = 256 - right;
uint8_t i;
uint32_t val, val2;
for (i = 0; i < 4; i++) {
val = brow0[lowx * 4 + i] * left;
val += brow0[highx * 4 + i] * right;
val >>= 8;
val2 = brow1[lowx * 4 + i] * left;
val2 += brow1[highx * 4 + i] * right;
val2 >>= 8;
dst[x * 4 + i] =
(val * top + val2 * bot) >> 8;
}
}
}
}
}; // namespace rfb

View File

@@ -1,38 +0,0 @@
/* Copyright (C) 2021 Kasm Web
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
#ifndef __RFB_SCALE_SSE2_H__
#define __RFB_SCALE_SSE2_H__
#include <stdint.h>
namespace rfb {
void SSE2_halve(const uint8_t *oldpx,
const uint16_t tgtw, const uint16_t tgth,
uint8_t *newpx,
const unsigned oldstride, const unsigned newstride);
void SSE2_scale(const uint8_t *oldpx,
const uint16_t tgtw, const uint16_t tgth,
uint8_t *newpx,
const unsigned oldstride, const unsigned newstride,
const float tgtdiff);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
TARGET_OS := $(shell lsb_release -is | tr '[:upper:]' '[:lower:]')
TARGET_OS_CODENAME := $(shell lsb_release -cs | tr '[:upper:]' '[:lower:]')
TARBALL_DIR := builder/build
TARBALL := $(TARBALL_DIR)/kasmvnc.$(TARGET_OS)_$(TARGET_OS_CODENAME)$(BUILD_TAG).tar.gz
TARBALL := $(TARBALL_DIR)/kasmvnc.$(TARGET_OS)_$(TARGET_OS_CODENAME).tar.gz
TAR_DATA := $(shell mktemp -d)
SRC := $(TAR_DATA)/usr/local
SRC_BIN := $(SRC)/bin
@@ -16,6 +16,7 @@ install: unpack_tarball
cp $(SRC_BIN)/vncserver $(DESTDIR)/usr/bin/kasmvncserver
cp $(SRC_BIN)/vncconfig $(DESTDIR)/usr/bin/kasmvncconfig
cp $(SRC_BIN)/kasmvncpasswd $(DESTDIR)/usr/bin/
cp $(SRC_BIN)/kasmxproxy $(DESTDIR)/usr/bin/
cp -r $(SRC)/share/doc/kasmvnc*/* $(DESTDIR)/usr/share/doc/kasmvncserver/
rsync -r --exclude '.git*' --exclude po2js --exclude xgettext-html \
--exclude www/utils/ --exclude .eslintrc \

4
debian/control vendored
View File

@@ -3,14 +3,14 @@ Section: x11
Priority: optional
Maintainer: Kasm Technologies LLC <info@kasmweb.com>
Build-Depends: debhelper (>= 11), rsync, libjpeg-dev, libjpeg-dev, libpng-dev,
libtiff-dev, libgif-dev, libavcodec-dev, libssl-dev, libgl1, libxfont2, libsm6, libunwind8
libtiff-dev, libgif-dev, libavcodec-dev, libssl-dev, libgl1, libxfont2, libsm6, libxext-dev, libxrandr-dev, libxtst-dev, libxcursor-dev
Standards-Version: 4.1.3
Homepage: https://github.com/kasmtech/KasmVNC
#Vcs-Browser: https://salsa.debian.org/debian/kasmvnc
#Vcs-Git: https://salsa.debian.org/debian/kasmvnc.git
Package: kasmvncserver
Architecture: amd64 arm64
Architecture: amd64
Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, ssl-cert, xauth,
x11-xkb-utils, xkb-data, procps
Provides: vnc-server

2
debian/postinst vendored
View File

@@ -21,7 +21,7 @@ case "$1" in
configure)
bindir=/usr/bin
mandir=/usr/share/man
commands="kasmvncserver kasmvncpasswd kasmvncconfig Xkasmvnc"
commands="kasmvncserver kasmvncpasswd kasmvncconfig Xkasmvnc kasmxproxy"
for kasm_command in $commands; do
generic_command=`echo "$kasm_command" | sed -e 's/kasm//'`;

2
debian/prerm vendored
View File

@@ -21,7 +21,7 @@ case "$1" in
remove)
bindir=/usr/bin
mandir=/usr/share/man
commands="kasmvncserver kasmvncpasswd kasmvncconfig Xkasmvnc"
commands="kasmvncserver kasmvncpasswd kasmvncconfig Xkasmvnc kasmxproxy"
for kasm_command in $commands; do
generic_command=`echo "$kasm_command" | sed -e 's/kasm//'`;

Submodule kasmweb updated: b5a1586c0a...ba40cacce0

View File

@@ -45,6 +45,7 @@ mkdir -p $OUTDIR/man/man1
make DESTDIR=$TMPDIR/inst install
if [ $SERVER = 1 ]; then
install -m 755 ./unix/kasmxproxy/kasmxproxy $OUTDIR/bin/
install -m 755 ./xorg.build/bin/Xvnc $OUTDIR/bin/
install -m 644 ./xorg.build/man/man1/Xvnc.1 $OUTDIR/man/man1/Xvnc.1
install -m 644 ./xorg.build/man/man1/Xserver.1 $OUTDIR/man/man1/Xserver.1

View File

@@ -3,6 +3,7 @@ add_subdirectory(common)
add_subdirectory(vncconfig)
add_subdirectory(vncpasswd)
add_subdirectory(kasmvncpasswd)
add_subdirectory(kasmxproxy)
install(PROGRAMS vncserver DESTINATION ${BIN_DIR})
install(FILES vncserver.man DESTINATION ${MAN_DIR}/man1 RENAME vncserver.1)

1
unix/kasmxproxy/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
kasmxproxy

View File

@@ -0,0 +1,11 @@
include_directories(${X11_INCLUDE_DIR})
add_executable(kasmxproxy
xxhash.c
kasmxproxy.c)
target_link_libraries(kasmxproxy ${X11_LIBRARIES} ${X11_XTest_LIB} ${X11_Xrandr_LIB}
${X11_Xcursor_LIB} ${X11_Xfixes_LIB})
install(TARGETS kasmxproxy DESTINATION ${BIN_DIR})
install(FILES kasmxproxy.man DESTINATION ${MAN_DIR}/man1 RENAME kasmxproxy.1)

View File

@@ -0,0 +1,530 @@
/* Copyright (C) 2021 Kasm. All Rights Reserved.
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
#include <getopt.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xcursor/Xcursor.h>
#include <X11/extensions/Xfixes.h>
#include <X11/extensions/Xrandr.h>
#include <X11/extensions/XShm.h>
#include <X11/extensions/XTest.h>
#include "xxhash.h"
#define min(a, b) ((a) < (b) ? (a) : (b))
static void help(const char name[]) {
printf("Usage: %s [opts]\n\n"
"-a --app-display disp App display, default :0\n"
"-v --vnc-display disp VNC display, default :1\n"
"\n"
"-f --fps fps FPS, default 30\n"
"-r --resize Enable resize, default disabled.\n"
" Do not enable this if there's a physical screen\n"
" connected to the app display.\n",
name);
exit(1);
}
#define CUT_MAX (16 * 1024)
static uint8_t cutbuf[CUT_MAX];
static void supplyselection(Display *disp, const XEvent * const ev, const Atom xa_targets) {
XSelectionEvent sev;
sev.type = SelectionNotify;
sev.display = disp;
sev.requestor = ev->xselectionrequest.requestor;
sev.selection = ev->xselectionrequest.selection;
sev.target = ev->xselectionrequest.target;
sev.time = ev->xselectionrequest.time;
/*printf("someone wants our clipboard, sel %lu, tgt %lu, prop %lu\n",
sev.selection, sev.target,
ev.xselectionrequest.property);*/
if (ev->xselectionrequest.property == None)
sev.property = sev.target;
else
sev.property = ev->xselectionrequest.property;
const uint32_t len = strlen((char *) cutbuf);
if (xa_targets != None &&
sev.target == xa_targets) {
// Which formats can we do
Atom tgt[2] = {
xa_targets,
XA_STRING
};
XChangeProperty(disp, sev.requestor,
ev->xselectionrequest.property,
XA_ATOM, 32,
PropModeReplace,
(unsigned char *) tgt,
2);
//puts("sent targets");
} else {
// Data
XChangeProperty(disp, sev.requestor,
ev->xselectionrequest.property,
sev.target, 8,
PropModeReplace,
cutbuf, len);
//printf("sent data, of len %u\n", len);
}
// Send the notify event
XSendEvent(disp, sev.requestor, False, 0,
(XEvent *) &sev);
}
int main(int argc, char **argv) {
const char *appstr = ":0";
const char *vncstr = ":1";
uint8_t resize = 0;
uint8_t fps = 30;
const struct option longargs[] = {
{"app-display", 1, NULL, 'a'},
{"vnc-display", 1, NULL, 'v'},
{"resize", 0, NULL, 'r'},
{"fps", 1, NULL, 'f'},
{NULL, 0, NULL, 0},
};
while (1) {
int c = getopt_long(argc, argv, "a:v:rf:", longargs, NULL);
if (c == -1)
break;
switch (c) {
case 'a':
appstr = strdup(optarg);
break;
case 'v':
vncstr = strdup(optarg);
break;
case 'r':
resize = 1;
break;
case 'f':
fps = atoi(optarg);
if (fps < 1 || fps > 120) {
printf("Invalid fps\n");
return 1;
}
break;
default:
help(argv[0]);
break;
}
}
Display *appdisp = XOpenDisplay(appstr);
if (!appdisp) {
printf("Cannot open display %s\n", appstr);
return 1;
}
if (!XShmQueryExtension(appdisp)) {
printf("Display %s lacks SHM extension\n", appstr);
return 1;
}
Display *vncdisp = XOpenDisplay(vncstr);
if (!vncdisp) {
printf("Cannot open display %s\n", vncstr);
return 1;
}
if (!XShmQueryExtension(vncdisp)) {
printf("Display %s lacks SHM extension\n", vncstr);
return 1;
}
const int appscreen = DefaultScreen(appdisp);
const int vncscreen = DefaultScreen(vncdisp);
Visual *appvis = DefaultVisual(appdisp, appscreen);
//Visual *vncvis = DefaultVisual(vncdisp, vncscreen);
const int appdepth = DefaultDepth(appdisp, appscreen);
const int vncdepth = DefaultDepth(vncdisp, vncscreen);
if (appdepth != vncdepth) {
printf("Depths don't match, app %u vnc %u\n", appdepth, vncdepth);
return 1;
}
Window approot = DefaultRootWindow(appdisp);
Window vncroot = DefaultRootWindow(vncdisp);
XWindowAttributes appattr, vncattr;
XGCValues gcval;
gcval.plane_mask = AllPlanes;
gcval.function = GXcopy;
GC gc = XCreateGC(vncdisp, vncroot, GCFunction | GCPlaneMask, &gcval);
XImage *img = NULL;
XShmSegmentInfo shminfo;
unsigned imgw = 0, imgh = 0;
if (XGrabPointer(vncdisp, vncroot, False,
ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
GrabModeAsync, GrabModeAsync, None, None,
CurrentTime) != Success)
return 1;
if (XGrabKeyboard(vncdisp, vncroot, False, GrabModeAsync, GrabModeAsync,
CurrentTime) != Success)
return 1;
int xfixesbase, xfixeserrbase;
XFixesQueryExtension(appdisp, &xfixesbase, &xfixeserrbase);
XFixesSelectSelectionInput(appdisp, approot, XA_PRIMARY,
XFixesSetSelectionOwnerNotifyMask);
int xfixesbasevnc, xfixeserrbasevnc;
XFixesQueryExtension(vncdisp, &xfixesbasevnc, &xfixeserrbasevnc);
XFixesSelectSelectionInput(vncdisp, vncroot, XA_PRIMARY,
XFixesSetSelectionOwnerNotifyMask);
Atom xa_targets_vnc = XInternAtom(vncdisp, "TARGETS", False);
Atom xa_targets_app = XInternAtom(appdisp, "TARGETS", False);
Window selwin = XCreateSimpleWindow(appdisp, approot, 3, 2, 1, 1, 0, 0, 0);
Window vncselwin = XCreateSimpleWindow(vncdisp, vncroot, 3, 2, 1, 1, 0, 0, 0);
XFixesCursorImage *cursor = NULL;
uint64_t cursorhash = 0;
Cursor xcursor = None;
const unsigned sleeptime = 1000 * 1000 / fps;
while (1) {
if (!XGetWindowAttributes(appdisp, approot, &appattr))
break;
if (!XGetWindowAttributes(vncdisp, vncroot, &vncattr))
break;
if (resize && (appattr.width != vncattr.width ||
appattr.height != vncattr.height)) {
// resize app display to VNC display size
XRRScreenConfiguration *config = XRRGetScreenInfo(appdisp, approot);
int nsizes, i, match = -1;
XRRScreenSize *sizes = XRRConfigSizes(config, &nsizes);
//printf("%u sizes\n", nsizes);
for (i = 0; i < nsizes; i++) {
if (sizes[i].width == vncattr.width &&
sizes[i].height == vncattr.height) {
//printf("match %u\n", i);
match = i;
break;
}
}
if (match >= 0) {
XRRSetScreenConfig(appdisp, config, approot, match,
RR_Rotate_0, CurrentTime);
} else {
/*XRRSetScreenSize(appdisp, approot,
vncattr.width, vncattr.height,
sizes[0].mwidth, sizes[0].mheight);*/
XRRScreenResources *res = XRRGetScreenResources(appdisp, approot);
//printf("%u outputs, %u crtcs\n", res->noutput, res->ncrtc);
// Nvidia crap uses a *different* list for 1.0 and 1.2!
unsigned oldmode = 0xffff;
//printf("1.2 modes %u\n", res->nmode);
for (i = 0; i < res->nmode; i++) {
if (res->modes[i].width == vncattr.width &&
res->modes[i].height == vncattr.height) {
oldmode = i;
//printf("old mode %u matched\n", i);
break;
}
}
unsigned tgt = 0;
if (res->noutput > 1) {
for (i = 0; i < res->noutput; i++) {
XRROutputInfo *info = XRRGetOutputInfo(appdisp, res, res->outputs[i]);
if (info->connection == RR_Connected)
tgt = i;
//printf("%u %s %u\n", i, info->name, info->connection);
XRRFreeOutputInfo(info);
}
}
if (oldmode < 0xffff) {
Status s;
// nvidia needs this weird dance
s = XRRSetCrtcConfig(appdisp, res, res->crtcs[tgt],
CurrentTime,
0, 0,
None, RR_Rotate_0,
NULL, 0);
//printf("disable %u\n", s);
XRRSetScreenSize(appdisp, approot,
vncattr.width, vncattr.height,
sizes[0].mwidth, sizes[0].mheight);
s = XRRSetCrtcConfig(appdisp, res, res->crtcs[tgt],
CurrentTime,
0, 0,
res->modes[oldmode].id, RR_Rotate_0,
&res->outputs[tgt], 1);
//printf("set %u\n", s);
} else {
char name[32];
sprintf(name, "%ux%u_60", vncattr.width, vncattr.height);
printf("Creating new Mode %s\n", name);
XRRModeInfo *mode = XRRAllocModeInfo(name, strlen(name));
mode->width = vncattr.width;
mode->height = vncattr.height;
RRMode rmode = XRRCreateMode(appdisp, approot, mode);
XRRAddOutputMode(appdisp,
res->outputs[tgt],
rmode);
XRRFreeModeInfo(mode);
}
XRRFreeScreenResources(res);
}
XRRFreeScreenConfigInfo(config);
}
const unsigned w = min(appattr.width, vncattr.width);
const unsigned h = min(appattr.height, vncattr.height);
if (w != imgw || h != imgh) {
if (img) {
XShmDetach(appdisp, &shminfo);
XDestroyImage(img);
shmdt(shminfo.shmaddr);
shmctl(shminfo.shmid, IPC_RMID, NULL);
}
img = XShmCreateImage(appdisp, appvis, appdepth, ZPixmap,
NULL, &shminfo, w, h);
if (!img)
break;
shminfo.shmid = shmget(IPC_PRIVATE,
img->bytes_per_line * img->height,
IPC_CREAT | 0666);
if (shminfo.shmid == -1)
break;
shminfo.shmaddr = img->data = shmat(shminfo.shmid, 0, 0);
shminfo.readOnly = False;
if (!XShmAttach(appdisp, &shminfo))
break;
imgw = w;
imgh = h;
}
XShmGetImage(appdisp, approot, img, 0, 0, 0xffffffff);
XPutImage(vncdisp, vncroot, gc, img, 0, 0, 0, 0, w, h);
// Handle events
while (XPending(vncdisp)) {
XEvent ev;
XNextEvent(vncdisp, &ev);
if (ev.type == xfixesbasevnc + XFixesSelectionNotify) {
XFixesSelectionNotifyEvent *xfe =
(XFixesSelectionNotifyEvent *) &ev;
// printf("vnc disp did a copy, owner %lu, root %lu\n",
// xfe->owner, vncroot);
if (xfe->owner == None || xfe->owner == vncroot)
continue;
XConvertSelection(vncdisp, XA_PRIMARY, XA_STRING, XA_STRING,
vncselwin, CurrentTime);
} else switch (ev.type) {
case KeyPress:
case KeyRelease:
XTestFakeKeyEvent(appdisp, ev.xkey.keycode,
ev.type == KeyPress,
CurrentTime);
break;
case ButtonPress:
case ButtonRelease:
XTestFakeButtonEvent(appdisp, ev.xbutton.button,
ev.type == ButtonPress,
CurrentTime);
break;
case MotionNotify:
XTestFakeMotionEvent(appdisp, appscreen,
ev.xmotion.x,
ev.xmotion.y,
CurrentTime);
break;
case SelectionRequest:
supplyselection(vncdisp, &ev, xa_targets_vnc);
break;
case SelectionNotify:
{
Atom realtype;
int fmt;
unsigned long nitems, bytes_rem;
unsigned char *data;
if (XGetWindowProperty(vncdisp, vncselwin,
XA_STRING,
0, CUT_MAX / 4,
False, AnyPropertyType,
&realtype, &fmt,
&nitems, &bytes_rem,
&data) == Success) {
if (bytes_rem) {
printf("Clipboard too large, ignoring\n");
} else {
const uint32_t len = nitems * (fmt / 8);
//printf("realtype %lu, fmt %u, nitems %lu\n",
// realtype, fmt, nitems);
memcpy(cutbuf, data, len);
if (len < CUT_MAX)
cutbuf[len] = 0;
else
cutbuf[CUT_MAX - 1] = 0;
// Send it to the app screen
XSetSelectionOwner(appdisp, XA_PRIMARY,
approot,
CurrentTime);
}
XFree(data);
} else {
printf("Failed to fetch vnc clipboard\n");
}
}
break;
case SelectionClear:
cutbuf[0] = '\0';
XSetSelectionOwner(appdisp, XA_PRIMARY, None,
CurrentTime);
break;
default:
printf("Unexpected event type %u\n", ev.type);
break;
}
}
// App-side events
while (XPending(appdisp)) {
XEvent ev;
XNextEvent(appdisp, &ev);
if (ev.type == xfixesbase + XFixesSelectionNotify) {
XFixesSelectionNotifyEvent *xfe =
(XFixesSelectionNotifyEvent *) &ev;
//printf("app disp did a copy, owner %lu\n", xfe->owner);
if (xfe->owner == None || xfe->owner == approot)
continue;
XConvertSelection(appdisp, XA_PRIMARY, XA_STRING, XA_STRING,
selwin, CurrentTime);
} else switch (ev.type) {
case SelectionNotify:
{
Atom realtype;
int fmt;
unsigned long nitems, bytes_rem;
unsigned char *data;
if (XGetWindowProperty(appdisp, selwin,
XA_STRING,
0, CUT_MAX / 4,
False, AnyPropertyType,
&realtype, &fmt,
&nitems, &bytes_rem,
&data) == Success) {
if (bytes_rem) {
printf("Clipboard too large, ignoring\n");
} else {
const uint32_t len = nitems * (fmt / 8);
//printf("realtype %lu, fmt %u, nitems %lu\n",
// realtype, fmt, nitems);
memcpy(cutbuf, data, len);
if (len < CUT_MAX)
cutbuf[len] = 0;
else
cutbuf[CUT_MAX - 1] = 0;
// Send it to the VNC screen
XSetSelectionOwner(vncdisp, XA_PRIMARY,
vncroot,
CurrentTime);
}
XFree(data);
} else {
printf("Failed to fetch app clipboard\n");
}
}
break;
case SelectionRequest:
supplyselection(appdisp, &ev, xa_targets_app);
break;
default:
printf("Unexpected app event type %u\n", ev.type);
break;
}
}
// Cursors
cursor = XFixesGetCursorImage(appdisp);
uint64_t newhash = XXH64(cursor->pixels,
cursor->width * cursor->height * sizeof(unsigned long),
0);
if (cursorhash != newhash) {
if (cursorhash)
XFreeCursor(vncdisp, xcursor);
XcursorImage *converted = XcursorImageCreate(cursor->width, cursor->height);
converted->xhot = cursor->xhot;
converted->yhot = cursor->yhot;
unsigned i;
for (i = 0; i < cursor->width * cursor->height; i++) {
converted->pixels[i] = cursor->pixels[i];
}
xcursor = XcursorImageLoadCursor(vncdisp, converted);
XDefineCursor(vncdisp, vncroot, xcursor);
XcursorImageDestroy(converted);
cursorhash = newhash;
}
usleep(sleeptime);
}
XCloseDisplay(appdisp);
XCloseDisplay(vncdisp);
return 0;
}

View File

@@ -0,0 +1,18 @@
.TH kasmxproxy 1 "" "KasmVNC" "Virtual Network Computing"
.SH NAME
kasmxproxy \- proxy an existing X11 display to a KasmVNC display
.SH SYNOPSIS
.B kasmxproxy
.RB [ \-a \-\-app\-display
.IR source display, default :0 ]
.RB [ \-v \-\-vnc\-display
.IR destination display, default :1 ]
.RB [ \-f \-\-fps
.IR FPS, default 30 ]
.RB [ \-r \-\-resize
.IR Enable resizing, default disabled. WARNING: DO NOT ENABLE IF PHYSICAL DISPLAY IS ATTACHED ]
.br
.BI "kasmxproxy -a :1 -v :10 -r"
.SH DESCRIPTION
.B kasmxproxy
is used to proxy an x display, usually attached to a physical GPU, to KasmVNC display. This is usually used in the context of providing GPU acceleration to a KasmVNC session.

1
unix/kasmxproxy/xxhash.c Symbolic link
View File

@@ -0,0 +1 @@
../../common/rfb/xxhash.c

1
unix/kasmxproxy/xxhash.h Symbolic link
View File

@@ -0,0 +1 @@
../../common/rfb/xxhash.h

View File

@@ -234,14 +234,6 @@ void vncPointerMove(int x, int y)
cursorPosY = y;
}
void vncScroll(int x, int y) {
ValuatorMask mask;
valuator_mask_zero(&mask);
valuator_mask_set(&mask, 2, x);
valuator_mask_set(&mask, 3, y);
QueuePointerEvents(vncPointerDev, MotionNotify, 0, POINTER_RELATIVE, &mask);
}
void vncGetPointerPos(int *x, int *y)
{
if (vncPointerDev != NULL) {
@@ -269,7 +261,7 @@ static int vncPointerProc(DeviceIntPtr pDevice, int onoff)
* is not a bug.
*/
Atom btn_labels[BUTTONS];
Atom axes_labels[4];
Atom axes_labels[2];
switch (onoff) {
case DEVICE_INIT:
@@ -286,29 +278,11 @@ static int vncPointerProc(DeviceIntPtr pDevice, int onoff)
axes_labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_X);
axes_labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_Y);
axes_labels[2] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_HSCROLL);
axes_labels[3] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_VSCROLL);
InitPointerDeviceStruct(pDev, map, BUTTONS, btn_labels,
(PtrCtrlProcPtr)NoopDDA,
GetMotionHistorySize(),
4, axes_labels);
InitValuatorAxisStruct(pDevice, 2, axes_labels[2], NO_AXIS_LIMITS, NO_AXIS_LIMITS, 0, 0, 0, Relative);
InitValuatorAxisStruct(pDevice, 3, axes_labels[3], NO_AXIS_LIMITS, NO_AXIS_LIMITS, 0, 0, 0, Relative);
char* envScrollFactorH = getenv("SCROLL_FACTOR_H");
char* envScrollFactorV = getenv("SCROLL_FACTOR_V");
float scrollFactorH = envScrollFactorH ? atof(envScrollFactorH) : 50.0;
float scrollFactorV = envScrollFactorV ? atof(envScrollFactorV) : 50.0;
LOG_INFO("Mouse horizonatl scroll factor: %f", scrollFactorH);
LOG_INFO("Mouse vertical scroll factor: %f", scrollFactorV);
SetScrollValuator(pDevice, 2, SCROLL_TYPE_HORIZONTAL, scrollFactorH, SCROLL_FLAG_NONE);
SetScrollValuator(pDevice, 3, SCROLL_TYPE_VERTICAL, scrollFactorV, SCROLL_FLAG_PREFERRED);
2, axes_labels);
break;
case DEVICE_ON:
pDev->on = TRUE;

View File

@@ -35,7 +35,6 @@ void vncInitInputDevice(void);
void vncPointerButtonAction(int buttonMask, const unsigned char skipclick,
const unsigned char skiprelease);
void vncPointerMove(int x, int y);
void vncScroll(int x, int y);
void vncGetPointerPos(int *x, int *y);
void vncKeyboardEvent(KeySym keysym, unsigned xtcode, int down);

View File

@@ -176,6 +176,15 @@ XserverDesktop::queryConnection(network::Socket* sock,
return rfb::VNCServerST::PENDING;
}
void XserverDesktop::requestClipboard()
{
try {
server->requestClipboard();
} catch (rdr::Exception& e) {
vlog.error("XserverDesktop::requestClipboard: %s",e.str());
}
}
void XserverDesktop::announceClipboard(bool available)
{
try {
@@ -185,34 +194,12 @@ void XserverDesktop::announceClipboard(bool available)
}
}
void XserverDesktop::clearBinaryClipboardData()
void XserverDesktop::sendClipboardData(const char* data)
{
try {
server->clearBinaryClipboardData();
server->sendClipboardData(data);
} catch (rdr::Exception& e) {
vlog.error("XserverDesktop::clearBinaryClipboardData: %s",e.str());
}
}
void XserverDesktop::sendBinaryClipboardData(const char* mime,
const unsigned char *data,
const unsigned len)
{
try {
server->sendBinaryClipboardData(mime, data, len);
} catch (rdr::Exception& e) {
vlog.error("XserverDesktop::sendBinaryClipboardData: %s",e.str());
}
}
void XserverDesktop::getBinaryClipboardData(const char* mime,
const unsigned char **data,
unsigned *len)
{
try {
server->getBinaryClipboardData(mime, data, len);
} catch (rdr::Exception& e) {
vlog.error("XserverDesktop::getBinaryClipboardData: %s",e.str());
vlog.error("XserverDesktop::sendClipboardData: %s",e.str());
}
}
@@ -459,14 +446,11 @@ void XserverDesktop::approveConnection(uint32_t opaqueId, bool accept,
void XserverDesktop::pointerEvent(const Point& pos, int buttonMask,
const bool skipClick, const bool skipRelease, int scrollX, int scrollY)
const bool skipClick, const bool skipRelease)
{
if (scrollX == 0 && scrollY == 0) {
vncPointerMove(pos.x + vncGetScreenX(screenIndex), pos.y + vncGetScreenY(screenIndex));
vncPointerButtonAction(buttonMask, skipClick, skipRelease);
} else {
vncScroll(scrollX, scrollY);
}
vncPointerMove(pos.x + vncGetScreenX(screenIndex),
pos.y + vncGetScreenY(screenIndex));
vncPointerButtonAction(buttonMask, skipClick, skipRelease);
}
unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height,
@@ -485,14 +469,19 @@ unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height,
return ret;
}
void XserverDesktop::handleClipboardRequest()
{
vncHandleClipboardRequest();
}
void XserverDesktop::handleClipboardAnnounce(bool available)
{
vncHandleClipboardAnnounce(available);
}
void XserverDesktop::handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32])
void XserverDesktop::handleClipboardData(const char* data_, int len)
{
vncHandleClipboardAnnounceBinary(num, mimes);
vncHandleClipboardData(data_, len);
}
void XserverDesktop::grabRegion(const rfb::Region& region)

View File

@@ -62,11 +62,7 @@ public:
void refreshScreenLayout();
void requestClipboard();
void announceClipboard(bool available);
void clearBinaryClipboardData();
void sendBinaryClipboardData(const char* mime, const unsigned char *data,
const unsigned len);
void getBinaryClipboardData(const char *mime, const unsigned char **ptr,
unsigned *len);
void sendClipboardData(const char* data);
void bell();
void setLEDState(unsigned int state);
void setDesktopName(const char* name);
@@ -94,12 +90,13 @@ public:
// rfb::SDesktop callbacks
virtual void pointerEvent(const rfb::Point& pos, int buttonMask,
const bool skipClick, const bool skipRelease, int scrollX = 0, int scrollY = 0);
const bool skipClick, const bool skipRelease);
virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down);
virtual unsigned int setScreenLayout(int fb_width, int fb_height,
const rfb::ScreenSet& layout);
virtual void handleClipboardRequest();
virtual void handleClipboardAnnounce(bool available);
virtual void handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]);
virtual void handleClipboardData(const char* data, int len);
// rfb::PixelBuffer callbacks
virtual void grabRegion(const rfb::Region& r);

View File

@@ -137,7 +137,7 @@ Send clipboard changes to clients. Default is on.
.TP
.B \-SendPrimary
Send the primary selection and cut buffer to the server as well as the
clipboard selection. Default is off.
clipboard selection. Default is on.
.
.TP
.B \-AcceptPointerEvents
@@ -295,22 +295,17 @@ Allow click releases inside the blacked-out region.
.
.TP
.B \-DLP_ClipSendMax \fIbytes\fP
Limit clipboard bytes to send to clients in one transaction. Default 0.
Limit clipboard bytes to send to clients in one transaction. Default 10,000.
0 disables the limit, use \fBSendCutText\fP to disable clipboard sending entirely.
.
.TP
.B \-DLP_ClipAcceptMax \fIbytes\fP
Limit clipboard bytes to receive from clients in one transaction. Default 0.
Limit clipboard bytes to receive from clients in one transaction. Default 10,000.
0 disables the limit, use \fBAcceptCutText\fP to disable clipboard receiving entirely.
.
.TP
.B \-DLP_ClipDelay \fIms\fP
This many milliseconds must pass between clipboard actions. Default 0, 0 disables the limit.
.
.TP
.B \-DLP_ClipTypes \fIa,b\fP
Allowed binary clipboard mimetypes, separated by commas. Default
chromium/x-web-custom-data,text/html,image/png
This many milliseconds must pass between clipboard actions. Default 1000.
.
.TP
.B \-DLP_KeyRateLimit \fIkeys-per-second\fP
@@ -322,10 +317,6 @@ Log clipboard and keyboard actions. Info logs just clipboard direction and size,
verbose adds the contents for both.
.
.TP
.B \-selfBench
Run a set of self-benchmarks and exit.
.
.TP
.B \-noWebsocket
Disable websockets and expose a traditional VNC port (5901, etc.).
.

View File

@@ -18,12 +18,10 @@
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <set>
#include <string>
#include <vector>
#include <rfb/Configuration.h>
#include <rfb/Logger_stdio.h>
@@ -66,8 +64,6 @@ int vncFbstride[MAXSCREENS];
int vncInetdSock = -1;
static std::vector<dlp_mimetype_t> dlp_mimetypes;
struct CaseInsensitiveCompare {
bool operator() (const std::string &a, const std::string &b) const {
return strcasecmp(a.c_str(), b.c_str()) < 0;
@@ -107,7 +103,7 @@ rfb::BoolParameter setPrimary("SetPrimary", "Set the PRIMARY as well "
"as the CLIPBOARD selection", true);
rfb::BoolParameter sendPrimary("SendPrimary",
"Send the PRIMARY as well as the CLIPBOARD selection",
false);
true);
static PixelFormat vncGetPixelFormat(int scrIdx)
{
@@ -151,38 +147,6 @@ static void parseOverrideList(const char *text, ParamSet &out)
}
}
static void parseClipTypes()
{
char *str = strdup(Server::DLP_Clip_Types);
char * const origstr = str;
while (1) {
char *cur = strsep(&str, ",");
if (!cur)
break;
if (!cur[0])
continue;
// Hardcoded filters
if (!strcmp(cur, "TEXT") ||
!strcmp(cur, "STRING") ||
strstr(cur, "text/plain") ||
!strcmp(cur, "UTF8_STRING"))
continue;
struct dlp_mimetype_t m;
strncpy(m.mime, cur, sizeof(m.mime));
m.mime[sizeof(m.mime) - 1] = '\0';
dlp_mimetypes.push_back(m);
vlog.debug("Adding DLP binary mime type %s", m.mime);
}
vlog.debug("Total %u binary mime types", dlp_mimetypes.size());
free(origstr);
}
void vncExtensionInit(void)
{
if (vncExtGeneration == vncGetServerGeneration()) {
@@ -199,8 +163,6 @@ void vncExtensionInit(void)
vncAddExtension();
if (!initialised)
parseClipTypes();
vncSelectionInit();
vlog.info("VNC extension running!");
@@ -353,30 +315,22 @@ void vncUpdateDesktopName(void)
desktop[scr]->setDesktopName(desktopName);
}
void vncRequestClipboard(void)
{
for (int scr = 0; scr < vncGetScreenCount(); scr++)
desktop[scr]->requestClipboard();
}
void vncAnnounceClipboard(int available)
{
for (int scr = 0; scr < vncGetScreenCount(); scr++)
desktop[scr]->announceClipboard(available);
}
void vncSendBinaryClipboardData(const char* mime, const unsigned char *data,
const unsigned len)
void vncSendClipboardData(const char* data)
{
for (int scr = 0; scr < vncGetScreenCount(); scr++)
desktop[scr]->sendBinaryClipboardData(mime, data, len);
}
void vncGetBinaryClipboardData(const char *mime, const unsigned char **ptr,
unsigned *len)
{
for (int scr = 0; scr < vncGetScreenCount(); scr++)
desktop[scr]->getBinaryClipboardData(mime, ptr, len);
}
void vncClearBinaryClipboardData()
{
for (int scr = 0; scr < vncGetScreenCount(); scr++)
desktop[scr]->clearBinaryClipboardData();
desktop[scr]->sendClipboardData(data);
}
int vncConnectClient(const char *addr)
@@ -532,13 +486,3 @@ int vncOverrideParam(const char *nameAndValue)
return rfb::Configuration::setParam(nameAndValue);
}
unsigned dlp_num_mimetypes()
{
return dlp_mimetypes.size();
}
const char *dlp_get_mimetype(const unsigned i)
{
return dlp_mimetypes[i].mime;
}

View File

@@ -45,13 +45,6 @@ int vncNotifyQueryConnect(void);
extern void* vncFbptr[];
extern int vncFbstride[];
struct dlp_mimetype_t {
char mime[32];
};
unsigned dlp_num_mimetypes();
const char *dlp_get_mimetype(const unsigned i);
extern int vncInetdSock;
void vncExtensionInit(void);
@@ -67,12 +60,9 @@ int vncGetSendPrimary(void);
void vncUpdateDesktopName(void);
void vncRequestClipboard(void);
void vncAnnounceClipboard(int available);
void vncClearBinaryClipboardData();
void vncSendBinaryClipboardData(const char* mime, const unsigned char *data,
const unsigned len);
void vncGetBinaryClipboardData(const char *mime, const unsigned char **ptr,
unsigned *len);
void vncSendClipboardData(const char* data);
int vncConnectClient(const char *addr);

View File

@@ -43,14 +43,6 @@
static Atom xaPRIMARY, xaCLIPBOARD;
static Atom xaTARGETS, xaTIMESTAMP, xaSTRING, xaTEXT, xaUTF8_STRING;
static Atom xaINCR;
static Atom *xaBinclips;
static unsigned xaHtmlIndex, xaPngIndex;
static Bool htmlPngPresent;
static unsigned *mimeIndexesFromClient;
static unsigned numMimesFromClient;
static Bool textFromClient;
static WindowPtr pWindow;
static Window wid;
@@ -74,7 +66,8 @@ static int vncCreateSelectionWindow(void);
static int vncOwnSelection(Atom selection);
static int vncConvertSelection(ClientPtr client, Atom selection,
Atom target, Atom property,
Window requestor, CARD32 time);
Window requestor, CARD32 time,
const char* data, int len);
static int vncProcConvertSelection(ClientPtr client);
static void vncSelectionRequest(Atom selection, Atom target);
static int vncProcSendEvent(ClientPtr client);
@@ -97,32 +90,6 @@ void vncSelectionInit(void)
xaTEXT = MakeAtom("TEXT", 4, TRUE);
xaUTF8_STRING = MakeAtom("UTF8_STRING", 11, TRUE);
xaINCR = MakeAtom("INCR", 4, TRUE);
unsigned i;
mimeIndexesFromClient = calloc(dlp_num_mimetypes(), sizeof(unsigned));
numMimesFromClient = 0;
textFromClient = FALSE;
xaBinclips = calloc(dlp_num_mimetypes(), sizeof(Atom));
htmlPngPresent = FALSE;
Bool htmlfound = FALSE, pngfound = FALSE;
for (i = 0; i < dlp_num_mimetypes(); i++) {
const char *cur = dlp_get_mimetype(i);
xaBinclips[i] = MakeAtom(cur, strlen(cur), TRUE);
if (!strcmp(cur, "text/html")) {
xaHtmlIndex = i;
htmlfound = TRUE;
}
if (!strcmp(cur, "image/png")) {
xaPngIndex = i;
pngfound = TRUE;
}
}
if (htmlfound && pngfound)
htmlPngPresent = TRUE;
/* There are no hooks for when these are internal windows, so
* override the relevant handlers. */
origProcConvertSelection = ProcVector[X_ConvertSelection];
@@ -136,7 +103,7 @@ void vncSelectionInit(void)
FatalError("Add VNC ClientStateCallback failed\n");
}
static void vncHandleClipboardRequest(void)
void vncHandleClipboardRequest(void)
{
if (activeSelection == None) {
LOG_DEBUG("Got request for local clipboard although no clipboard is active");
@@ -194,55 +161,24 @@ void vncHandleClipboardAnnounce(int available)
}
}
void vncHandleClipboardAnnounceBinary(const unsigned num, const char mimes[][32])
void vncHandleClipboardData(const char* data, int len)
{
if (num) {
struct VncDataTarget* next;
LOG_DEBUG("Got remote clipboard data, sending to X11 clients");
while (vncDataTargetHead != NULL) {
int rc;
xEvent event;
LOG_DEBUG("Remote binary clipboard announced, grabbing local ownership");
if (vncGetSetPrimary()) {
rc = vncOwnSelection(xaPRIMARY);
if (rc != Success)
LOG_ERROR("Could not set PRIMARY selection");
}
rc = vncOwnSelection(xaCLIPBOARD);
if (rc != Success)
LOG_ERROR("Could not set CLIPBOARD selection");
unsigned i, valid = 0;
for (i = 0; i < num; i++) {
unsigned j;
for (j = 0; j < dlp_num_mimetypes(); j++) {
if (!strcmp(dlp_get_mimetype(j), mimes[i])) {
mimeIndexesFromClient[valid] = j;
valid++;
break;
}
}
if (!strcmp(mimes[i], "text/plain"))
textFromClient = TRUE;
}
numMimesFromClient = valid;
LOG_DEBUG("Client sent %u mimes, %u were valid", num, valid);
} else {
struct VncDataTarget* next;
numMimesFromClient = 0;
textFromClient = FALSE;
if (pWindow == NULL)
return;
LOG_DEBUG("Remote binary clipboard lost, removing local ownership");
DeleteWindowFromAnySelections(pWindow);
/* Abort any pending transfer */
while (vncDataTargetHead != NULL) {
xEvent event;
rc = vncConvertSelection(vncDataTargetHead->client,
vncDataTargetHead->selection,
vncDataTargetHead->target,
vncDataTargetHead->property,
vncDataTargetHead->requestor,
vncDataTargetHead->time,
data, len);
if (rc != Success) {
event.u.u.type = SelectionNotify;
event.u.selectionNotify.time = vncDataTargetHead->time;
event.u.selectionNotify.requestor = vncDataTargetHead->requestor;
@@ -250,11 +186,11 @@ void vncHandleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]
event.u.selectionNotify.target = vncDataTargetHead->target;
event.u.selectionNotify.property = None;
WriteEventsToClient(vncDataTargetHead->client, 1, &event);
next = vncDataTargetHead->next;
free(vncDataTargetHead);
vncDataTargetHead = next;
}
next = vncDataTargetHead->next;
free(vncDataTargetHead);
vncDataTargetHead = next;
}
}
@@ -341,22 +277,10 @@ static int vncOwnSelection(Atom selection)
return Success;
}
static Bool clientHasBinaryAtom(const Atom target, unsigned *which)
{
unsigned i;
for (i = 0; i < numMimesFromClient; i++) {
if (xaBinclips[mimeIndexesFromClient[i]] == target) {
*which = mimeIndexesFromClient[i];
return TRUE;
}
}
return FALSE;
}
static int vncConvertSelection(ClientPtr client, Atom selection,
Atom target, Atom property,
Window requestor, CARD32 time)
Window requestor, CARD32 time,
const char* data, int len)
{
Selection *pSel;
WindowPtr pWin;
@@ -366,8 +290,13 @@ static int vncConvertSelection(ClientPtr client, Atom selection,
xEvent event;
LOG_DEBUG("Selection request for %s (type %s)",
NameForAtom(selection), NameForAtom(target));
if (data == NULL) {
LOG_DEBUG("Selection request for %s (type %s)",
NameForAtom(selection), NameForAtom(target));
} else {
LOG_DEBUG("Sending data for selection request for %s (type %s)",
NameForAtom(selection), NameForAtom(target));
}
rc = dixLookupSelection(&pSel, selection, client, DixGetAttrAccess);
if (rc != Success)
@@ -386,23 +315,14 @@ static int vncConvertSelection(ClientPtr client, Atom selection,
realProperty = target;
/* FIXME: MULTIPLE target */
unsigned binatomidx;
if (target == xaTARGETS) {
Atom targets[5 + numMimesFromClient];
targets[0] = xaTARGETS;
targets[1] = xaTIMESTAMP;
targets[2] = xaSTRING;
targets[3] = xaTEXT;
targets[4] = xaUTF8_STRING;
unsigned i;
for (i = 0; i < numMimesFromClient; i++)
targets[5 + i] = xaBinclips[mimeIndexesFromClient[i]];
Atom targets[] = { xaTARGETS, xaTIMESTAMP,
xaSTRING, xaTEXT, xaUTF8_STRING };
rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
XA_ATOM, 32, PropModeReplace,
5 + numMimesFromClient,
sizeof(targets)/sizeof(targets[0]),
targets, TRUE);
if (rc != Success)
return rc;
@@ -413,47 +333,59 @@ static int vncConvertSelection(ClientPtr client, Atom selection,
TRUE);
if (rc != Success)
return rc;
} else if (clientHasBinaryAtom(target, &binatomidx)) {
const unsigned char *data;
unsigned len;
vncGetBinaryClipboardData(dlp_get_mimetype(binatomidx), &data, &len);
rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
xaBinclips[binatomidx], 8, PropModeReplace,
len, (unsigned char *) data, TRUE);
if (rc != Success)
return rc;
} else if (textFromClient) {
const unsigned char *data;
unsigned len;
vncGetBinaryClipboardData("text/plain", &data, &len);
} else {
if (data == NULL) {
struct VncDataTarget* vdt;
if ((target == xaSTRING) || (target == xaTEXT)) {
char* latin1;
latin1 = vncUTF8ToLatin1(data, (size_t)-1);
if (latin1 == NULL)
if ((target != xaSTRING) && (target != xaTEXT) &&
(target != xaUTF8_STRING))
return BadMatch;
vdt = calloc(1, sizeof(struct VncDataTarget));
if (vdt == NULL)
return BadAlloc;
rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
XA_STRING, 8, PropModeReplace,
len, latin1, TRUE);
vdt->client = client;
vdt->selection = selection;
vdt->target = target;
vdt->property = property;
vdt->requestor = requestor;
vdt->time = time;
vncStrFree(latin1);
vdt->next = vncDataTargetHead;
vncDataTargetHead = vdt;
if (rc != Success)
return rc;
} else if (target == xaUTF8_STRING) {
rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
xaUTF8_STRING, 8, PropModeReplace,
len, (char *) data, TRUE);
if (rc != Success)
return rc;
LOG_DEBUG("Requesting clipboard data from client");
vncRequestClipboard();
return Success;
} else {
return BadMatch;
}
} else {
LOG_ERROR("Text clipboard paste requested, but client sent no text");
if ((target == xaSTRING) || (target == xaTEXT)) {
char* latin1;
return BadMatch;
latin1 = vncUTF8ToLatin1(data, (size_t)-1);
if (latin1 == NULL)
return BadAlloc;
rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
XA_STRING, 8, PropModeReplace,
len, latin1, TRUE);
vncStrFree(latin1);
if (rc != Success)
return rc;
} else if (target == xaUTF8_STRING) {
rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
xaUTF8_STRING, 8, PropModeReplace,
len, data, TRUE);
if (rc != Success)
return rc;
} else {
return BadMatch;
}
}
}
event.u.u.type = SelectionNotify;
@@ -492,7 +424,7 @@ static int vncProcConvertSelection(ClientPtr client)
pSel->window == wid) {
rc = vncConvertSelection(client, stuff->selection,
stuff->target, stuff->property,
stuff->requestor, stuff->time);
stuff->requestor, stuff->time, NULL, 0);
if (rc != Success) {
xEvent event;
@@ -551,21 +483,6 @@ static Bool vncHasAtom(Atom atom, const Atom list[], size_t size)
return FALSE;
}
static Bool vncHasBinaryClipboardAtom(const Atom list[], size_t size)
{
size_t i, b;
const unsigned num = dlp_num_mimetypes();
for (i = 0;i < size;i++) {
for (b = 0; b < num; b++) {
if (list[i] == xaBinclips[b])
return TRUE;
}
}
return FALSE;
}
static void vncHandleSelection(Atom selection, Atom target,
Atom property, Atom requestor,
TimeStamp time)
@@ -585,9 +502,6 @@ static void vncHandleSelection(Atom selection, Atom target,
if (target != property)
return;
if (prop->type == xaINCR)
LOG_INFO("Incremental clipboard transfer denied, too large");
if (target == xaTARGETS) {
if (prop->format != 32)
return;
@@ -596,36 +510,16 @@ static void vncHandleSelection(Atom selection, Atom target,
if (probing) {
if (vncHasAtom(xaSTRING, (const Atom*)prop->data, prop->size) ||
vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size) ||
vncHasBinaryClipboardAtom((const Atom*)prop->data, prop->size)) {
vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size)) {
LOG_DEBUG("Compatible format found, notifying clients");
vncClearBinaryClipboardData();
activeSelection = selection;
vncAnnounceClipboard(TRUE);
vncHandleClipboardRequest();
}
} else {
if (vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size))
vncSelectionRequest(selection, xaUTF8_STRING);
else if (vncHasAtom(xaSTRING, (const Atom*)prop->data, prop->size))
vncSelectionRequest(selection, xaSTRING);
unsigned i;
Bool skiphtml = FALSE;
if (htmlPngPresent &&
vncHasAtom(xaBinclips[xaHtmlIndex], (const Atom*)prop->data, prop->size) &&
vncHasAtom(xaBinclips[xaPngIndex], (const Atom*)prop->data, prop->size))
skiphtml = TRUE;
for (i = 0; i < dlp_num_mimetypes(); i++) {
if (skiphtml && i == xaHtmlIndex)
continue;
if (vncHasAtom(xaBinclips[i], (const Atom*)prop->data, prop->size)) {
vncSelectionRequest(selection, xaBinclips[i]);
//break;
}
}
}
} else if (target == xaSTRING) {
char* filtered;
@@ -645,10 +539,10 @@ static void vncHandleSelection(Atom selection, Atom target,
if (utf8 == NULL)
return;
LOG_DEBUG("Sending text part of binary clipboard to clients (%d bytes)",
LOG_DEBUG("Sending clipboard to clients (%d bytes)",
(int)strlen(utf8));
vncSendBinaryClipboardData("text/plain", utf8, strlen(utf8));
vncSendClipboardData(utf8);
vncStrFree(utf8);
} else if (target == xaUTF8_STRING) {
@@ -663,31 +557,12 @@ static void vncHandleSelection(Atom selection, Atom target,
if (filtered == NULL)
return;
LOG_DEBUG("Sending text part of binary clipboard to clients (%d bytes)",
LOG_DEBUG("Sending clipboard to clients (%d bytes)",
(int)strlen(filtered));
vncSendBinaryClipboardData("text/plain", filtered, strlen(filtered));
vncSendClipboardData(filtered);
vncStrFree(filtered);
} else {
unsigned i;
if (prop->format != 8)
return;
for (i = 0; i < dlp_num_mimetypes(); i++) {
if (target == xaBinclips[i]) {
if (prop->type != xaBinclips[i])
return;
LOG_DEBUG("Sending binary clipboard to clients (%d bytes)",
prop->size);
vncSendBinaryClipboardData(dlp_get_mimetype(i), prop->data, prop->size);
break;
}
}
}
}

View File

@@ -24,8 +24,9 @@ extern "C" {
void vncSelectionInit(void);
void vncHandleClipboardRequest(void);
void vncHandleClipboardAnnounce(int available);
void vncHandleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]);
void vncHandleClipboardData(const char* data, int len);
#ifdef __cplusplus
}