Compare commits
252 Commits
v0.9.2-bet
...
feature/KA
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
152821d77b | ||
|
|
906fcc3e67 | ||
|
|
ab32cbc53e | ||
|
|
a48811b12f | ||
|
|
4c4808d2bd | ||
|
|
e282851073 | ||
|
|
40d263368d | ||
|
|
377d3e5629 | ||
|
|
5b516e84d9 | ||
|
|
43c31d759b | ||
|
|
aa7d1b5a30 | ||
|
|
8b5600e8f3 | ||
|
|
bbd971e9f1 | ||
|
|
0467e938a2 | ||
|
|
01623eaffd | ||
|
|
3383ad52e4 | ||
|
|
6f2805e186 | ||
|
|
d80eb39686 | ||
|
|
4b28667e90 | ||
|
|
5341bc6fba | ||
|
|
9ec4cba184 | ||
|
|
643a0cfed6 | ||
|
|
9ca850a108 | ||
|
|
ec6bd697a8 | ||
|
|
544a9fc592 | ||
|
|
7b5838a1ea | ||
|
|
a9b66833a9 | ||
|
|
e208d5bb5f | ||
|
|
f57e6e644b | ||
|
|
c1ed769780 | ||
|
|
fae8aa9051 | ||
|
|
438271d68b | ||
|
|
fb9dd56703 | ||
|
|
32e8d40472 | ||
|
|
811e7cde3a | ||
|
|
72affbfcf4 | ||
|
|
b362110b07 | ||
|
|
29e0e2bd2e | ||
|
|
9144045718 | ||
|
|
7a695c976e | ||
|
|
aefdb1392f | ||
|
|
14e08a2067 | ||
|
|
f64bd4f1a4 | ||
|
|
0b6334369c | ||
|
|
b8c69a7560 | ||
|
|
e84a37fb3d | ||
|
|
23935145a8 | ||
|
|
7f7b177b1a | ||
|
|
95d34f7e58 | ||
|
|
a1cf454f06 | ||
|
|
04461b9d4c | ||
|
|
0d482f9a9b | ||
|
|
a7773bee6b | ||
|
|
75cef8b726 | ||
|
|
380bc0e7f8 | ||
|
|
999cfbb806 | ||
|
|
12a0c0d8af | ||
|
|
7bfcb19ee1 | ||
|
|
df9ca2e0ce | ||
|
|
4fda2526f4 | ||
|
|
102257cb32 | ||
|
|
77509c1e56 | ||
|
|
9e0a5d26af | ||
|
|
c6c2661121 | ||
|
|
8b8201fd73 | ||
|
|
33a113a475 | ||
|
|
6d1cebc24a | ||
|
|
fcd7836a83 | ||
|
|
073737c8ac | ||
|
|
16c72ba0a7 | ||
|
|
d3c9ff8b12 | ||
|
|
4bc4c1d7f6 | ||
|
|
d8ba9a61e2 | ||
|
|
0e2a25476f | ||
|
|
b3b4acaaa3 | ||
|
|
201f2517f7 | ||
|
|
f51167a2ce | ||
|
|
87b3a630cf | ||
|
|
7412dde8ee | ||
|
|
3ef6e1333b | ||
|
|
b84220f319 | ||
|
|
42d437ad25 | ||
|
|
d5b84d9185 | ||
|
|
6db1c2bf0d | ||
|
|
ae1bc396bf | ||
|
|
c7a7ce70be | ||
|
|
55178c76b8 | ||
|
|
5752bf8ab7 | ||
|
|
6fd1035fb1 | ||
|
|
212e0f1a10 | ||
|
|
69b945fc87 | ||
|
|
b837bd6f59 | ||
|
|
670eefbc97 | ||
|
|
b2d8db3f7b | ||
|
|
a3e2b1e46e | ||
|
|
472ede66ea | ||
|
|
da228af778 | ||
|
|
59f5648592 | ||
|
|
19c473f792 | ||
|
|
8a8fa1d906 | ||
|
|
5a81223d96 | ||
|
|
eff38d47f9 | ||
|
|
1de3769fd3 | ||
|
|
749ddce5b9 | ||
|
|
d420aa6413 | ||
|
|
93e42772bc | ||
|
|
9098478faf | ||
|
|
4710ec1822 | ||
|
|
02b9b268ff | ||
|
|
a015e33111 | ||
|
|
950d871b0e | ||
|
|
02e7b006de | ||
|
|
95af51e28d | ||
|
|
64dfb8d1d6 | ||
|
|
b173c8854a | ||
|
|
888f24e7af | ||
|
|
8be81165bd | ||
|
|
509b5795a0 | ||
|
|
581fe511ad | ||
|
|
9ca647667b | ||
|
|
dbe2930758 | ||
|
|
b690ae4c2d | ||
|
|
5e4544d5fc | ||
|
|
0b19961b06 | ||
|
|
286694869b | ||
|
|
fdeefcfab4 | ||
|
|
24cf1f0f9a | ||
|
|
d01f6e6d27 | ||
|
|
c1160d1468 | ||
|
|
1c38b6f120 | ||
|
|
70dd0058ac | ||
|
|
a4c5c38b53 | ||
|
|
789308212b | ||
|
|
ce5fe304cb | ||
|
|
781075c841 | ||
|
|
2f43cead79 | ||
|
|
e7a5aa271e | ||
|
|
ad206180d2 | ||
|
|
89dd199317 | ||
|
|
8be70e5ae0 | ||
|
|
ce94d92e18 | ||
|
|
c3ef9ff557 | ||
|
|
c0276776e9 | ||
|
|
10f4aa9e7b | ||
|
|
b2dc76ee18 | ||
|
|
12cdad066e | ||
|
|
a850be4afa | ||
|
|
354d544843 | ||
|
|
0bf9403a36 | ||
|
|
29db8997ec | ||
|
|
46f15667f7 | ||
|
|
edb5fee88b | ||
|
|
23871b42d1 | ||
|
|
d6d875ef3b | ||
|
|
6c84631bbc | ||
|
|
6bb87ee2b2 | ||
|
|
15c74e7dc9 | ||
|
|
f1be4be653 | ||
|
|
b5f1a00282 | ||
|
|
740a8217ab | ||
|
|
796de9653f | ||
|
|
6916c83b48 | ||
|
|
d10d7167f6 | ||
|
|
a8dc933701 | ||
|
|
d61bf69c33 | ||
|
|
62fca18cb9 | ||
|
|
dc3ddc8efc | ||
|
|
e497b53d09 | ||
|
|
7dc51fa7a5 | ||
|
|
443858cf83 | ||
|
|
6b39933658 | ||
|
|
6067290169 | ||
|
|
a8699ab8cb | ||
|
|
996895268e | ||
|
|
fb14c2dec9 | ||
|
|
83d5e5bffd | ||
|
|
19dfb7901c | ||
|
|
6acf3c9e62 | ||
|
|
0dcb896920 | ||
|
|
d88aefba4e | ||
|
|
b0c54f6b24 | ||
|
|
6342a117ff | ||
|
|
766fc43855 | ||
|
|
19f0803636 | ||
|
|
3c3ac34eb8 | ||
|
|
8c43287afb | ||
|
|
c16cc6e2b9 | ||
|
|
c211f31113 | ||
|
|
4cc536636a | ||
|
|
734bdd3746 | ||
|
|
8218a0cb2a | ||
|
|
2525174260 | ||
|
|
b0896c8859 | ||
|
|
09de4b8349 | ||
|
|
d4747a8c80 | ||
|
|
5b73c1ca23 | ||
|
|
f21fb95bb9 | ||
|
|
7b02f3d8b8 | ||
|
|
03a333fb18 | ||
|
|
54253efaa0 | ||
|
|
8b859f4598 | ||
|
|
8c5b5f17eb | ||
|
|
71fa476514 | ||
|
|
4fd1f19f3b | ||
|
|
257aabecb0 | ||
|
|
74161066af | ||
|
|
e2d86788ba | ||
|
|
4c635f65d3 | ||
|
|
1d4ada6815 | ||
|
|
d3ec2aa4d1 | ||
|
|
10c334303a | ||
|
|
c4987024ef | ||
|
|
04d7bef2ae | ||
|
|
12565dc4ac | ||
|
|
46b7d1db95 | ||
|
|
0045f07965 | ||
|
|
d1fdb877ef | ||
|
|
e62e12fbfe | ||
|
|
50183c7caa | ||
|
|
79ab05192b | ||
|
|
c6c278f9d5 | ||
|
|
37c17ddbf9 | ||
|
|
c755008d15 | ||
|
|
075eed5cbb | ||
|
|
7b7295fd4e | ||
|
|
5964156a6f | ||
|
|
1ff792cf4b | ||
|
|
3043216b63 | ||
|
|
499b251716 | ||
|
|
d20f751441 | ||
|
|
915901848d | ||
|
|
ee2423ed2b | ||
|
|
ce6d66f030 | ||
|
|
41e6fedba6 | ||
|
|
642a67f76f | ||
|
|
d7198cbe5d | ||
|
|
25b8e64adb | ||
|
|
d8caab699d | ||
|
|
da83ecf86a | ||
|
|
189f503b98 | ||
|
|
f54dc7829a | ||
|
|
fde088ce65 | ||
|
|
a55f142c98 | ||
|
|
25995e2490 | ||
|
|
281d65292a | ||
|
|
57a3c3bba8 | ||
|
|
92c7695981 | ||
|
|
7f90205cf2 | ||
|
|
910fd8fa3e | ||
|
|
c97828471c | ||
|
|
c5b7137f2b | ||
|
|
a3c0ce55c9 |
@@ -4,6 +4,7 @@ services:
|
|||||||
|
|
||||||
variables:
|
variables:
|
||||||
GITLAB_SHARED_DIND_DIR: /builds/$CI_PROJECT_PATH/shared
|
GITLAB_SHARED_DIND_DIR: /builds/$CI_PROJECT_PATH/shared
|
||||||
|
GIT_SUBMODULE_STRATEGY: normal
|
||||||
GIT_FETCH_EXTRA_FLAGS: --tags
|
GIT_FETCH_EXTRA_FLAGS: --tags
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
|
|||||||
4
.gitmodules
vendored
Normal file
4
.gitmodules
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[submodule "kasmweb"]
|
||||||
|
path = kasmweb
|
||||||
|
url = https://github.com/kasmtech/noVNC.git
|
||||||
|
branch = master
|
||||||
@@ -20,7 +20,7 @@ Incomplete and generally out of date copyright list::
|
|||||||
All Rights Reserved.
|
All Rights Reserved.
|
||||||
|
|
||||||
This software is distributed under the GNU General Public Licence as published
|
This software is distributed under the GNU General Public Licence as published
|
||||||
by the Free Software Foundation. See the file LICENCE.TXT for the conditions
|
by the Free Software Foundation. See the file LICENSE.TXT for the conditions
|
||||||
under which this software is made available. KasmVNC also contains code from
|
under which this software is made available. KasmVNC also contains code from
|
||||||
other sources. See the Acknowledgements section below, and the individual
|
other sources. See the Acknowledgements section below, and the individual
|
||||||
source files, for details of the conditions under which they are made
|
source files, for details of the conditions under which they are made
|
||||||
@@ -105,4 +105,4 @@ This distribution contains software from the X Window System. This is:
|
|||||||
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||||
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||||
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|||||||
18
README.md
18
README.md
@@ -1,11 +1,11 @@
|
|||||||
# KasmVNC - Linux Web Remote Desktop
|
# KasmVNC - Linux Web Remote Desktop
|
||||||
|
|
||||||
[](https://kasmweb.com)
|
<a href="https://kasmweb.com"><img src="https://kasm-static-content.s3.amazonaws.com/logo_kasm.png" width="300"><a/>
|
||||||
|
|
||||||
[Kasm Technologies LLC](https://www.kasmweb.com) developed Kasm Server, a Containerized Desktop Infrastructure (CDI) solution. Kasm started with TigerVNC and eventually forked it to create KasmVNC. KasmVNC is used within the overal Kasm CDI infrastructure, however, you can use KasmVNC for individual servers. KasmVNC has different goals than TigerVNC:
|
[Kasm Technologies](https://www.kasmweb.com) developed Kasm Workspaces, the Containerized Streaming Platform. Kasm has open-sourced the Workspace docker images, which include containerized [full desktops and apps](https://github.com/kasmtech/workspaces-images) and [base images](https://github.com/kasmtech/workspaces-core-images) intended for developers to create custimized streaming containers. These containers can be used standalone or within the [Kasm Workspaces Platform](https://www.kasmweb.com) which provides a full Enterprise feature set. KasmVNC is used as the streaming tech for our container images, however, you can use KasmVNC for individual servers. While the term VNC is in the name, KasmVNC is not intended to remain compliant with the RFB spec and has different goals than other VNC projects:
|
||||||
|
|
||||||
- Web-based - KasmVNC is designed to provide a web accessible remote desktop. It comes with a web server and websocket server built in. There is no need to install other components. Simply run and navigate to your desktop's URL on the port you specify. While you can still tun on the legacy VNC port, it is disabled by default.
|
- Web-based - KasmVNC is designed to provide a web accessible remote desktop. It comes with a web server and websocket server built in. There is no need to install other components. Simply run and navigate to your desktop's URL on the port you specify. While you can still tun on the legacy VNC port, it is disabled by default.
|
||||||
- Security - KasmVNC defaults to HTTPS and allows for HTTP Basic Auth. VNC Password authentication is limited by specification to 8 characters and is not suffecient for use on an internet accessible remote desktop. Our goal is to create a by default secure, web based experience.
|
- Security - The RFB specification (VNC) limits the password field to 8 characters, so while the client may take a longer password, only the first 8 characters are sent. KasmVNC defaults to HTTPS with HTTP Basic Auth and disables the legacy VNC authentication method which is not sufficiently secure for internet accessible systems.
|
||||||
- Simplicity - KasmVNC aims at being simple to deploy and configure.
|
- Simplicity - KasmVNC aims at being simple to deploy and configure.
|
||||||
|
|
||||||
# New Features!
|
# New Features!
|
||||||
@@ -91,14 +91,16 @@ The options for vncserver:
|
|||||||
| interface | Which interface to bind the web server to. |
|
| interface | Which interface to bind the web server to. |
|
||||||
|
|
||||||
### Development
|
### Development
|
||||||
Would you like to contribute to KasmVNC? Please reachout to us at info@kasmweb.com
|
Would you like to contribute to KasmVNC? Please reachout to us at info@kasmweb.com. We have investigated or are working on the following, if you have experience in these fields and would like to help please let us know.
|
||||||
|
|
||||||
We need help, especially in packaging KasmVNC for various operating systems. We would love to have standard debian or RMP packages and host our own repo, however, that all requires a lot of experience, proper testing, and pipeline development for automated builds.
|
Real-time H264 encoding using NVIDIA and Intel encoding technology.
|
||||||
|
|
||||||
We also need help with Windows, which is not currently supported. While KasmVNC can technically be built for Windows 10, it is unusably slow, due to all the changes that occured in Windows since the original Windows support was added in the chain of VNC forked projects.
|
Windows version of KasmVNC. We have been able to get it to compile for Windows and increased the performance, but still not releasable. Experienced Windows developers with a background in cross compiling would help.
|
||||||
|
|
||||||
|
ARM version of KasmVNC, we have had requests for this and at one point we did have an ARM build of KasmVNC but it takes dev cycles to mainain and bring it back to life.
|
||||||
|
|
||||||
### Compiling From Source
|
### Compiling From Source
|
||||||
See the [builder/README.md](https://github.com/kasmtech/KasmVNC/blob/master/builder/README.md). We containerize our build systems to ensure highly repeatable builds.
|
See the [builder/README.md](https://github.com/kasmtech/KasmVNC/blob/master/builder/README.md). We containerize our build systems to ensure highly repeatable builds.
|
||||||
|
|
||||||
### License and Acknowledgements
|
### License and Acknowledgements
|
||||||
See the [LICENSE.TXT](https://github.com/kasmtech/KasmVNC/blob/master/LICENCE.TXT) and [ACKNOWLEDGEMENTS.MD](https://github.com/kasmtech/KasmVNC/blob/master/LICENSE.TXT)
|
See the [LICENSE.TXT](https://github.com/kasmtech/KasmVNC/blob/master/LICENSE.TXT) and [ACKNOWLEDGEMENTS.MD](https://github.com/kasmtech/KasmVNC/blob/master/LICENSE.TXT)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
build_www_dir() {
|
build_www_dir() {
|
||||||
local webpacked_www=$PWD/builder/www
|
local webpacked_www=$PWD/builder/www
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#!/bin/sh -e
|
#!/bin/sh -e
|
||||||
|
|
||||||
|
set -x
|
||||||
|
|
||||||
detect_quilt() {
|
detect_quilt() {
|
||||||
if which quilt 1>/dev/null; then
|
if which quilt 1>/dev/null; then
|
||||||
QUILT_PRESENT=1
|
QUILT_PRESENT=1
|
||||||
@@ -80,7 +82,9 @@ cd /src
|
|||||||
detect_quilt
|
detect_quilt
|
||||||
if [ -n "$QUILT_PRESENT" ]; then
|
if [ -n "$QUILT_PRESENT" ]; then
|
||||||
quilt push -a
|
quilt push -a
|
||||||
|
echo 'Patches applied!'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
make servertarball
|
make servertarball
|
||||||
|
|
||||||
cp kasmvnc*.tar.gz /build/kasmvnc.${KASMVNC_BUILD_OS}_${KASMVNC_BUILD_OS_CODENAME}.tar.gz
|
cp kasmvnc*.tar.gz /build/kasmvnc.${KASMVNC_BUILD_OS}_${KASMVNC_BUILD_OS_CODENAME}.tar.gz
|
||||||
|
|||||||
@@ -14,6 +14,5 @@ cp -R ./* /build/
|
|||||||
cd /build
|
cd /build
|
||||||
rm *.md
|
rm *.md
|
||||||
rm AUTHORS
|
rm AUTHORS
|
||||||
rm *.yml
|
|
||||||
rm vnc.html
|
rm vnc.html
|
||||||
rm vnc_lite.html
|
rm vnc_lite.html
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ RUN yum install -y make
|
|||||||
RUN yum group install -y "Development Tools"
|
RUN yum group install -y "Development Tools"
|
||||||
RUN yum install -y xorg-x11-server-devel zlib-devel libjpeg-turbo-devel
|
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 \
|
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
|
RUN yum install -y mesa-dri-drivers
|
||||||
|
|
||||||
# Additions for webp
|
# Additions for webp
|
||||||
@@ -22,8 +23,7 @@ RUN cd /tmp/libwebp-1.0.2 && \
|
|||||||
|
|
||||||
RUN useradd -m docker && echo "docker:docker" | chpasswd
|
RUN useradd -m docker && echo "docker:docker" | chpasswd
|
||||||
|
|
||||||
COPY . /src
|
COPY --chown=docker:docker . /src/
|
||||||
RUN chown -R docker:docker /src
|
|
||||||
|
|
||||||
USER docker
|
USER docker
|
||||||
ENTRYPOINT ["/src/builder/build.sh"]
|
ENTRYPOINT ["/src/builder/build.sh"]
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ RUN apt-get update && \
|
|||||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata
|
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 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 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
|
# 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 && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
|
||||||
@@ -24,8 +24,7 @@ RUN cd /tmp/libwebp-1.0.2 && \
|
|||||||
|
|
||||||
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
|
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
|
||||||
|
|
||||||
COPY . /src
|
COPY --chown=docker:docker . /src/
|
||||||
RUN chown -R docker:docker /src
|
|
||||||
|
|
||||||
USER docker
|
USER docker
|
||||||
ENTRYPOINT ["/src/builder/build.sh"]
|
ENTRYPOINT ["/src/builder/build.sh"]
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ RUN apt-get update && \
|
|||||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata
|
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 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 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
|
# 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 && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
|
||||||
@@ -24,8 +24,7 @@ RUN cd /tmp/libwebp-1.0.2 && \
|
|||||||
|
|
||||||
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
|
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
|
||||||
|
|
||||||
COPY . /src
|
COPY --chown=docker:docker . /src/
|
||||||
RUN chown -R docker:docker /src
|
|
||||||
|
|
||||||
USER docker
|
USER docker
|
||||||
ENTRYPOINT ["/src/builder/build.sh"]
|
ENTRYPOINT ["/src/builder/build.sh"]
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ RUN dnf install -y make
|
|||||||
RUN dnf group install -y "Development Tools"
|
RUN dnf group install -y "Development Tools"
|
||||||
RUN dnf install -y xorg-x11-server-devel zlib-devel libjpeg-turbo-devel
|
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 \
|
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 mesa-dri-drivers
|
||||||
RUN dnf install -y bzip2 redhat-lsb-core
|
RUN dnf install -y bzip2 redhat-lsb-core
|
||||||
|
|
||||||
@@ -31,8 +31,7 @@ RUN cd /tmp/libwebp-1.0.2 && \
|
|||||||
|
|
||||||
RUN useradd -m docker && echo "docker:docker" | chpasswd
|
RUN useradd -m docker && echo "docker:docker" | chpasswd
|
||||||
|
|
||||||
COPY . /src
|
COPY --chown=docker:docker . /src/
|
||||||
RUN chown -R docker:docker /src
|
|
||||||
|
|
||||||
USER docker
|
USER docker
|
||||||
ENTRYPOINT ["/src/builder/build.sh"]
|
ENTRYPOINT ["/src/builder/build.sh"]
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ RUN apt-get update && \
|
|||||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata
|
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 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 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
|
# 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 && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
|
||||||
@@ -24,8 +24,7 @@ RUN cd /tmp/libwebp-1.0.2 && \
|
|||||||
|
|
||||||
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
|
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
|
||||||
|
|
||||||
COPY . /src
|
COPY --chown=docker:docker . /src/
|
||||||
RUN chown -R docker:docker /src
|
|
||||||
|
|
||||||
USER docker
|
USER docker
|
||||||
ENTRYPOINT ["/src/builder/build.sh"]
|
ENTRYPOINT ["/src/builder/build.sh"]
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ RUN cd /tmp/libwebp-1.0.2 && ./configure && make && make install
|
|||||||
|
|
||||||
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
|
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
|
||||||
|
|
||||||
COPY . /src
|
COPY --chown=docker:docker . /src/
|
||||||
RUN chown -R docker:docker /src
|
|
||||||
|
|
||||||
|
|
||||||
USER docker
|
USER docker
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ RUN apt-get update && \
|
|||||||
|
|
||||||
RUN apt-get update && apt-get -y build-dep xorg-server libxfont-dev
|
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 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
|
# 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 && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
|
||||||
@@ -28,8 +28,7 @@ RUN cd /tmp/libwebp-1.0.2 && \
|
|||||||
|
|
||||||
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
|
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
|
||||||
|
|
||||||
COPY . /src
|
COPY --chown=docker:docker . /src/
|
||||||
RUN chown -R docker:docker /src
|
|
||||||
|
|
||||||
USER docker
|
USER docker
|
||||||
ENTRYPOINT ["/src/builder/build.sh"]
|
ENTRYPOINT ["/src/builder/build.sh"]
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ RUN apt-get update && \
|
|||||||
RUN apt-get update && apt-get install -y --no-install-recommends tzdata
|
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 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 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
|
# 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 && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
|
||||||
@@ -24,8 +24,7 @@ RUN cd /tmp/libwebp-1.0.2 && \
|
|||||||
|
|
||||||
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
|
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
|
||||||
|
|
||||||
COPY . /src
|
COPY --chown=docker:docker . /src/
|
||||||
RUN chown -R docker:docker /src
|
|
||||||
|
|
||||||
USER docker
|
USER docker
|
||||||
ENTRYPOINT ["/src/builder/build.sh"]
|
ENTRYPOINT ["/src/builder/build.sh"]
|
||||||
|
|||||||
@@ -62,14 +62,16 @@ 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/vncserver.1 $DST_MAN;
|
||||||
cp $SRC/share/man/man1/vncconfig.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/vncpasswd.1 $DST_MAN;
|
||||||
|
cp $SRC/share/man/man1/kasmxpoxy.1 $DST_MAN;
|
||||||
cd $DST_MAN && ln -s vncpasswd.1 kasmvncpasswd.1;
|
cd $DST_MAN && ln -s vncpasswd.1 kasmvncpasswd.1;
|
||||||
|
|
||||||
|
|
||||||
%files
|
%files
|
||||||
/usr/bin/*
|
/usr/bin/*
|
||||||
/usr/share/man/man1/*
|
/usr/share/man/man1/*
|
||||||
/usr/share/kasmvnc/www
|
/usr/share/kasmvnc/www
|
||||||
|
|
||||||
%license /usr/share/doc/kasmvncserver/LICENCE.TXT
|
%license /usr/share/doc/kasmvncserver/LICENSE.TXT
|
||||||
%doc /usr/share/doc/kasmvncserver/README.md
|
%doc /usr/share/doc/kasmvncserver/README.md
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
|||||||
@@ -76,5 +76,5 @@ endif() #UNIX
|
|||||||
# Common
|
# Common
|
||||||
#
|
#
|
||||||
|
|
||||||
install(FILES ${CMAKE_SOURCE_DIR}/LICENCE.TXT DESTINATION ${DOC_DIR})
|
install(FILES ${CMAKE_SOURCE_DIR}/LICENSE.TXT DESTINATION ${DOC_DIR})
|
||||||
install(FILES ${CMAKE_SOURCE_DIR}/README.md DESTINATION ${DOC_DIR})
|
install(FILES ${CMAKE_SOURCE_DIR}/README.md DESTINATION ${DOC_DIR})
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
#include <rfb/PixelBuffer.h>
|
#include <rfb/PixelBuffer.h>
|
||||||
#include <rfb/PixelFormat.h>
|
#include <rfb/PixelFormat.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace network {
|
namespace network {
|
||||||
@@ -34,6 +36,17 @@ namespace network {
|
|||||||
|
|
||||||
// from main thread
|
// from main thread
|
||||||
void mainUpdateScreen(rfb::PixelBuffer *pb);
|
void mainUpdateScreen(rfb::PixelBuffer *pb);
|
||||||
|
void mainUpdateBottleneckStats(const char userid[], const char stats[]);
|
||||||
|
void mainClearBottleneckStats(const char userid[]);
|
||||||
|
void mainUpdateServerFrameStats(uint8_t changedPerc, uint32_t all,
|
||||||
|
uint32_t jpeg, uint32_t webp, uint32_t analysis,
|
||||||
|
uint32_t jpegarea, uint32_t webparea,
|
||||||
|
uint16_t njpeg, uint16_t nwebp,
|
||||||
|
uint16_t enc, uint16_t scale, uint16_t shot,
|
||||||
|
uint16_t w, uint16_t h);
|
||||||
|
void mainUpdateClientFrameStats(const char userid[], uint32_t render, uint32_t all,
|
||||||
|
uint32_t ping);
|
||||||
|
void mainUpdateUserInfo(const uint8_t ownerConn, const uint8_t numUsers);
|
||||||
|
|
||||||
// from network threads
|
// from network threads
|
||||||
uint8_t *netGetScreenshot(uint16_t w, uint16_t h,
|
uint8_t *netGetScreenshot(uint16_t w, uint16_t h,
|
||||||
@@ -42,13 +55,25 @@ namespace network {
|
|||||||
uint8_t netAddUser(const char name[], const char pw[], const bool write);
|
uint8_t netAddUser(const char name[], const char pw[], const bool write);
|
||||||
uint8_t netRemoveUser(const char name[]);
|
uint8_t netRemoveUser(const char name[]);
|
||||||
uint8_t netGiveControlTo(const char name[]);
|
uint8_t netGiveControlTo(const char name[]);
|
||||||
|
void netGetBottleneckStats(char *buf, uint32_t len);
|
||||||
|
void netGetFrameStats(char *buf, uint32_t len);
|
||||||
|
uint8_t netServerFrameStatsReady();
|
||||||
|
|
||||||
enum USER_ACTION {
|
enum USER_ACTION {
|
||||||
//USER_ADD, - handled locally for interactivity
|
//USER_ADD, - handled locally for interactivity
|
||||||
USER_REMOVE,
|
USER_REMOVE,
|
||||||
USER_GIVE_CONTROL,
|
USER_GIVE_CONTROL,
|
||||||
|
WANT_FRAME_STATS_SERVERONLY,
|
||||||
|
WANT_FRAME_STATS_ALL,
|
||||||
|
WANT_FRAME_STATS_OWNER,
|
||||||
|
WANT_FRAME_STATS_SPECIFIC,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
uint8_t netRequestFrameStats(USER_ACTION what, const char *client);
|
||||||
|
uint8_t netOwnerConnected();
|
||||||
|
uint8_t netNumActiveUsers();
|
||||||
|
uint8_t netGetClientFrameStatsNum();
|
||||||
|
|
||||||
struct action_data {
|
struct action_data {
|
||||||
enum USER_ACTION action;
|
enum USER_ACTION action;
|
||||||
kasmpasswd_entry_t data;
|
kasmpasswd_entry_t data;
|
||||||
@@ -68,6 +93,40 @@ namespace network {
|
|||||||
std::vector<uint8_t> cachedJpeg;
|
std::vector<uint8_t> cachedJpeg;
|
||||||
uint16_t cachedW, cachedH;
|
uint16_t cachedW, cachedH;
|
||||||
uint8_t cachedQ;
|
uint8_t cachedQ;
|
||||||
|
|
||||||
|
std::map<std::string, std::string> bottleneckStats;
|
||||||
|
pthread_mutex_t statMutex;
|
||||||
|
|
||||||
|
struct clientFrameStats_t {
|
||||||
|
uint32_t render;
|
||||||
|
uint32_t all;
|
||||||
|
uint32_t ping;
|
||||||
|
};
|
||||||
|
struct serverFrameStats_t {
|
||||||
|
uint32_t all;
|
||||||
|
uint32_t jpeg;
|
||||||
|
uint32_t webp;
|
||||||
|
uint32_t analysis;
|
||||||
|
uint32_t jpegarea;
|
||||||
|
uint32_t webparea;
|
||||||
|
uint16_t njpeg;
|
||||||
|
uint16_t nwebp;
|
||||||
|
uint16_t enc;
|
||||||
|
uint16_t scale;
|
||||||
|
uint16_t shot;
|
||||||
|
uint16_t w;
|
||||||
|
uint16_t h;
|
||||||
|
uint8_t changedPerc;
|
||||||
|
|
||||||
|
uint8_t inprogress;
|
||||||
|
};
|
||||||
|
std::map<std::string, clientFrameStats_t> clientFrameStats;
|
||||||
|
serverFrameStats_t serverFrameStats;
|
||||||
|
pthread_mutex_t frameStatMutex;
|
||||||
|
|
||||||
|
uint8_t ownerConnected;
|
||||||
|
uint8_t activeUsers;
|
||||||
|
pthread_mutex_t userInfoMutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,10 +56,16 @@ static const struct TightJPEGConfiguration conf[10] = {
|
|||||||
|
|
||||||
GetAPIMessager::GetAPIMessager(const char *passwdfile_): passwdfile(passwdfile_),
|
GetAPIMessager::GetAPIMessager(const char *passwdfile_): passwdfile(passwdfile_),
|
||||||
screenW(0), screenH(0), screenHash(0),
|
screenW(0), screenH(0), screenHash(0),
|
||||||
cachedW(0), cachedH(0), cachedQ(0) {
|
cachedW(0), cachedH(0), cachedQ(0),
|
||||||
|
ownerConnected(0), activeUsers(0) {
|
||||||
|
|
||||||
pthread_mutex_init(&screenMutex, NULL);
|
pthread_mutex_init(&screenMutex, NULL);
|
||||||
pthread_mutex_init(&userMutex, NULL);
|
pthread_mutex_init(&userMutex, NULL);
|
||||||
|
pthread_mutex_init(&statMutex, NULL);
|
||||||
|
pthread_mutex_init(&frameStatMutex, NULL);
|
||||||
|
pthread_mutex_init(&userInfoMutex, NULL);
|
||||||
|
|
||||||
|
serverFrameStats.inprogress = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// from main thread
|
// from main thread
|
||||||
@@ -95,6 +101,78 @@ void GetAPIMessager::mainUpdateScreen(rfb::PixelBuffer *pb) {
|
|||||||
pthread_mutex_unlock(&screenMutex);
|
pthread_mutex_unlock(&screenMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GetAPIMessager::mainUpdateBottleneckStats(const char userid[], const char stats[]) {
|
||||||
|
if (pthread_mutex_trylock(&statMutex))
|
||||||
|
return;
|
||||||
|
|
||||||
|
bottleneckStats[userid] = stats;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&statMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetAPIMessager::mainClearBottleneckStats(const char userid[]) {
|
||||||
|
if (pthread_mutex_lock(&statMutex))
|
||||||
|
return;
|
||||||
|
|
||||||
|
bottleneckStats.erase(userid);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&statMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetAPIMessager::mainUpdateServerFrameStats(uint8_t changedPerc,
|
||||||
|
uint32_t all, uint32_t jpeg, uint32_t webp, uint32_t analysis,
|
||||||
|
uint32_t jpegarea, uint32_t webparea,
|
||||||
|
uint16_t njpeg, uint16_t nwebp,
|
||||||
|
uint16_t enc, uint16_t scale, uint16_t shot,
|
||||||
|
uint16_t w, uint16_t h) {
|
||||||
|
|
||||||
|
if (pthread_mutex_lock(&frameStatMutex))
|
||||||
|
return;
|
||||||
|
|
||||||
|
serverFrameStats.changedPerc = changedPerc;
|
||||||
|
serverFrameStats.all = all;
|
||||||
|
serverFrameStats.jpeg = jpeg;
|
||||||
|
serverFrameStats.webp = webp;
|
||||||
|
serverFrameStats.analysis = analysis;
|
||||||
|
serverFrameStats.jpegarea = jpegarea;
|
||||||
|
serverFrameStats.webparea = webparea;
|
||||||
|
serverFrameStats.njpeg = njpeg;
|
||||||
|
serverFrameStats.nwebp = nwebp;
|
||||||
|
serverFrameStats.enc = enc;
|
||||||
|
serverFrameStats.scale = scale;
|
||||||
|
serverFrameStats.shot = shot;
|
||||||
|
serverFrameStats.w = w;
|
||||||
|
serverFrameStats.h = h;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&frameStatMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetAPIMessager::mainUpdateClientFrameStats(const char userid[], uint32_t render,
|
||||||
|
uint32_t all, uint32_t ping) {
|
||||||
|
|
||||||
|
if (pthread_mutex_lock(&frameStatMutex))
|
||||||
|
return;
|
||||||
|
|
||||||
|
clientFrameStats_t s;
|
||||||
|
s.render = render;
|
||||||
|
s.all = all;
|
||||||
|
s.ping = ping;
|
||||||
|
|
||||||
|
clientFrameStats[userid] = s;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&frameStatMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetAPIMessager::mainUpdateUserInfo(const uint8_t ownerConn, const uint8_t numUsers) {
|
||||||
|
if (pthread_mutex_lock(&userInfoMutex))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ownerConnected = ownerConn;
|
||||||
|
activeUsers = numUsers;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&userInfoMutex);
|
||||||
|
}
|
||||||
|
|
||||||
// from network threads
|
// from network threads
|
||||||
uint8_t *GetAPIMessager::netGetScreenshot(uint16_t w, uint16_t h,
|
uint8_t *GetAPIMessager::netGetScreenshot(uint16_t w, uint16_t h,
|
||||||
const uint8_t q, const bool dedup,
|
const uint8_t q, const bool dedup,
|
||||||
@@ -286,3 +364,271 @@ uint8_t GetAPIMessager::netGiveControlTo(const char name[]) {
|
|||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GetAPIMessager::netGetBottleneckStats(char *buf, uint32_t len) {
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
"username.1": {
|
||||||
|
"192.168.100.2:14908": [ 100, 100, 100, 100 ],
|
||||||
|
"192.168.100.3:14918": [ 100, 100, 100, 100 ]
|
||||||
|
},
|
||||||
|
"username.2": {
|
||||||
|
"192.168.100.5:14904": [ 100, 100, 100, 100 ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
std::map<std::string, std::string>::const_iterator it;
|
||||||
|
const char *prev = NULL;
|
||||||
|
FILE *f;
|
||||||
|
|
||||||
|
if (pthread_mutex_lock(&statMutex)) {
|
||||||
|
buf[0] = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conservative estimate
|
||||||
|
if (len < bottleneckStats.size() * 60) {
|
||||||
|
buf[0] = 0;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
f = fmemopen(buf, len, "w");
|
||||||
|
|
||||||
|
fprintf(f, "{\n");
|
||||||
|
|
||||||
|
for (it = bottleneckStats.begin(); it != bottleneckStats.end(); it++) {
|
||||||
|
// user@127.0.0.1_1627311208.791752::websocket
|
||||||
|
const char *id = it->first.c_str();
|
||||||
|
const char *data = it->second.c_str();
|
||||||
|
|
||||||
|
const char *at = strchr(id, '@');
|
||||||
|
if (!at)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const unsigned userlen = at - id;
|
||||||
|
if (prev && !strncmp(prev, id, userlen)) {
|
||||||
|
// Same user
|
||||||
|
fprintf(f, ",\n\t\t\"%s\": %s", at + 1, data);
|
||||||
|
} else {
|
||||||
|
// New one
|
||||||
|
if (prev) {
|
||||||
|
fprintf(f, "\n\t},\n");
|
||||||
|
}
|
||||||
|
fprintf(f, "\t\"%.*s\": {\n", userlen, id);
|
||||||
|
fprintf(f, "\t\t\"%s\": %s", at + 1, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bottleneckStats.size())
|
||||||
|
fprintf(f, "}\n");
|
||||||
|
else
|
||||||
|
fprintf(f, "\n\t}\n}\n");
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
out:
|
||||||
|
pthread_mutex_unlock(&statMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetAPIMessager::netGetFrameStats(char *buf, uint32_t len) {
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
"frame" : {
|
||||||
|
"resx": 1024,
|
||||||
|
"resy": 1280,
|
||||||
|
"changed": 75,
|
||||||
|
"server_time": 23
|
||||||
|
},
|
||||||
|
"server_side" : [
|
||||||
|
{ "process_name": "Analysis", "time": 20 },
|
||||||
|
{ "process_name": "TightWEBPEncoder", "time": 20, "count": 64, "area": 12 },
|
||||||
|
{ "process_name": "TightJPEGEncoder", "time": 20, "count": 64, "area": 12 }
|
||||||
|
],
|
||||||
|
"client_side" : [
|
||||||
|
{
|
||||||
|
"client": "123.1.2.1:1211",
|
||||||
|
"client_time": 20,
|
||||||
|
"ping": 20,
|
||||||
|
"processes" : [
|
||||||
|
{ "process_name": "scanRenderQ", "time": 20 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
std::map<std::string, clientFrameStats_t>::const_iterator it;
|
||||||
|
unsigned i = 0;
|
||||||
|
FILE *f;
|
||||||
|
|
||||||
|
if (pthread_mutex_lock(&frameStatMutex)) {
|
||||||
|
buf[0] = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned num = clientFrameStats.size();
|
||||||
|
|
||||||
|
// Conservative estimate
|
||||||
|
if (len < 1024) {
|
||||||
|
buf[0] = 0;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
f = fmemopen(buf, len, "w");
|
||||||
|
|
||||||
|
fprintf(f, "{\n");
|
||||||
|
|
||||||
|
fprintf(f, "\t\"frame\" : {\n"
|
||||||
|
"\t\t\"resx\": %u,\n"
|
||||||
|
"\t\t\"resy\": %u,\n"
|
||||||
|
"\t\t\"changed\": %u,\n"
|
||||||
|
"\t\t\"server_time\": %u\n"
|
||||||
|
"\t},\n",
|
||||||
|
serverFrameStats.w,
|
||||||
|
serverFrameStats.h,
|
||||||
|
serverFrameStats.changedPerc,
|
||||||
|
serverFrameStats.all);
|
||||||
|
|
||||||
|
fprintf(f, "\t\"server_side\" : [\n"
|
||||||
|
"\t\t{ \"process_name\": \"Analysis\", \"time\": %u },\n"
|
||||||
|
"\t\t{ \"process_name\": \"Screenshot\", \"time\": %u },\n"
|
||||||
|
"\t\t{ \"process_name\": \"Encoding_total\", \"time\": %u, \"videoscaling\": %u },\n"
|
||||||
|
"\t\t{ \"process_name\": \"TightJPEGEncoder\", \"time\": %u, \"count\": %u, \"area\": %u },\n"
|
||||||
|
"\t\t{ \"process_name\": \"TightWEBPEncoder\", \"time\": %u, \"count\": %u, \"area\": %u }\n"
|
||||||
|
"\t],\n",
|
||||||
|
serverFrameStats.analysis,
|
||||||
|
serverFrameStats.shot,
|
||||||
|
serverFrameStats.enc,
|
||||||
|
serverFrameStats.scale,
|
||||||
|
serverFrameStats.jpeg,
|
||||||
|
serverFrameStats.njpeg,
|
||||||
|
serverFrameStats.jpegarea,
|
||||||
|
serverFrameStats.webp,
|
||||||
|
serverFrameStats.nwebp,
|
||||||
|
serverFrameStats.webparea);
|
||||||
|
|
||||||
|
fprintf(f, "\t\"client_side\" : [\n");
|
||||||
|
|
||||||
|
for (it = clientFrameStats.begin(); it != clientFrameStats.end(); it++, i++) {
|
||||||
|
const char *id = it->first.c_str();
|
||||||
|
const clientFrameStats_t &s = it->second;
|
||||||
|
|
||||||
|
fprintf(f, "\t\t\{\n"
|
||||||
|
"\t\t\t\"client\": \"%s\",\n"
|
||||||
|
"\t\t\t\"client_time\": %u,\n"
|
||||||
|
"\t\t\t\"ping\": %u,\n"
|
||||||
|
"\t\t\t\"processes\" : [\n"
|
||||||
|
"\t\t\t\t{ \"process_name\": \"scanRenderQ\", \"time\": %u }\n"
|
||||||
|
"\t\t\t]\n"
|
||||||
|
"\t\t}",
|
||||||
|
id,
|
||||||
|
s.all,
|
||||||
|
s.ping,
|
||||||
|
s.render);
|
||||||
|
|
||||||
|
if (i == num - 1)
|
||||||
|
fprintf(f, "\n");
|
||||||
|
else
|
||||||
|
fprintf(f, ",\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(f, "\t]\n}\n");
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
serverFrameStats.inprogress = 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
pthread_mutex_unlock(&frameStatMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t GetAPIMessager::netRequestFrameStats(USER_ACTION what, const char *client) {
|
||||||
|
// Return 1 for success
|
||||||
|
action_data act;
|
||||||
|
act.action = what;
|
||||||
|
if (client) {
|
||||||
|
strncpy(act.data.password, client, PASSWORD_LEN);
|
||||||
|
act.data.password[PASSWORD_LEN - 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// In progress already?
|
||||||
|
bool fail = false;
|
||||||
|
if (pthread_mutex_lock(&frameStatMutex))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (serverFrameStats.inprogress) {
|
||||||
|
fail = true;
|
||||||
|
vlog.error("Frame stats request already in progress, refusing another");
|
||||||
|
} else {
|
||||||
|
clientFrameStats.clear();
|
||||||
|
memset(&serverFrameStats, 0, sizeof(serverFrameStats_t));
|
||||||
|
serverFrameStats.inprogress = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&frameStatMutex);
|
||||||
|
if (fail)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Send it in
|
||||||
|
if (pthread_mutex_lock(&userMutex))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
actionQueue.push_back(act);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&userMutex);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t GetAPIMessager::netOwnerConnected() {
|
||||||
|
uint8_t ret;
|
||||||
|
|
||||||
|
if (pthread_mutex_lock(&userInfoMutex))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = ownerConnected;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&userInfoMutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t GetAPIMessager::netNumActiveUsers() {
|
||||||
|
uint8_t ret;
|
||||||
|
|
||||||
|
if (pthread_mutex_lock(&userInfoMutex))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = activeUsers;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&userInfoMutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t GetAPIMessager::netGetClientFrameStatsNum() {
|
||||||
|
uint8_t ret;
|
||||||
|
|
||||||
|
if (pthread_mutex_lock(&frameStatMutex))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = clientFrameStats.size();
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&frameStatMutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t GetAPIMessager::netServerFrameStatsReady() {
|
||||||
|
uint8_t ret;
|
||||||
|
|
||||||
|
if (pthread_mutex_lock(&frameStatMutex))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = serverFrameStats.w != 0;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&frameStatMutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,6 +25,9 @@
|
|||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <ws2tcpip.h>
|
#include <ws2tcpip.h>
|
||||||
#define errorNumber WSAGetLastError()
|
#define errorNumber WSAGetLastError()
|
||||||
|
#define SHUT_RD SD_RECEIVE
|
||||||
|
#define SHUT_WR SD_SEND
|
||||||
|
#define SHUT_RDWR SD_BOTH
|
||||||
#else
|
#else
|
||||||
#define errorNumber errno
|
#define errorNumber errno
|
||||||
#define closesocket close
|
#define closesocket close
|
||||||
@@ -94,7 +97,7 @@ Socket::~Socket()
|
|||||||
void Socket::shutdown()
|
void Socket::shutdown()
|
||||||
{
|
{
|
||||||
isShutdown_ = true;
|
isShutdown_ = true;
|
||||||
::shutdown(getFd(), 2);
|
::shutdown(getFd(), SHUT_RDWR);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Socket::isShutdown() const
|
bool Socket::isShutdown() const
|
||||||
@@ -149,7 +152,7 @@ void SocketListener::shutdown()
|
|||||||
closesocket(fd);
|
closesocket(fd);
|
||||||
fd = -1;
|
fd = -1;
|
||||||
#else
|
#else
|
||||||
::shutdown(fd, 2);
|
::shutdown(fd, SHUT_RDWR);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,8 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <wordexp.h>
|
#include <wordexp.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include "websocket.h"
|
#include "websocket.h"
|
||||||
|
|
||||||
#include <network/GetAPI.h>
|
#include <network/GetAPI.h>
|
||||||
@@ -459,6 +461,67 @@ static uint8_t givecontrolCb(void *messager, const char name[])
|
|||||||
return msgr->netGiveControlTo(name);
|
return msgr->netGiveControlTo(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void bottleneckStatsCb(void *messager, char *buf, uint32_t len)
|
||||||
|
{
|
||||||
|
GetAPIMessager *msgr = (GetAPIMessager *) messager;
|
||||||
|
msgr->netGetBottleneckStats(buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void frameStatsCb(void *messager, char *buf, uint32_t len)
|
||||||
|
{
|
||||||
|
GetAPIMessager *msgr = (GetAPIMessager *) messager;
|
||||||
|
msgr->netGetFrameStats(buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t requestFrameStatsNoneCb(void *messager)
|
||||||
|
{
|
||||||
|
GetAPIMessager *msgr = (GetAPIMessager *) messager;
|
||||||
|
return msgr->netRequestFrameStats(GetAPIMessager::WANT_FRAME_STATS_SERVERONLY, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t requestFrameStatsOwnerCb(void *messager)
|
||||||
|
{
|
||||||
|
GetAPIMessager *msgr = (GetAPIMessager *) messager;
|
||||||
|
return msgr->netRequestFrameStats(GetAPIMessager::WANT_FRAME_STATS_OWNER, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t requestFrameStatsAllCb(void *messager)
|
||||||
|
{
|
||||||
|
GetAPIMessager *msgr = (GetAPIMessager *) messager;
|
||||||
|
return msgr->netRequestFrameStats(GetAPIMessager::WANT_FRAME_STATS_ALL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t requestFrameStatsOneCb(void *messager, const char *client)
|
||||||
|
{
|
||||||
|
GetAPIMessager *msgr = (GetAPIMessager *) messager;
|
||||||
|
return msgr->netRequestFrameStats(GetAPIMessager::WANT_FRAME_STATS_SPECIFIC, client);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t ownerConnectedCb(void *messager)
|
||||||
|
{
|
||||||
|
GetAPIMessager *msgr = (GetAPIMessager *) messager;
|
||||||
|
return msgr->netOwnerConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t numActiveUsersCb(void *messager)
|
||||||
|
{
|
||||||
|
GetAPIMessager *msgr = (GetAPIMessager *) messager;
|
||||||
|
return msgr->netNumActiveUsers();
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t getClientFrameStatsNumCb(void *messager)
|
||||||
|
{
|
||||||
|
GetAPIMessager *msgr = (GetAPIMessager *) messager;
|
||||||
|
return msgr->netGetClientFrameStatsNum();
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t serverFrameStatsReadyCb(void *messager)
|
||||||
|
{
|
||||||
|
GetAPIMessager *msgr = (GetAPIMessager *) messager;
|
||||||
|
return msgr->netServerFrameStatsReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
WebsocketListener::WebsocketListener(const struct sockaddr *listenaddr,
|
WebsocketListener::WebsocketListener(const struct sockaddr *listenaddr,
|
||||||
socklen_t listenaddrlen,
|
socklen_t listenaddrlen,
|
||||||
bool sslonly, const char *cert, const char *certkey,
|
bool sslonly, const char *cert, const char *certkey,
|
||||||
@@ -503,7 +566,7 @@ WebsocketListener::WebsocketListener(const struct sockaddr *listenaddr,
|
|||||||
if (bind(sock, &sa.u.sa, listenaddrlen) == -1) {
|
if (bind(sock, &sa.u.sa, listenaddrlen) == -1) {
|
||||||
int e = errorNumber;
|
int e = errorNumber;
|
||||||
closesocket(sock);
|
closesocket(sock);
|
||||||
throw SocketException("failed to bind socket", e);
|
throw SocketException("failed to bind socket, is someone else on our -websocketPort?", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
listen(sock); // sets the internal fd
|
listen(sock); // sets the internal fd
|
||||||
@@ -513,13 +576,16 @@ WebsocketListener::WebsocketListener(const struct sockaddr *listenaddr,
|
|||||||
//
|
//
|
||||||
internalSocket = socket(AF_UNIX, SOCK_STREAM, 0);
|
internalSocket = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
|
||||||
|
char sockname[32];
|
||||||
|
sprintf(sockname, ".KasmVNCSock%u", getpid());
|
||||||
|
|
||||||
struct sockaddr_un addr;
|
struct sockaddr_un addr;
|
||||||
addr.sun_family = AF_UNIX;
|
addr.sun_family = AF_UNIX;
|
||||||
strcpy(addr.sun_path, ".KasmVNCSock");
|
strcpy(addr.sun_path, sockname);
|
||||||
addr.sun_path[0] = '\0';
|
addr.sun_path[0] = '\0';
|
||||||
|
|
||||||
if (bind(internalSocket, (struct sockaddr *) &addr,
|
if (bind(internalSocket, (struct sockaddr *) &addr,
|
||||||
sizeof(sa_family_t) + sizeof(".KasmVNCSock"))) {
|
sizeof(sa_family_t) + strlen(sockname))) {
|
||||||
throw SocketException("failed to bind socket", errorNumber);
|
throw SocketException("failed to bind socket", errorNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -548,6 +614,18 @@ WebsocketListener::WebsocketListener(const struct sockaddr *listenaddr,
|
|||||||
settings.adduserCb = adduserCb;
|
settings.adduserCb = adduserCb;
|
||||||
settings.removeCb = removeCb;
|
settings.removeCb = removeCb;
|
||||||
settings.givecontrolCb = givecontrolCb;
|
settings.givecontrolCb = givecontrolCb;
|
||||||
|
settings.bottleneckStatsCb = bottleneckStatsCb;
|
||||||
|
settings.frameStatsCb = frameStatsCb;
|
||||||
|
|
||||||
|
settings.requestFrameStatsNoneCb = requestFrameStatsNoneCb;
|
||||||
|
settings.requestFrameStatsOwnerCb = requestFrameStatsOwnerCb;
|
||||||
|
settings.requestFrameStatsAllCb = requestFrameStatsAllCb;
|
||||||
|
settings.requestFrameStatsOneCb = requestFrameStatsOneCb;
|
||||||
|
|
||||||
|
settings.ownerConnectedCb = ownerConnectedCb;
|
||||||
|
settings.numActiveUsersCb = numActiveUsersCb;
|
||||||
|
settings.getClientFrameStatsNumCb = getClientFrameStatsNumCb;
|
||||||
|
settings.serverFrameStatsReadyCb = serverFrameStatsReadyCb;
|
||||||
|
|
||||||
pthread_t tid;
|
pthread_t tid;
|
||||||
pthread_create(&tid, NULL, start_server, NULL);
|
pthread_create(&tid, NULL, start_server, NULL);
|
||||||
|
|||||||
@@ -566,7 +566,7 @@ int parse_handshake(ws_ctx_t *ws_ctx, char *handshake) {
|
|||||||
headers->key3[0] = '\0';
|
headers->key3[0] = '\0';
|
||||||
|
|
||||||
if ((strlen(handshake) < 92) || (bcmp(handshake, "GET ", 4) != 0) ||
|
if ((strlen(handshake) < 92) || (bcmp(handshake, "GET ", 4) != 0) ||
|
||||||
(!strstr(handshake, "Upgrade: websocket"))) {
|
(!strcasestr(handshake, "Upgrade: websocket"))) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
start = handshake+4;
|
start = handshake+4;
|
||||||
@@ -583,11 +583,11 @@ int parse_handshake(ws_ctx_t *ws_ctx, char *handshake) {
|
|||||||
headers->host[end-start] = '\0';
|
headers->host[end-start] = '\0';
|
||||||
|
|
||||||
headers->origin[0] = '\0';
|
headers->origin[0] = '\0';
|
||||||
start = strstr(handshake, "\r\nOrigin: ");
|
start = strcasestr(handshake, "\r\nOrigin: ");
|
||||||
if (start) {
|
if (start) {
|
||||||
start += 10;
|
start += 10;
|
||||||
} else {
|
} else {
|
||||||
start = strstr(handshake, "\r\nSec-WebSocket-Origin: ");
|
start = strcasestr(handshake, "\r\nSec-WebSocket-Origin: ");
|
||||||
if (!start) { return 0; }
|
if (!start) { return 0; }
|
||||||
start += 24;
|
start += 24;
|
||||||
}
|
}
|
||||||
@@ -595,7 +595,7 @@ int parse_handshake(ws_ctx_t *ws_ctx, char *handshake) {
|
|||||||
strncpy(headers->origin, start, end-start);
|
strncpy(headers->origin, start, end-start);
|
||||||
headers->origin[end-start] = '\0';
|
headers->origin[end-start] = '\0';
|
||||||
|
|
||||||
start = strstr(handshake, "\r\nSec-WebSocket-Version: ");
|
start = strcasestr(handshake, "\r\nSec-WebSocket-Version: ");
|
||||||
if (start) {
|
if (start) {
|
||||||
// HyBi/RFC 6455
|
// HyBi/RFC 6455
|
||||||
start += 25;
|
start += 25;
|
||||||
@@ -605,7 +605,7 @@ int parse_handshake(ws_ctx_t *ws_ctx, char *handshake) {
|
|||||||
ws_ctx->hixie = 0;
|
ws_ctx->hixie = 0;
|
||||||
ws_ctx->hybi = strtol(headers->version, NULL, 10);
|
ws_ctx->hybi = strtol(headers->version, NULL, 10);
|
||||||
|
|
||||||
start = strstr(handshake, "\r\nSec-WebSocket-Key: ");
|
start = strcasestr(handshake, "\r\nSec-WebSocket-Key: ");
|
||||||
if (!start) { return 0; }
|
if (!start) { return 0; }
|
||||||
start += 21;
|
start += 21;
|
||||||
end = strstr(start, "\r\n");
|
end = strstr(start, "\r\n");
|
||||||
@@ -619,7 +619,7 @@ int parse_handshake(ws_ctx_t *ws_ctx, char *handshake) {
|
|||||||
strncpy(headers->connection, start, end-start);
|
strncpy(headers->connection, start, end-start);
|
||||||
headers->connection[end-start] = '\0';
|
headers->connection[end-start] = '\0';
|
||||||
|
|
||||||
start = strstr(handshake, "\r\nSec-WebSocket-Protocol: ");
|
start = strcasestr(handshake, "\r\nSec-WebSocket-Protocol: ");
|
||||||
if (!start) { return 0; }
|
if (!start) { return 0; }
|
||||||
start += 26;
|
start += 26;
|
||||||
end = strstr(start, "\r\n");
|
end = strstr(start, "\r\n");
|
||||||
@@ -637,14 +637,14 @@ int parse_handshake(ws_ctx_t *ws_ctx, char *handshake) {
|
|||||||
strncpy(headers->key3, start, 8);
|
strncpy(headers->key3, start, 8);
|
||||||
headers->key3[8] = '\0';
|
headers->key3[8] = '\0';
|
||||||
|
|
||||||
start = strstr(handshake, "\r\nSec-WebSocket-Key1: ");
|
start = strcasestr(handshake, "\r\nSec-WebSocket-Key1: ");
|
||||||
if (!start) { return 0; }
|
if (!start) { return 0; }
|
||||||
start += 22;
|
start += 22;
|
||||||
end = strstr(start, "\r\n");
|
end = strstr(start, "\r\n");
|
||||||
strncpy(headers->key1, start, end-start);
|
strncpy(headers->key1, start, end-start);
|
||||||
headers->key1[end-start] = '\0';
|
headers->key1[end-start] = '\0';
|
||||||
|
|
||||||
start = strstr(handshake, "\r\nSec-WebSocket-Key2: ");
|
start = strcasestr(handshake, "\r\nSec-WebSocket-Key2: ");
|
||||||
if (!start) { return 0; }
|
if (!start) { return 0; }
|
||||||
start += 22;
|
start += 22;
|
||||||
end = strstr(start, "\r\n");
|
end = strstr(start, "\r\n");
|
||||||
@@ -779,7 +779,13 @@ 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, ".."))
|
if (!strcmp(names[i]->d_name, ".") || !strcmp(names[i]->d_name, ".."))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
sprintf(buf, "<li><a href=\"%s\">%s</a></li>", names[i]->d_name, names[i]->d_name);
|
if (names[i]->d_type == DT_DIR)
|
||||||
|
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>", names[i]->d_name,
|
||||||
|
names[i]->d_name);
|
||||||
|
|
||||||
ws_send(ws_ctx, buf, strlen(buf));
|
ws_send(ws_ctx, buf, strlen(buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1068,6 +1074,89 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) {
|
|||||||
|
|
||||||
wserr("Passed give_control request to main thread\n");
|
wserr("Passed give_control request to main thread\n");
|
||||||
ret = 1;
|
ret = 1;
|
||||||
|
} else entry("/api/get_bottleneck_stats") {
|
||||||
|
char statbuf[4096];
|
||||||
|
settings.bottleneckStatsCb(settings.messager, statbuf, 4096);
|
||||||
|
|
||||||
|
sprintf(buf, "HTTP/1.1 200 OK\r\n"
|
||||||
|
"Server: KasmVNC/4.0\r\n"
|
||||||
|
"Connection: close\r\n"
|
||||||
|
"Content-type: text/plain\r\n"
|
||||||
|
"Content-length: %lu\r\n"
|
||||||
|
"\r\n", strlen(statbuf));
|
||||||
|
ws_send(ws_ctx, buf, strlen(buf));
|
||||||
|
ws_send(ws_ctx, statbuf, strlen(statbuf));
|
||||||
|
|
||||||
|
wserr("Sent bottleneck stats to API caller\n");
|
||||||
|
ret = 1;
|
||||||
|
} else entry("/api/get_frame_stats") {
|
||||||
|
char statbuf[4096], decname[1024];
|
||||||
|
unsigned waitfor;
|
||||||
|
|
||||||
|
param = parse_get(args, "client", &len);
|
||||||
|
if (len) {
|
||||||
|
memcpy(buf, param, len);
|
||||||
|
buf[len] = '\0';
|
||||||
|
percent_decode(buf, decname);
|
||||||
|
} else {
|
||||||
|
wserr("client param required\n");
|
||||||
|
goto nope;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!decname[0])
|
||||||
|
goto nope;
|
||||||
|
|
||||||
|
if (!strcmp(decname, "none")) {
|
||||||
|
waitfor = 0;
|
||||||
|
if (!settings.requestFrameStatsNoneCb(settings.messager))
|
||||||
|
goto nope;
|
||||||
|
} else if (!strcmp(decname, "auto")) {
|
||||||
|
waitfor = settings.ownerConnectedCb(settings.messager);
|
||||||
|
if (!waitfor) {
|
||||||
|
if (!settings.requestFrameStatsNoneCb(settings.messager))
|
||||||
|
goto nope;
|
||||||
|
} else {
|
||||||
|
if (!settings.requestFrameStatsOwnerCb(settings.messager))
|
||||||
|
goto nope;
|
||||||
|
}
|
||||||
|
} else if (!strcmp(decname, "all")) {
|
||||||
|
waitfor = settings.numActiveUsersCb(settings.messager);
|
||||||
|
if (!settings.requestFrameStatsAllCb(settings.messager))
|
||||||
|
goto nope;
|
||||||
|
} else {
|
||||||
|
waitfor = 1;
|
||||||
|
if (!settings.requestFrameStatsOneCb(settings.messager, decname))
|
||||||
|
goto nope;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
usleep(10 * 1000);
|
||||||
|
if (settings.serverFrameStatsReadyCb(settings.messager))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (waitfor) {
|
||||||
|
unsigned waits;
|
||||||
|
for (waits = 0; waits < 20; waits++) { // wait up to 2s
|
||||||
|
if (settings.getClientFrameStatsNumCb(settings.messager) >= waitfor)
|
||||||
|
break;
|
||||||
|
usleep(100 * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.frameStatsCb(settings.messager, statbuf, 4096);
|
||||||
|
|
||||||
|
sprintf(buf, "HTTP/1.1 200 OK\r\n"
|
||||||
|
"Server: KasmVNC/4.0\r\n"
|
||||||
|
"Connection: close\r\n"
|
||||||
|
"Content-type: text/plain\r\n"
|
||||||
|
"Content-length: %lu\r\n"
|
||||||
|
"\r\n", strlen(statbuf));
|
||||||
|
ws_send(ws_ctx, buf, strlen(buf));
|
||||||
|
ws_send(ws_ctx, statbuf, strlen(statbuf));
|
||||||
|
|
||||||
|
wserr("Sent frame stats to API caller\n");
|
||||||
|
ret = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef entry
|
#undef entry
|
||||||
|
|||||||
@@ -84,6 +84,18 @@ typedef struct {
|
|||||||
const uint8_t write);
|
const uint8_t write);
|
||||||
uint8_t (*removeCb)(void *messager, const char name[]);
|
uint8_t (*removeCb)(void *messager, const char name[]);
|
||||||
uint8_t (*givecontrolCb)(void *messager, const char name[]);
|
uint8_t (*givecontrolCb)(void *messager, const char name[]);
|
||||||
|
void (*bottleneckStatsCb)(void *messager, char *buf, uint32_t len);
|
||||||
|
void (*frameStatsCb)(void *messager, char *buf, uint32_t len);
|
||||||
|
|
||||||
|
uint8_t (*requestFrameStatsNoneCb)(void *messager);
|
||||||
|
uint8_t (*requestFrameStatsOwnerCb)(void *messager);
|
||||||
|
uint8_t (*requestFrameStatsAllCb)(void *messager);
|
||||||
|
uint8_t (*requestFrameStatsOneCb)(void *messager, const char *client);
|
||||||
|
|
||||||
|
uint8_t (*ownerConnectedCb)(void *messager);
|
||||||
|
uint8_t (*numActiveUsersCb)(void *messager);
|
||||||
|
uint8_t (*getClientFrameStatsNumCb)(void *messager);
|
||||||
|
uint8_t (*serverFrameStatsReadyCb)(void *messager);
|
||||||
} settings_t;
|
} settings_t;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
#include "websocket.h"
|
#include "websocket.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -222,14 +224,21 @@ static void do_proxy(ws_ctx_t *ws_ctx, int target) {
|
|||||||
|
|
||||||
void proxy_handler(ws_ctx_t *ws_ctx) {
|
void proxy_handler(ws_ctx_t *ws_ctx) {
|
||||||
|
|
||||||
|
char sockname[32];
|
||||||
|
sprintf(sockname, ".KasmVNCSock%u", getpid());
|
||||||
|
|
||||||
struct sockaddr_un addr;
|
struct sockaddr_un addr;
|
||||||
addr.sun_family = AF_UNIX;
|
addr.sun_family = AF_UNIX;
|
||||||
strcpy(addr.sun_path, ".KasmVNCSock");
|
strcpy(addr.sun_path, sockname);
|
||||||
addr.sun_path[0] = '\0';
|
addr.sun_path[0] = '\0';
|
||||||
|
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
|
||||||
struct sockaddr_un myaddr;
|
struct sockaddr_un myaddr;
|
||||||
myaddr.sun_family = AF_UNIX;
|
myaddr.sun_family = AF_UNIX;
|
||||||
sprintf(myaddr.sun_path, ".%s@%s", ws_ctx->user, ws_ctx->ip);
|
sprintf(myaddr.sun_path, ".%s@%s_%lu.%lu", ws_ctx->user, ws_ctx->ip,
|
||||||
|
tv.tv_sec, tv.tv_usec);
|
||||||
myaddr.sun_path[0] = '\0';
|
myaddr.sun_path[0] = '\0';
|
||||||
|
|
||||||
int tsock = socket(AF_UNIX, SOCK_STREAM, 0);
|
int tsock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
@@ -238,7 +247,7 @@ void proxy_handler(ws_ctx_t *ws_ctx) {
|
|||||||
handler_msg("connecting to VNC target\n");
|
handler_msg("connecting to VNC target\n");
|
||||||
|
|
||||||
if (connect(tsock, (struct sockaddr *) &addr,
|
if (connect(tsock, (struct sockaddr *) &addr,
|
||||||
sizeof(sa_family_t) + sizeof(".KasmVNCSock")) < 0) {
|
sizeof(sa_family_t) + strlen(sockname)) < 0) {
|
||||||
|
|
||||||
handler_emsg("Could not connect to target: %s\n",
|
handler_emsg("Could not connect to target: %s\n",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
|
|||||||
69
common/rdr/BufferedInStream.cxx
Normal file
69
common/rdr/BufferedInStream.cxx
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||||
|
* Copyright 2020 Pierre Ossman for Cendio AB
|
||||||
|
*
|
||||||
|
* 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 HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <rdr/BufferedInStream.h>
|
||||||
|
#include <rdr/Exception.h>
|
||||||
|
|
||||||
|
using namespace rdr;
|
||||||
|
|
||||||
|
static const size_t DEFAULT_BUF_SIZE = 8192;
|
||||||
|
|
||||||
|
BufferedInStream::BufferedInStream()
|
||||||
|
: bufSize(DEFAULT_BUF_SIZE), offset(0)
|
||||||
|
{
|
||||||
|
ptr = end = start = new U8[bufSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferedInStream::~BufferedInStream()
|
||||||
|
{
|
||||||
|
delete [] start;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t BufferedInStream::pos()
|
||||||
|
{
|
||||||
|
return offset + ptr - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BufferedInStream::overrun(size_t needed, bool wait)
|
||||||
|
{
|
||||||
|
if (needed > bufSize)
|
||||||
|
throw Exception("BufferedInStream overrun: "
|
||||||
|
"requested size of %lu bytes exceeds maximum of %lu bytes",
|
||||||
|
(long unsigned)needed, (long unsigned)bufSize);
|
||||||
|
|
||||||
|
// Do we need to shuffle things around?
|
||||||
|
if ((bufSize - (ptr - start)) < needed) {
|
||||||
|
memmove(start, ptr, end - ptr);
|
||||||
|
|
||||||
|
offset += ptr - start;
|
||||||
|
end -= ptr - start;
|
||||||
|
ptr = start;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (avail() < needed) {
|
||||||
|
if (!fillBuffer(start + bufSize - end, wait))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
54
common/rdr/BufferedInStream.h
Normal file
54
common/rdr/BufferedInStream.h
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||||
|
* Copyright 2020 Pierre Ossman for Cendio AB
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//
|
||||||
|
// Base class for input streams with a buffer
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef __RDR_BUFFEREDINSTREAM_H__
|
||||||
|
#define __RDR_BUFFEREDINSTREAM_H__
|
||||||
|
|
||||||
|
#include <rdr/InStream.h>
|
||||||
|
|
||||||
|
namespace rdr {
|
||||||
|
|
||||||
|
class BufferedInStream : public InStream {
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~BufferedInStream();
|
||||||
|
|
||||||
|
virtual size_t pos();
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual bool fillBuffer(size_t maxSize, bool wait) = 0;
|
||||||
|
|
||||||
|
virtual bool overrun(size_t needed, bool wait);
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t bufSize;
|
||||||
|
size_t offset;
|
||||||
|
U8* start;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
BufferedInStream();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // end of namespace rdr
|
||||||
|
|
||||||
|
#endif
|
||||||
108
common/rdr/BufferedOutStream.cxx
Normal file
108
common/rdr/BufferedOutStream.cxx
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||||
|
* Copyright 2011-2020 Pierre Ossman for Cendio AB
|
||||||
|
* Copyright 2017 Peter Astrand <astrand@cendio.se> for Cendio AB
|
||||||
|
*
|
||||||
|
* 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 HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <rdr/BufferedOutStream.h>
|
||||||
|
#include <rdr/Exception.h>
|
||||||
|
|
||||||
|
|
||||||
|
using namespace rdr;
|
||||||
|
|
||||||
|
static const size_t DEFAULT_BUF_SIZE = 16384;
|
||||||
|
|
||||||
|
BufferedOutStream::BufferedOutStream()
|
||||||
|
: bufSize(DEFAULT_BUF_SIZE), offset(0)
|
||||||
|
{
|
||||||
|
ptr = start = sentUpTo = new U8[bufSize];
|
||||||
|
end = start + bufSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferedOutStream::~BufferedOutStream()
|
||||||
|
{
|
||||||
|
// FIXME: Complain about non-flushed buffer?
|
||||||
|
delete [] start;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t BufferedOutStream::length()
|
||||||
|
{
|
||||||
|
return offset + ptr - sentUpTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t BufferedOutStream::bufferUsage()
|
||||||
|
{
|
||||||
|
return ptr - sentUpTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BufferedOutStream::flush()
|
||||||
|
{
|
||||||
|
while (sentUpTo < ptr) {
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
len = bufferUsage();
|
||||||
|
|
||||||
|
if (!flushBuffer(false))
|
||||||
|
break;
|
||||||
|
|
||||||
|
offset += len - bufferUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Managed to flush everything?
|
||||||
|
if (sentUpTo == ptr)
|
||||||
|
ptr = sentUpTo = start;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BufferedOutStream::overrun(size_t needed)
|
||||||
|
{
|
||||||
|
if (needed > bufSize)
|
||||||
|
throw Exception("BufferedOutStream overrun: "
|
||||||
|
"requested size of %lu bytes exceeds maximum of %lu bytes",
|
||||||
|
(long unsigned)needed, (long unsigned)bufSize);
|
||||||
|
|
||||||
|
// First try to get rid of the data we have
|
||||||
|
flush();
|
||||||
|
|
||||||
|
// Still not enough space?
|
||||||
|
while (needed > avail()) {
|
||||||
|
// Can we shuffle things around?
|
||||||
|
// (don't do this if it gains us less than 25%)
|
||||||
|
if (((size_t)(sentUpTo - start) > bufSize / 4) &&
|
||||||
|
(needed < bufSize - (ptr - sentUpTo))) {
|
||||||
|
memmove(start, sentUpTo, ptr - sentUpTo);
|
||||||
|
ptr = start + (ptr - sentUpTo);
|
||||||
|
sentUpTo = start;
|
||||||
|
} else {
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
len = bufferUsage();
|
||||||
|
|
||||||
|
// Have to get rid of more data, so allow the flush to wait...
|
||||||
|
flushBuffer(true);
|
||||||
|
|
||||||
|
offset += len - bufferUsage();
|
||||||
|
|
||||||
|
// Managed to flush everything?
|
||||||
|
if (sentUpTo == ptr)
|
||||||
|
ptr = sentUpTo = start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
65
common/rdr/BufferedOutStream.h
Normal file
65
common/rdr/BufferedOutStream.h
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||||
|
* Copyright 2011-2020 Pierre Ossman for Cendio AB
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//
|
||||||
|
// Base class for output streams with a buffer
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef __RDR_BUFFEREDOUTSTREAM_H__
|
||||||
|
#define __RDR_BUFFEREDOUTSTREAM_H__
|
||||||
|
|
||||||
|
#include <rdr/OutStream.h>
|
||||||
|
|
||||||
|
namespace rdr {
|
||||||
|
|
||||||
|
class BufferedOutStream : public OutStream {
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~BufferedOutStream();
|
||||||
|
|
||||||
|
virtual size_t length();
|
||||||
|
virtual void flush();
|
||||||
|
|
||||||
|
size_t bufferUsage();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// flushBuffer() requests that the stream be flushed. Returns true if it is
|
||||||
|
// able to progress the output (which might still not mean any bytes
|
||||||
|
// actually moved) and can be called again. If wait is true then it will
|
||||||
|
// block until all data has been written.
|
||||||
|
|
||||||
|
virtual bool flushBuffer(bool wait) = 0;
|
||||||
|
|
||||||
|
virtual void overrun(size_t needed);
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t bufSize;
|
||||||
|
size_t offset;
|
||||||
|
U8* start;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
U8* sentUpTo;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
BufferedOutStream();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
include_directories(${CMAKE_SOURCE_DIR}/common ${ZLIB_INCLUDE_DIRS})
|
include_directories(${CMAKE_SOURCE_DIR}/common ${ZLIB_INCLUDE_DIRS})
|
||||||
|
|
||||||
add_library(rdr STATIC
|
add_library(rdr STATIC
|
||||||
|
BufferedInStream.cxx
|
||||||
|
BufferedOutStream.cxx
|
||||||
Exception.cxx
|
Exception.cxx
|
||||||
FdInStream.cxx
|
FdInStream.cxx
|
||||||
FdOutStream.cxx
|
FdOutStream.cxx
|
||||||
|
|||||||
@@ -36,13 +36,6 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef vncmin
|
|
||||||
#define vncmin(a,b) (((a) < (b)) ? (a) : (b))
|
|
||||||
#endif
|
|
||||||
#ifndef vncmax
|
|
||||||
#define vncmax(a,b) (((a) > (b)) ? (a) : (b))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Old systems have select() in sys/time.h */
|
/* Old systems have select() in sys/time.h */
|
||||||
#ifdef HAVE_SYS_SELECT_H
|
#ifdef HAVE_SYS_SELECT_H
|
||||||
#include <sys/select.h>
|
#include <sys/select.h>
|
||||||
@@ -53,31 +46,22 @@
|
|||||||
|
|
||||||
using namespace rdr;
|
using namespace rdr;
|
||||||
|
|
||||||
enum { DEFAULT_BUF_SIZE = 8192,
|
enum { DEFAULT_BUF_SIZE = 8192 };
|
||||||
MIN_BULK_SIZE = 1024 };
|
|
||||||
|
|
||||||
FdInStream::FdInStream(int fd_, int timeoutms_, size_t bufSize_,
|
FdInStream::FdInStream(int fd_, int timeoutms_,
|
||||||
bool closeWhenDone_)
|
bool closeWhenDone_)
|
||||||
: fd(fd_), closeWhenDone(closeWhenDone_),
|
: fd(fd_), closeWhenDone(closeWhenDone_),
|
||||||
timeoutms(timeoutms_), blockCallback(0),
|
timeoutms(timeoutms_), blockCallback(0)
|
||||||
timing(false), timeWaitedIn100us(5), timedKbits(0),
|
|
||||||
bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
|
|
||||||
{
|
{
|
||||||
ptr = end = start = new U8[bufSize];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FdInStream::FdInStream(int fd_, FdInStreamBlockCallback* blockCallback_,
|
FdInStream::FdInStream(int fd_, FdInStreamBlockCallback* blockCallback_)
|
||||||
size_t bufSize_)
|
: fd(fd_), timeoutms(0), blockCallback(blockCallback_)
|
||||||
: fd(fd_), timeoutms(0), blockCallback(blockCallback_),
|
|
||||||
timing(false), timeWaitedIn100us(5), timedKbits(0),
|
|
||||||
bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
|
|
||||||
{
|
{
|
||||||
ptr = end = start = new U8[bufSize];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FdInStream::~FdInStream()
|
FdInStream::~FdInStream()
|
||||||
{
|
{
|
||||||
delete [] start;
|
|
||||||
if (closeWhenDone) close(fd);
|
if (closeWhenDone) close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,72 +76,15 @@ void FdInStream::setBlockCallback(FdInStreamBlockCallback* blockCallback_)
|
|||||||
timeoutms = 0;
|
timeoutms = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t FdInStream::pos()
|
|
||||||
|
bool FdInStream::fillBuffer(size_t maxSize, bool wait)
|
||||||
{
|
{
|
||||||
return offset + ptr - start;
|
size_t n = readWithTimeoutOrCallback((U8*)end, maxSize, wait);
|
||||||
}
|
if (n == 0)
|
||||||
|
return false;
|
||||||
|
end += n;
|
||||||
|
|
||||||
void FdInStream::readBytes(void* data, size_t length)
|
return true;
|
||||||
{
|
|
||||||
if (length < MIN_BULK_SIZE) {
|
|
||||||
InStream::readBytes(data, length);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
U8* dataPtr = (U8*)data;
|
|
||||||
|
|
||||||
size_t n = end - ptr;
|
|
||||||
if (n > length) n = length;
|
|
||||||
|
|
||||||
memcpy(dataPtr, ptr, n);
|
|
||||||
dataPtr += n;
|
|
||||||
length -= n;
|
|
||||||
ptr += n;
|
|
||||||
|
|
||||||
while (length > 0) {
|
|
||||||
n = readWithTimeoutOrCallback(dataPtr, length);
|
|
||||||
dataPtr += n;
|
|
||||||
length -= n;
|
|
||||||
offset += n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
size_t FdInStream::overrun(size_t itemSize, size_t nItems, bool wait)
|
|
||||||
{
|
|
||||||
if (itemSize > bufSize)
|
|
||||||
throw Exception("FdInStream overrun: max itemSize exceeded");
|
|
||||||
|
|
||||||
if (end - ptr != 0)
|
|
||||||
memmove(start, ptr, end - ptr);
|
|
||||||
|
|
||||||
offset += ptr - start;
|
|
||||||
end -= ptr - start;
|
|
||||||
ptr = start;
|
|
||||||
|
|
||||||
size_t bytes_to_read;
|
|
||||||
while ((size_t)(end - start) < itemSize) {
|
|
||||||
bytes_to_read = start + bufSize - end;
|
|
||||||
if (!timing) {
|
|
||||||
// When not timing, we must be careful not to read too much
|
|
||||||
// extra data into the buffer. Otherwise, the line speed
|
|
||||||
// estimation might stay at zero for a long time: All reads
|
|
||||||
// during timing=1 can be satisfied without calling
|
|
||||||
// readWithTimeoutOrCallback. However, reading only 1 or 2 bytes
|
|
||||||
// bytes is ineffecient.
|
|
||||||
bytes_to_read = vncmin(bytes_to_read, vncmax(itemSize*nItems, 8));
|
|
||||||
}
|
|
||||||
size_t n = readWithTimeoutOrCallback((U8*)end, bytes_to_read, wait);
|
|
||||||
if (n == 0) return 0;
|
|
||||||
end += n;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t nAvail;
|
|
||||||
nAvail = (end - ptr) / itemSize;
|
|
||||||
if (nAvail < nItems)
|
|
||||||
return nAvail;
|
|
||||||
|
|
||||||
return nItems;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -175,10 +102,6 @@ size_t FdInStream::overrun(size_t itemSize, size_t nItems, bool wait)
|
|||||||
|
|
||||||
size_t FdInStream::readWithTimeoutOrCallback(void* buf, size_t len, bool wait)
|
size_t FdInStream::readWithTimeoutOrCallback(void* buf, size_t len, bool wait)
|
||||||
{
|
{
|
||||||
struct timeval before, after;
|
|
||||||
if (timing)
|
|
||||||
gettimeofday(&before, 0);
|
|
||||||
|
|
||||||
int n;
|
int n;
|
||||||
while (true) {
|
while (true) {
|
||||||
do {
|
do {
|
||||||
@@ -215,48 +138,5 @@ size_t FdInStream::readWithTimeoutOrCallback(void* buf, size_t len, bool wait)
|
|||||||
if (n < 0) throw SystemException("read",errno);
|
if (n < 0) throw SystemException("read",errno);
|
||||||
if (n == 0) throw EndOfStream();
|
if (n == 0) throw EndOfStream();
|
||||||
|
|
||||||
if (timing) {
|
|
||||||
gettimeofday(&after, 0);
|
|
||||||
int newTimeWaited = ((after.tv_sec - before.tv_sec) * 10000 +
|
|
||||||
(after.tv_usec - before.tv_usec) / 100);
|
|
||||||
int newKbits = n * 8 / 1000;
|
|
||||||
|
|
||||||
// limit rate to between 10kbit/s and 40Mbit/s
|
|
||||||
|
|
||||||
if (newTimeWaited > newKbits*1000) newTimeWaited = newKbits*1000;
|
|
||||||
if (newTimeWaited < newKbits/4) newTimeWaited = newKbits/4;
|
|
||||||
|
|
||||||
timeWaitedIn100us += newTimeWaited;
|
|
||||||
timedKbits += newKbits;
|
|
||||||
}
|
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FdInStream::startTiming()
|
|
||||||
{
|
|
||||||
timing = true;
|
|
||||||
|
|
||||||
// Carry over up to 1s worth of previous rate for smoothing.
|
|
||||||
|
|
||||||
if (timeWaitedIn100us > 10000) {
|
|
||||||
timedKbits = timedKbits * 10000 / timeWaitedIn100us;
|
|
||||||
timeWaitedIn100us = 10000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FdInStream::stopTiming()
|
|
||||||
{
|
|
||||||
timing = false;
|
|
||||||
if (timeWaitedIn100us < timedKbits/2)
|
|
||||||
timeWaitedIn100us = timedKbits/2; // upper limit 20Mbit/s
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int FdInStream::kbitsPerSecond()
|
|
||||||
{
|
|
||||||
// The following calculation will overflow 32-bit arithmetic if we have
|
|
||||||
// received more than about 50Mbytes (400Mbits) since we started timing, so
|
|
||||||
// it should be OK for a single RFB update.
|
|
||||||
|
|
||||||
return timedKbits * 10000 / timeWaitedIn100us;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
#ifndef __RDR_FDINSTREAM_H__
|
#ifndef __RDR_FDINSTREAM_H__
|
||||||
#define __RDR_FDINSTREAM_H__
|
#define __RDR_FDINSTREAM_H__
|
||||||
|
|
||||||
#include <rdr/InStream.h>
|
#include <rdr/BufferedInStream.h>
|
||||||
|
|
||||||
namespace rdr {
|
namespace rdr {
|
||||||
|
|
||||||
@@ -33,31 +33,21 @@ namespace rdr {
|
|||||||
virtual ~FdInStreamBlockCallback() {}
|
virtual ~FdInStreamBlockCallback() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class FdInStream : public InStream {
|
class FdInStream : public BufferedInStream {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
FdInStream(int fd, int timeoutms=-1, size_t bufSize=0,
|
FdInStream(int fd, int timeoutms=-1, bool closeWhenDone_=false);
|
||||||
bool closeWhenDone_=false);
|
FdInStream(int fd, FdInStreamBlockCallback* blockCallback);
|
||||||
FdInStream(int fd, FdInStreamBlockCallback* blockCallback,
|
|
||||||
size_t bufSize=0);
|
|
||||||
virtual ~FdInStream();
|
virtual ~FdInStream();
|
||||||
|
|
||||||
void setTimeout(int timeoutms);
|
void setTimeout(int timeoutms);
|
||||||
void setBlockCallback(FdInStreamBlockCallback* blockCallback);
|
void setBlockCallback(FdInStreamBlockCallback* blockCallback);
|
||||||
int getFd() { return fd; }
|
int getFd() { return fd; }
|
||||||
size_t pos();
|
|
||||||
void readBytes(void* data, size_t length);
|
|
||||||
|
|
||||||
void startTiming();
|
|
||||||
void stopTiming();
|
|
||||||
unsigned int kbitsPerSecond();
|
|
||||||
unsigned int timeWaited() { return timeWaitedIn100us; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
size_t overrun(size_t itemSize, size_t nItems, bool wait);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
virtual bool fillBuffer(size_t maxSize, bool wait);
|
||||||
|
|
||||||
size_t readWithTimeoutOrCallback(void* buf, size_t len, bool wait=true);
|
size_t readWithTimeoutOrCallback(void* buf, size_t len, bool wait=true);
|
||||||
|
|
||||||
int fd;
|
int fd;
|
||||||
@@ -65,11 +55,6 @@ namespace rdr {
|
|||||||
int timeoutms;
|
int timeoutms;
|
||||||
FdInStreamBlockCallback* blockCallback;
|
FdInStreamBlockCallback* blockCallback;
|
||||||
|
|
||||||
bool timing;
|
|
||||||
unsigned int timeWaitedIn100us;
|
|
||||||
unsigned int timedKbits;
|
|
||||||
|
|
||||||
size_t bufSize;
|
|
||||||
size_t offset;
|
size_t offset;
|
||||||
U8* start;
|
U8* start;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -35,6 +35,8 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Old systems have select() in sys/time.h */
|
/* Old systems have select() in sys/time.h */
|
||||||
@@ -49,26 +51,19 @@
|
|||||||
|
|
||||||
using namespace rdr;
|
using namespace rdr;
|
||||||
|
|
||||||
enum { DEFAULT_BUF_SIZE = 16384 };
|
FdOutStream::FdOutStream(int fd_, bool blocking_, int timeoutms_)
|
||||||
|
: fd(fd_), blocking(blocking_), timeoutms(timeoutms_)
|
||||||
FdOutStream::FdOutStream(int fd_, bool blocking_, int timeoutms_, size_t bufSize_)
|
|
||||||
: fd(fd_), blocking(blocking_), timeoutms(timeoutms_),
|
|
||||||
bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
|
|
||||||
{
|
{
|
||||||
ptr = start = sentUpTo = new U8[bufSize];
|
|
||||||
end = start + bufSize;
|
|
||||||
|
|
||||||
gettimeofday(&lastWrite, NULL);
|
gettimeofday(&lastWrite, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
FdOutStream::~FdOutStream()
|
FdOutStream::~FdOutStream()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
blocking = true;
|
while (sentUpTo != ptr)
|
||||||
flush();
|
flushBuffer(true);
|
||||||
} catch (Exception&) {
|
} catch (Exception&) {
|
||||||
}
|
}
|
||||||
delete [] start;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FdOutStream::setTimeout(int timeoutms_) {
|
void FdOutStream::setTimeout(int timeoutms_) {
|
||||||
@@ -79,82 +74,29 @@ void FdOutStream::setBlocking(bool blocking_) {
|
|||||||
blocking = blocking_;
|
blocking = blocking_;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t FdOutStream::length()
|
|
||||||
{
|
|
||||||
return offset + ptr - sentUpTo;
|
|
||||||
}
|
|
||||||
|
|
||||||
int FdOutStream::bufferUsage()
|
|
||||||
{
|
|
||||||
return ptr - sentUpTo;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned FdOutStream::getIdleTime()
|
unsigned FdOutStream::getIdleTime()
|
||||||
{
|
{
|
||||||
return rfb::msSince(&lastWrite);
|
return rfb::msSince(&lastWrite);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FdOutStream::flush()
|
bool FdOutStream::flushBuffer(bool wait)
|
||||||
{
|
{
|
||||||
while (sentUpTo < ptr) {
|
size_t n = writeWithTimeout((const void*) sentUpTo,
|
||||||
size_t n = writeWithTimeout((const void*) sentUpTo,
|
ptr - sentUpTo,
|
||||||
ptr - sentUpTo,
|
(blocking || wait)? timeoutms : 0);
|
||||||
blocking? timeoutms : 0);
|
|
||||||
|
|
||||||
// Timeout?
|
// Timeout?
|
||||||
if (n == 0) {
|
if (n == 0) {
|
||||||
// If non-blocking then we're done here
|
// If non-blocking then we're done here
|
||||||
if (!blocking)
|
if (!blocking && !wait)
|
||||||
break;
|
return false;
|
||||||
|
|
||||||
throw TimedOut();
|
throw TimedOut();
|
||||||
}
|
|
||||||
|
|
||||||
sentUpTo += n;
|
|
||||||
offset += n;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Managed to flush everything?
|
sentUpTo += n;
|
||||||
if (sentUpTo == ptr)
|
|
||||||
ptr = sentUpTo = start;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return true;
|
||||||
size_t FdOutStream::overrun(size_t itemSize, size_t nItems)
|
|
||||||
{
|
|
||||||
if (itemSize > bufSize)
|
|
||||||
throw Exception("FdOutStream overrun: max itemSize exceeded");
|
|
||||||
|
|
||||||
// First try to get rid of the data we have
|
|
||||||
flush();
|
|
||||||
|
|
||||||
// Still not enough space?
|
|
||||||
if (itemSize > (size_t)(end - ptr)) {
|
|
||||||
// Can we shuffle things around?
|
|
||||||
// (don't do this if it gains us less than 25%)
|
|
||||||
if (((size_t)(sentUpTo - start) > bufSize / 4) &&
|
|
||||||
(itemSize < bufSize - (ptr - sentUpTo))) {
|
|
||||||
memmove(start, sentUpTo, ptr - sentUpTo);
|
|
||||||
ptr = start + (ptr - sentUpTo);
|
|
||||||
sentUpTo = start;
|
|
||||||
} else {
|
|
||||||
// Have to get rid of more data, so turn off non-blocking
|
|
||||||
// for a bit...
|
|
||||||
bool realBlocking;
|
|
||||||
|
|
||||||
realBlocking = blocking;
|
|
||||||
blocking = true;
|
|
||||||
flush();
|
|
||||||
blocking = realBlocking;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t nAvail;
|
|
||||||
nAvail = (end - ptr) / itemSize;
|
|
||||||
if (nAvail < nItems)
|
|
||||||
return nAvail;
|
|
||||||
|
|
||||||
return nItems;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -26,38 +26,29 @@
|
|||||||
|
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
|
||||||
#include <rdr/OutStream.h>
|
#include <rdr/BufferedOutStream.h>
|
||||||
|
|
||||||
namespace rdr {
|
namespace rdr {
|
||||||
|
|
||||||
class FdOutStream : public OutStream {
|
class FdOutStream : public BufferedOutStream {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
FdOutStream(int fd, bool blocking=true, int timeoutms=-1, size_t bufSize=0);
|
FdOutStream(int fd, bool blocking=true, int timeoutms=-1);
|
||||||
virtual ~FdOutStream();
|
virtual ~FdOutStream();
|
||||||
|
|
||||||
void setTimeout(int timeoutms);
|
void setTimeout(int timeoutms);
|
||||||
void setBlocking(bool blocking);
|
void setBlocking(bool blocking);
|
||||||
int getFd() { return fd; }
|
int getFd() { return fd; }
|
||||||
|
|
||||||
void flush();
|
|
||||||
size_t length();
|
|
||||||
|
|
||||||
int bufferUsage();
|
|
||||||
|
|
||||||
unsigned getIdleTime();
|
unsigned getIdleTime();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t overrun(size_t itemSize, size_t nItems);
|
virtual bool flushBuffer(bool wait);
|
||||||
size_t writeWithTimeout(const void* data, size_t length, int timeoutms);
|
size_t writeWithTimeout(const void* data, size_t length, int timeoutms);
|
||||||
int fd;
|
int fd;
|
||||||
bool blocking;
|
bool blocking;
|
||||||
int timeoutms;
|
int timeoutms;
|
||||||
size_t bufSize;
|
|
||||||
size_t offset;
|
|
||||||
U8* start;
|
|
||||||
U8* sentUpTo;
|
|
||||||
struct timeval lastWrite;
|
struct timeval lastWrite;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ FileInStream::FileInStream(const char *fileName)
|
|||||||
file = fopen(fileName, "rb");
|
file = fopen(fileName, "rb");
|
||||||
if (!file)
|
if (!file)
|
||||||
throw SystemException("fopen", errno);
|
throw SystemException("fopen", errno);
|
||||||
ptr = end = b;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FileInStream::~FileInStream(void) {
|
FileInStream::~FileInStream(void) {
|
||||||
@@ -40,50 +39,17 @@ FileInStream::~FileInStream(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileInStream::reset(void) {
|
bool FileInStream::fillBuffer(size_t maxSize, bool wait)
|
||||||
if (!file)
|
|
||||||
throw Exception("File is not open");
|
|
||||||
if (fseek(file, 0, SEEK_SET) != 0)
|
|
||||||
throw SystemException("fseek", errno);
|
|
||||||
ptr = end = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t FileInStream::pos()
|
|
||||||
{
|
{
|
||||||
if (!file)
|
size_t n = fread((U8 *)end, 1, maxSize, file);
|
||||||
throw Exception("File is not open");
|
if (n == 0) {
|
||||||
|
if (ferror(file))
|
||||||
return ftell(file) + ptr - b;
|
throw SystemException("fread", errno);
|
||||||
}
|
if (feof(file))
|
||||||
|
throw EndOfStream();
|
||||||
size_t FileInStream::overrun(size_t itemSize, size_t nItems, bool wait)
|
return false;
|
||||||
{
|
|
||||||
if (itemSize > sizeof(b))
|
|
||||||
throw Exception("FileInStream overrun: max itemSize exceeded");
|
|
||||||
|
|
||||||
if (end - ptr != 0)
|
|
||||||
memmove(b, ptr, end - ptr);
|
|
||||||
|
|
||||||
end -= ptr - b;
|
|
||||||
ptr = b;
|
|
||||||
|
|
||||||
|
|
||||||
while ((size_t)(end - b) < itemSize) {
|
|
||||||
size_t n = fread((U8 *)end, b + sizeof(b) - end, 1, file);
|
|
||||||
if (n == 0) {
|
|
||||||
if (ferror(file))
|
|
||||||
throw SystemException("fread", errno);
|
|
||||||
if (feof(file))
|
|
||||||
throw EndOfStream();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
end += b + sizeof(b) - end;
|
|
||||||
}
|
}
|
||||||
|
end += n;
|
||||||
|
|
||||||
size_t nAvail;
|
return true;
|
||||||
nAvail = (end - ptr) / itemSize;
|
|
||||||
if (nAvail < nItems)
|
|
||||||
return nAvail;
|
|
||||||
|
|
||||||
return nItems;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,26 +22,21 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include <rdr/InStream.h>
|
#include <rdr/BufferedInStream.h>
|
||||||
|
|
||||||
namespace rdr {
|
namespace rdr {
|
||||||
|
|
||||||
class FileInStream : public InStream {
|
class FileInStream : public BufferedInStream {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
FileInStream(const char *fileName);
|
FileInStream(const char *fileName);
|
||||||
~FileInStream(void);
|
~FileInStream(void);
|
||||||
|
|
||||||
void reset(void);
|
private:
|
||||||
|
virtual bool fillBuffer(size_t maxSize, bool wait);
|
||||||
size_t pos();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
size_t overrun(size_t itemSize, size_t nItems, bool wait = true);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
U8 b[131072];
|
|
||||||
FILE *file;
|
FILE *file;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -24,18 +24,14 @@
|
|||||||
|
|
||||||
using namespace rdr;
|
using namespace rdr;
|
||||||
|
|
||||||
const int DEFAULT_BUF_LEN = 16384;
|
|
||||||
|
|
||||||
static inline int min(int a, int b) {return a<b ? a : b;}
|
static inline int min(int a, int b) {return a<b ? a : b;}
|
||||||
|
|
||||||
HexInStream::HexInStream(InStream& is, size_t bufSize_)
|
HexInStream::HexInStream(InStream& is)
|
||||||
: bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_LEN), offset(0), in_stream(is)
|
: in_stream(is)
|
||||||
{
|
{
|
||||||
ptr = end = start = new U8[bufSize];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HexInStream::~HexInStream() {
|
HexInStream::~HexInStream() {
|
||||||
delete [] start;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -76,44 +72,24 @@ decodeError:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
size_t HexInStream::pos() {
|
bool HexInStream::fillBuffer(size_t maxSize, bool wait) {
|
||||||
return offset + ptr - start;
|
if (!in_stream.check(2, wait))
|
||||||
}
|
return false;
|
||||||
|
|
||||||
size_t HexInStream::overrun(size_t itemSize, size_t nItems, bool wait) {
|
const U8* iptr = in_stream.getptr();
|
||||||
if (itemSize > bufSize)
|
const U8* eptr = in_stream.getend();
|
||||||
throw Exception("HexInStream overrun: max itemSize exceeded");
|
size_t length = min((eptr - iptr)/2, maxSize);
|
||||||
|
|
||||||
if (end - ptr != 0)
|
U8* optr = (U8*) end;
|
||||||
memmove(start, ptr, end - ptr);
|
for (size_t i=0; i<length; i++) {
|
||||||
|
int v = 0;
|
||||||
end -= ptr - start;
|
readHexAndShift(iptr[i*2], &v);
|
||||||
offset += ptr - start;
|
readHexAndShift(iptr[i*2+1], &v);
|
||||||
ptr = start;
|
optr[i] = v;
|
||||||
|
|
||||||
while ((size_t)(end - ptr) < itemSize) {
|
|
||||||
size_t n = in_stream.check(2, 1, wait);
|
|
||||||
if (n == 0) return 0;
|
|
||||||
const U8* iptr = in_stream.getptr();
|
|
||||||
const U8* eptr = in_stream.getend();
|
|
||||||
size_t length = min((eptr - iptr)/2, start + bufSize - end);
|
|
||||||
|
|
||||||
U8* optr = (U8*) end;
|
|
||||||
for (size_t i=0; i<length; i++) {
|
|
||||||
int v = 0;
|
|
||||||
readHexAndShift(iptr[i*2], &v);
|
|
||||||
readHexAndShift(iptr[i*2+1], &v);
|
|
||||||
optr[i] = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
in_stream.setptr(iptr + length*2);
|
|
||||||
end += length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t nAvail;
|
in_stream.setptr(iptr + length*2);
|
||||||
nAvail = (end - ptr) / itemSize;
|
end += length;
|
||||||
if (nAvail < nItems)
|
|
||||||
return nAvail;
|
|
||||||
|
|
||||||
return nItems;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,29 +19,23 @@
|
|||||||
#ifndef __RDR_HEX_INSTREAM_H__
|
#ifndef __RDR_HEX_INSTREAM_H__
|
||||||
#define __RDR_HEX_INSTREAM_H__
|
#define __RDR_HEX_INSTREAM_H__
|
||||||
|
|
||||||
#include <rdr/InStream.h>
|
#include <rdr/BufferedInStream.h>
|
||||||
|
|
||||||
namespace rdr {
|
namespace rdr {
|
||||||
|
|
||||||
class HexInStream : public InStream {
|
class HexInStream : public BufferedInStream {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
HexInStream(InStream& is, size_t bufSize=0);
|
HexInStream(InStream& is);
|
||||||
virtual ~HexInStream();
|
virtual ~HexInStream();
|
||||||
|
|
||||||
size_t pos();
|
|
||||||
|
|
||||||
static bool readHexAndShift(char c, int* v);
|
static bool readHexAndShift(char c, int* v);
|
||||||
static bool hexStrToBin(const char* s, char** data, size_t* length);
|
static bool hexStrToBin(const char* s, char** data, size_t* length);
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
size_t overrun(size_t itemSize, size_t nItems, bool wait);
|
virtual bool fillBuffer(size_t maxSize, bool wait);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t bufSize;
|
|
||||||
U8* start;
|
|
||||||
size_t offset;
|
|
||||||
|
|
||||||
InStream& in_stream;
|
InStream& in_stream;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ const int DEFAULT_BUF_LEN = 16384;
|
|||||||
|
|
||||||
static inline size_t min(size_t a, size_t b) {return a<b ? a : b;}
|
static inline size_t min(size_t a, size_t b) {return a<b ? a : b;}
|
||||||
|
|
||||||
HexOutStream::HexOutStream(OutStream& os, size_t buflen)
|
HexOutStream::HexOutStream(OutStream& os)
|
||||||
: out_stream(os), offset(0), bufSize(buflen ? buflen : DEFAULT_BUF_LEN)
|
: out_stream(os), offset(0), bufSize(DEFAULT_BUF_LEN)
|
||||||
{
|
{
|
||||||
if (bufSize % 2)
|
if (bufSize % 2)
|
||||||
bufSize--;
|
bufSize--;
|
||||||
@@ -95,18 +95,10 @@ HexOutStream::flush() {
|
|||||||
out_stream.flush();
|
out_stream.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
void HexOutStream::overrun(size_t needed) {
|
||||||
HexOutStream::overrun(size_t itemSize, size_t nItems) {
|
if (needed > bufSize)
|
||||||
if (itemSize > bufSize)
|
throw Exception("HexOutStream overrun: buffer size exceeded");
|
||||||
throw Exception("HexOutStream overrun: max itemSize exceeded");
|
|
||||||
|
|
||||||
writeBuffer();
|
writeBuffer();
|
||||||
|
|
||||||
size_t nAvail;
|
|
||||||
nAvail = (end - ptr) / itemSize;
|
|
||||||
if (nAvail < nItems)
|
|
||||||
return nAvail;
|
|
||||||
|
|
||||||
return nItems;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace rdr {
|
|||||||
class HexOutStream : public OutStream {
|
class HexOutStream : public OutStream {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
HexOutStream(OutStream& os, size_t buflen=0);
|
HexOutStream(OutStream& os);
|
||||||
virtual ~HexOutStream();
|
virtual ~HexOutStream();
|
||||||
|
|
||||||
void flush();
|
void flush();
|
||||||
@@ -37,7 +37,7 @@ namespace rdr {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void writeBuffer();
|
void writeBuffer();
|
||||||
size_t overrun(size_t itemSize, size_t nItems);
|
virtual void overrun(size_t needed);
|
||||||
|
|
||||||
OutStream& out_stream;
|
OutStream& out_stream;
|
||||||
|
|
||||||
|
|||||||
@@ -35,28 +35,25 @@ namespace rdr {
|
|||||||
|
|
||||||
virtual ~InStream() {}
|
virtual ~InStream() {}
|
||||||
|
|
||||||
// check() ensures there is buffer data for at least one item of size
|
// avail() returns the number of bytes that are currenctly directly
|
||||||
// itemSize bytes. Returns the number of items in the buffer (up to a
|
// available from the stream.
|
||||||
// maximum of nItems). If wait is false, then instead of blocking to wait
|
|
||||||
// for the bytes, zero is returned if the bytes are not immediately
|
|
||||||
// available. If itemSize or nItems is zero, check() will return zero.
|
|
||||||
|
|
||||||
inline size_t check(size_t itemSize, size_t nItems=1, bool wait=true)
|
inline size_t avail()
|
||||||
{
|
{
|
||||||
size_t nAvail;
|
return end - ptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (itemSize == 0 || nItems == 0)
|
// check() ensures there is buffer data for at least needed bytes. Returns
|
||||||
return 0;
|
// true once the data is available. If wait is false, then instead of
|
||||||
|
// blocking to wait for the bytes, false is returned if the bytes are not
|
||||||
|
// immediately available.
|
||||||
|
|
||||||
if (itemSize > (size_t)(end - ptr))
|
inline size_t check(size_t needed, bool wait=true)
|
||||||
return overrun(itemSize, nItems, wait);
|
{
|
||||||
|
if (needed > avail())
|
||||||
|
return overrun(needed, wait);
|
||||||
|
|
||||||
// itemSize cannot be zero at this point
|
return true;
|
||||||
nAvail = (end - ptr) / itemSize;
|
|
||||||
if (nAvail < nItems)
|
|
||||||
return nAvail;
|
|
||||||
|
|
||||||
return nItems;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkNoWait() tries to make sure that the given number of bytes can
|
// checkNoWait() tries to make sure that the given number of bytes can
|
||||||
@@ -64,10 +61,7 @@ namespace rdr {
|
|||||||
// otherwise. The length must be "small" (less than the buffer size).
|
// otherwise. The length must be "small" (less than the buffer size).
|
||||||
// If length is zero, checkNoWait() will return true.
|
// If length is zero, checkNoWait() will return true.
|
||||||
|
|
||||||
inline bool checkNoWait(size_t length)
|
inline bool checkNoWait(size_t length) { return check(length, false); }
|
||||||
{
|
|
||||||
return length == 0 || check(length, 1, false) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// readU/SN() methods read unsigned and signed N-bit integers.
|
// readU/SN() methods read unsigned and signed N-bit integers.
|
||||||
|
|
||||||
@@ -138,13 +132,12 @@ namespace rdr {
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
// overrun() is implemented by a derived class to cope with buffer overrun.
|
// overrun() is implemented by a derived class to cope with buffer overrun.
|
||||||
// It ensures there are at least itemSize bytes of buffer data. Returns
|
// It ensures there are at least needed bytes of buffer data. Returns true
|
||||||
// the number of items in the buffer (up to a maximum of nItems). itemSize
|
// once the data is available. If wait is false, then instead of blocking
|
||||||
// is supposed to be "small" (a few bytes). If wait is false, then
|
// to wait for the bytes, false is returned if the bytes are not
|
||||||
// instead of blocking to wait for the bytes, zero is returned if the bytes
|
// immediately available.
|
||||||
// are not immediately available.
|
|
||||||
|
|
||||||
virtual size_t overrun(size_t itemSize, size_t nItems, bool wait=true) = 0;
|
virtual bool overrun(size_t needed, bool wait=true) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ namespace rdr {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
size_t overrun(size_t itemSize, size_t nItems, bool wait) { throw EndOfStream(); }
|
bool overrun(size_t needed, bool wait) { throw EndOfStream(); }
|
||||||
const U8* start;
|
const U8* start;
|
||||||
bool deleteWhenDone;
|
bool deleteWhenDone;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -41,12 +41,6 @@ namespace rdr {
|
|||||||
delete [] start;
|
delete [] start;
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeBytes(const void* data, size_t length) {
|
|
||||||
check(length);
|
|
||||||
memcpy(ptr, data, length);
|
|
||||||
ptr += length;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t length() { return ptr - start; }
|
size_t length() { return ptr - start; }
|
||||||
void clear() { ptr = start; };
|
void clear() { ptr = start; };
|
||||||
void clearAndZero() { memset(start, 0, ptr-start); clear(); }
|
void clearAndZero() { memset(start, 0, ptr-start); clear(); }
|
||||||
@@ -58,11 +52,11 @@ namespace rdr {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
// overrun() either doubles the buffer or adds enough space for nItems of
|
// overrun() either doubles the buffer or adds enough space for
|
||||||
// size itemSize bytes.
|
// needed bytes.
|
||||||
|
|
||||||
size_t overrun(size_t itemSize, size_t nItems) {
|
virtual void overrun(size_t needed) {
|
||||||
size_t len = ptr - start + itemSize * nItems;
|
size_t len = ptr - start + needed;
|
||||||
if (len < (size_t)(end - start) * 2)
|
if (len < (size_t)(end - start) * 2)
|
||||||
len = (end - start) * 2;
|
len = (end - start) * 2;
|
||||||
|
|
||||||
@@ -75,8 +69,6 @@ namespace rdr {
|
|||||||
delete [] start;
|
delete [] start;
|
||||||
start = newStart;
|
start = newStart;
|
||||||
end = newStart + len;
|
end = newStart + len;
|
||||||
|
|
||||||
return nItems;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
U8* start;
|
U8* start;
|
||||||
|
|||||||
@@ -40,22 +40,20 @@ namespace rdr {
|
|||||||
|
|
||||||
virtual ~OutStream() {}
|
virtual ~OutStream() {}
|
||||||
|
|
||||||
// check() ensures there is buffer space for at least one item of size
|
// avail() returns the number of bytes that currently be written to the
|
||||||
// itemSize bytes. Returns the number of items which fit (up to a maximum
|
// stream without any risk of blocking.
|
||||||
// of nItems).
|
|
||||||
|
|
||||||
inline size_t check(size_t itemSize, size_t nItems=1)
|
inline size_t avail()
|
||||||
{
|
{
|
||||||
size_t nAvail;
|
return end - ptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (itemSize > (size_t)(end - ptr))
|
// check() ensures there is buffer space for at least needed bytes.
|
||||||
return overrun(itemSize, nItems);
|
|
||||||
|
|
||||||
nAvail = (end - ptr) / itemSize;
|
inline void check(size_t needed)
|
||||||
if (nAvail < nItems)
|
{
|
||||||
return nAvail;
|
if (needed > avail())
|
||||||
|
overrun(needed);
|
||||||
return nItems;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeU/SN() methods write unsigned and signed N-bit integers.
|
// writeU/SN() methods write unsigned and signed N-bit integers.
|
||||||
@@ -83,19 +81,14 @@ namespace rdr {
|
|||||||
while (bytes-- > 0) writeU8(0);
|
while (bytes-- > 0) writeU8(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void skip(size_t bytes) {
|
|
||||||
while (bytes > 0) {
|
|
||||||
size_t n = check(1, bytes);
|
|
||||||
ptr += n;
|
|
||||||
bytes -= n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeBytes() writes an exact number of bytes.
|
// writeBytes() writes an exact number of bytes.
|
||||||
|
|
||||||
void writeBytes(const void* data, size_t length) {
|
void writeBytes(const void* data, size_t length) {
|
||||||
while (length > 0) {
|
while (length > 0) {
|
||||||
size_t n = check(1, length);
|
check(1);
|
||||||
|
size_t n = length;
|
||||||
|
if (length > avail())
|
||||||
|
n = avail();
|
||||||
memcpy(ptr, data, n);
|
memcpy(ptr, data, n);
|
||||||
ptr += n;
|
ptr += n;
|
||||||
data = (U8*)data + n;
|
data = (U8*)data + n;
|
||||||
@@ -107,7 +100,10 @@ namespace rdr {
|
|||||||
|
|
||||||
void copyBytes(InStream* is, size_t length) {
|
void copyBytes(InStream* is, size_t length) {
|
||||||
while (length > 0) {
|
while (length > 0) {
|
||||||
size_t n = check(1, length);
|
check(1);
|
||||||
|
size_t n = length;
|
||||||
|
if (length > avail())
|
||||||
|
n = avail();
|
||||||
is->readBytes(ptr, n);
|
is->readBytes(ptr, n);
|
||||||
ptr += n;
|
ptr += n;
|
||||||
length -= n;
|
length -= n;
|
||||||
@@ -143,11 +139,9 @@ namespace rdr {
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
// overrun() is implemented by a derived class to cope with buffer overrun.
|
// overrun() is implemented by a derived class to cope with buffer overrun.
|
||||||
// It ensures there are at least itemSize bytes of buffer space. Returns
|
// It ensures there are at least needed bytes of buffer space.
|
||||||
// the number of items which fit (up to a maximum of nItems). itemSize is
|
|
||||||
// supposed to be "small" (a few bytes).
|
|
||||||
|
|
||||||
virtual size_t overrun(size_t itemSize, size_t nItems) = 0;
|
virtual void overrun(size_t needed) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
|||||||
@@ -32,15 +32,10 @@
|
|||||||
|
|
||||||
using namespace rdr;
|
using namespace rdr;
|
||||||
|
|
||||||
const size_t DEFAULT_BUF_LEN = 256;
|
|
||||||
|
|
||||||
unsigned int RandomStream::seed;
|
unsigned int RandomStream::seed;
|
||||||
|
|
||||||
RandomStream::RandomStream()
|
RandomStream::RandomStream()
|
||||||
: offset(0)
|
|
||||||
{
|
{
|
||||||
ptr = end = start = new U8[DEFAULT_BUF_LEN];
|
|
||||||
|
|
||||||
#ifdef RFB_HAVE_WINCRYPT
|
#ifdef RFB_HAVE_WINCRYPT
|
||||||
provider = 0;
|
provider = 0;
|
||||||
if (!CryptAcquireContext(&provider, 0, 0, PROV_RSA_FULL, 0)) {
|
if (!CryptAcquireContext(&provider, 0, 0, PROV_RSA_FULL, 0)) {
|
||||||
@@ -72,8 +67,6 @@ RandomStream::RandomStream()
|
|||||||
}
|
}
|
||||||
|
|
||||||
RandomStream::~RandomStream() {
|
RandomStream::~RandomStream() {
|
||||||
delete [] start;
|
|
||||||
|
|
||||||
#ifdef RFB_HAVE_WINCRYPT
|
#ifdef RFB_HAVE_WINCRYPT
|
||||||
if (provider)
|
if (provider)
|
||||||
CryptReleaseContext(provider, 0);
|
CryptReleaseContext(provider, 0);
|
||||||
@@ -83,50 +76,29 @@ RandomStream::~RandomStream() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t RandomStream::pos() {
|
bool RandomStream::fillBuffer(size_t maxSize, bool wait) {
|
||||||
return offset + ptr - start;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t RandomStream::overrun(size_t itemSize, size_t nItems, bool wait) {
|
|
||||||
if (itemSize > DEFAULT_BUF_LEN)
|
|
||||||
throw Exception("RandomStream overrun: max itemSize exceeded");
|
|
||||||
|
|
||||||
if (end - ptr != 0)
|
|
||||||
memmove(start, ptr, end - ptr);
|
|
||||||
|
|
||||||
end -= ptr - start;
|
|
||||||
offset += ptr - start;
|
|
||||||
ptr = start;
|
|
||||||
|
|
||||||
size_t length = start + DEFAULT_BUF_LEN - end;
|
|
||||||
|
|
||||||
#ifdef RFB_HAVE_WINCRYPT
|
#ifdef RFB_HAVE_WINCRYPT
|
||||||
if (provider) {
|
if (provider) {
|
||||||
if (!CryptGenRandom(provider, length, (U8*)end))
|
if (!CryptGenRandom(provider, maxSize, (U8*)end))
|
||||||
throw rdr::SystemException("unable to CryptGenRandom", GetLastError());
|
throw rdr::SystemException("unable to CryptGenRandom", GetLastError());
|
||||||
end += length;
|
end += maxSize;
|
||||||
} else {
|
} else {
|
||||||
#else
|
#else
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
if (fp) {
|
if (fp) {
|
||||||
size_t n = fread((U8*)end, length, 1, fp);
|
size_t n = fread((U8*)end, 1, maxSize, fp);
|
||||||
if (n != 1)
|
if (n <= 0)
|
||||||
throw rdr::SystemException("reading /dev/urandom or /dev/random failed",
|
throw rdr::SystemException("reading /dev/urandom or /dev/random failed",
|
||||||
errno);
|
errno);
|
||||||
end += length;
|
end += n;
|
||||||
} else {
|
} else {
|
||||||
#else
|
#else
|
||||||
{
|
{
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
for (size_t i=0; i<length; i++)
|
for (size_t i=0; i<maxSize; i++)
|
||||||
*(U8*)end++ = (int) (256.0*rand()/(RAND_MAX+1.0));
|
*(U8*)end++ = (int) (256.0*rand()/(RAND_MAX+1.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t nAvail;
|
return true;
|
||||||
nAvail = (end - ptr) / itemSize;
|
|
||||||
if (nAvail < nItems)
|
|
||||||
return nAvail;
|
|
||||||
|
|
||||||
return nItems;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
#define __RDR_RANDOMSTREAM_H__
|
#define __RDR_RANDOMSTREAM_H__
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <rdr/InStream.h>
|
#include <rdr/BufferedInStream.h>
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
@@ -32,22 +32,17 @@
|
|||||||
|
|
||||||
namespace rdr {
|
namespace rdr {
|
||||||
|
|
||||||
class RandomStream : public InStream {
|
class RandomStream : public BufferedInStream {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
RandomStream();
|
RandomStream();
|
||||||
virtual ~RandomStream();
|
virtual ~RandomStream();
|
||||||
|
|
||||||
size_t pos();
|
private:
|
||||||
|
virtual bool fillBuffer(size_t maxSize, bool wait);
|
||||||
protected:
|
|
||||||
size_t overrun(size_t itemSize, size_t nItems, bool wait);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
U8* start;
|
|
||||||
size_t offset;
|
|
||||||
|
|
||||||
static unsigned int seed;
|
static unsigned int seed;
|
||||||
#ifdef RFB_HAVE_WINCRYPT
|
#ifdef RFB_HAVE_WINCRYPT
|
||||||
HCRYPTPROV provider;
|
HCRYPTPROV provider;
|
||||||
|
|||||||
@@ -30,21 +30,19 @@
|
|||||||
#ifdef HAVE_GNUTLS
|
#ifdef HAVE_GNUTLS
|
||||||
using namespace rdr;
|
using namespace rdr;
|
||||||
|
|
||||||
enum { DEFAULT_BUF_SIZE = 16384 };
|
|
||||||
|
|
||||||
ssize_t TLSInStream::pull(gnutls_transport_ptr_t str, void* data, size_t size)
|
ssize_t TLSInStream::pull(gnutls_transport_ptr_t str, void* data, size_t size)
|
||||||
{
|
{
|
||||||
TLSInStream* self= (TLSInStream*) str;
|
TLSInStream* self= (TLSInStream*) str;
|
||||||
InStream *in = self->in;
|
InStream *in = self->in;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!in->check(1, 1, false)) {
|
if (!in->check(1, false)) {
|
||||||
gnutls_transport_set_errno(self->session, EAGAIN);
|
gnutls_transport_set_errno(self->session, EAGAIN);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((size_t)(in->getend() - in->getptr()) < size)
|
if (in->avail() < size)
|
||||||
size = in->getend() - in->getptr();
|
size = in->avail();
|
||||||
|
|
||||||
in->readBytes(data, size);
|
in->readBytes(data, size);
|
||||||
|
|
||||||
@@ -57,12 +55,10 @@ ssize_t TLSInStream::pull(gnutls_transport_ptr_t str, void* data, size_t size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
TLSInStream::TLSInStream(InStream* _in, gnutls_session_t _session)
|
TLSInStream::TLSInStream(InStream* _in, gnutls_session_t _session)
|
||||||
: session(_session), in(_in), bufSize(DEFAULT_BUF_SIZE), offset(0)
|
: session(_session), in(_in)
|
||||||
{
|
{
|
||||||
gnutls_transport_ptr_t recv, send;
|
gnutls_transport_ptr_t recv, send;
|
||||||
|
|
||||||
ptr = end = start = new U8[bufSize];
|
|
||||||
|
|
||||||
gnutls_transport_set_pull_function(session, pull);
|
gnutls_transport_set_pull_function(session, pull);
|
||||||
gnutls_transport_get_ptr2(session, &recv, &send);
|
gnutls_transport_get_ptr2(session, &recv, &send);
|
||||||
gnutls_transport_set_ptr2(session, this, send);
|
gnutls_transport_set_ptr2(session, this, send);
|
||||||
@@ -71,40 +67,16 @@ TLSInStream::TLSInStream(InStream* _in, gnutls_session_t _session)
|
|||||||
TLSInStream::~TLSInStream()
|
TLSInStream::~TLSInStream()
|
||||||
{
|
{
|
||||||
gnutls_transport_set_pull_function(session, NULL);
|
gnutls_transport_set_pull_function(session, NULL);
|
||||||
|
|
||||||
delete[] start;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t TLSInStream::pos()
|
bool TLSInStream::fillBuffer(size_t maxSize, bool wait)
|
||||||
{
|
{
|
||||||
return offset + ptr - start;
|
size_t n = readTLS((U8*) end, maxSize, wait);
|
||||||
}
|
if (!wait && n == 0)
|
||||||
|
return false;
|
||||||
|
end += n;
|
||||||
|
|
||||||
size_t TLSInStream::overrun(size_t itemSize, size_t nItems, bool wait)
|
return true;
|
||||||
{
|
|
||||||
if (itemSize > bufSize)
|
|
||||||
throw Exception("TLSInStream overrun: max itemSize exceeded");
|
|
||||||
|
|
||||||
if (end - ptr != 0)
|
|
||||||
memmove(start, ptr, end - ptr);
|
|
||||||
|
|
||||||
offset += ptr - start;
|
|
||||||
end -= ptr - start;
|
|
||||||
ptr = start;
|
|
||||||
|
|
||||||
while ((size_t)(end - start) < itemSize) {
|
|
||||||
size_t n = readTLS((U8*) end, start + bufSize - end, wait);
|
|
||||||
if (!wait && n == 0)
|
|
||||||
return 0;
|
|
||||||
end += n;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t nAvail;
|
|
||||||
nAvail = (end - ptr) / itemSize;
|
|
||||||
if (nAvail < nItems)
|
|
||||||
return nAvail;
|
|
||||||
|
|
||||||
return nItems;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t TLSInStream::readTLS(U8* buf, size_t len, bool wait)
|
size_t TLSInStream::readTLS(U8* buf, size_t len, bool wait)
|
||||||
@@ -112,7 +84,7 @@ size_t TLSInStream::readTLS(U8* buf, size_t len, bool wait)
|
|||||||
int n;
|
int n;
|
||||||
|
|
||||||
if (gnutls_record_check_pending(session) == 0) {
|
if (gnutls_record_check_pending(session) == 0) {
|
||||||
n = in->check(1, 1, wait);
|
n = in->check(1, wait);
|
||||||
if (n == 0)
|
if (n == 0)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,27 +27,22 @@
|
|||||||
#ifdef HAVE_GNUTLS
|
#ifdef HAVE_GNUTLS
|
||||||
|
|
||||||
#include <gnutls/gnutls.h>
|
#include <gnutls/gnutls.h>
|
||||||
#include <rdr/InStream.h>
|
#include <rdr/BufferedInStream.h>
|
||||||
|
|
||||||
namespace rdr {
|
namespace rdr {
|
||||||
|
|
||||||
class TLSInStream : public InStream {
|
class TLSInStream : public BufferedInStream {
|
||||||
public:
|
public:
|
||||||
TLSInStream(InStream* in, gnutls_session_t session);
|
TLSInStream(InStream* in, gnutls_session_t session);
|
||||||
virtual ~TLSInStream();
|
virtual ~TLSInStream();
|
||||||
|
|
||||||
size_t pos();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t overrun(size_t itemSize, size_t nItems, bool wait);
|
virtual bool fillBuffer(size_t maxSize, bool wait);
|
||||||
size_t readTLS(U8* buf, size_t len, bool wait);
|
size_t readTLS(U8* buf, size_t len, bool wait);
|
||||||
static ssize_t pull(gnutls_transport_ptr_t str, void* data, size_t size);
|
static ssize_t pull(gnutls_transport_ptr_t str, void* data, size_t size);
|
||||||
|
|
||||||
gnutls_session_t session;
|
gnutls_session_t session;
|
||||||
InStream* in;
|
InStream* in;
|
||||||
size_t bufSize;
|
|
||||||
size_t offset;
|
|
||||||
U8* start;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -93,19 +93,12 @@ void TLSOutStream::flush()
|
|||||||
out->flush();
|
out->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t TLSOutStream::overrun(size_t itemSize, size_t nItems)
|
void TLSOutStream::overrun(size_t needed)
|
||||||
{
|
{
|
||||||
if (itemSize > bufSize)
|
if (needed > bufSize)
|
||||||
throw Exception("TLSOutStream overrun: max itemSize exceeded");
|
throw Exception("TLSOutStream overrun: buffer size exceeded");
|
||||||
|
|
||||||
flush();
|
flush();
|
||||||
|
|
||||||
size_t nAvail;
|
|
||||||
nAvail = (end - ptr) / itemSize;
|
|
||||||
if (nAvail < nItems)
|
|
||||||
return nAvail;
|
|
||||||
|
|
||||||
return nItems;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t TLSOutStream::writeTLS(const U8* data, size_t length)
|
size_t TLSOutStream::writeTLS(const U8* data, size_t length)
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ namespace rdr {
|
|||||||
size_t length();
|
size_t length();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
size_t overrun(size_t itemSize, size_t nItems);
|
virtual void overrun(size_t needed);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t writeTLS(const U8* data, size_t length);
|
size_t writeTLS(const U8* data, size_t length);
|
||||||
|
|||||||
@@ -24,41 +24,30 @@
|
|||||||
|
|
||||||
using namespace rdr;
|
using namespace rdr;
|
||||||
|
|
||||||
enum { DEFAULT_BUF_SIZE = 16384 };
|
ZlibInStream::ZlibInStream()
|
||||||
|
: underlying(0), zs(NULL), bytesIn(0)
|
||||||
ZlibInStream::ZlibInStream(size_t bufSize_)
|
|
||||||
: underlying(0), bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0),
|
|
||||||
zs(NULL), bytesIn(0)
|
|
||||||
{
|
{
|
||||||
ptr = end = start = new U8[bufSize];
|
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
ZlibInStream::~ZlibInStream()
|
ZlibInStream::~ZlibInStream()
|
||||||
{
|
{
|
||||||
deinit();
|
deinit();
|
||||||
delete [] start;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZlibInStream::setUnderlying(InStream* is, size_t bytesIn_)
|
void ZlibInStream::setUnderlying(InStream* is, size_t bytesIn_)
|
||||||
{
|
{
|
||||||
underlying = is;
|
underlying = is;
|
||||||
bytesIn = bytesIn_;
|
bytesIn = bytesIn_;
|
||||||
ptr = end = start;
|
skip(avail());
|
||||||
}
|
|
||||||
|
|
||||||
size_t ZlibInStream::pos()
|
|
||||||
{
|
|
||||||
return offset + ptr - start;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZlibInStream::flushUnderlying()
|
void ZlibInStream::flushUnderlying()
|
||||||
{
|
{
|
||||||
ptr = end = start;
|
|
||||||
|
|
||||||
while (bytesIn > 0) {
|
while (bytesIn > 0) {
|
||||||
decompress(true);
|
if (!check(1))
|
||||||
end = start; // throw away any data
|
throw Exception("ZlibInStream: failed to flush remaining stream data");
|
||||||
|
skip(avail());
|
||||||
}
|
}
|
||||||
|
|
||||||
setUnderlying(NULL, 0);
|
setUnderlying(NULL, 0);
|
||||||
@@ -96,47 +85,18 @@ void ZlibInStream::deinit()
|
|||||||
zs = NULL;
|
zs = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ZlibInStream::overrun(size_t itemSize, size_t nItems, bool wait)
|
bool ZlibInStream::fillBuffer(size_t maxSize, bool wait)
|
||||||
{
|
|
||||||
if (itemSize > bufSize)
|
|
||||||
throw Exception("ZlibInStream overrun: max itemSize exceeded");
|
|
||||||
|
|
||||||
if (end - ptr != 0)
|
|
||||||
memmove(start, ptr, end - ptr);
|
|
||||||
|
|
||||||
offset += ptr - start;
|
|
||||||
end -= ptr - start;
|
|
||||||
ptr = start;
|
|
||||||
|
|
||||||
while ((size_t)(end - ptr) < itemSize) {
|
|
||||||
if (!decompress(wait))
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t nAvail;
|
|
||||||
nAvail = (end - ptr) / itemSize;
|
|
||||||
if (nAvail < nItems)
|
|
||||||
return nAvail;
|
|
||||||
|
|
||||||
return nItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
// decompress() calls the decompressor once. Note that this won't necessarily
|
|
||||||
// generate any output data - it may just consume some input data. Returns
|
|
||||||
// false if wait is false and we would block on the underlying stream.
|
|
||||||
|
|
||||||
bool ZlibInStream::decompress(bool wait)
|
|
||||||
{
|
{
|
||||||
if (!underlying)
|
if (!underlying)
|
||||||
throw Exception("ZlibInStream overrun: no underlying stream");
|
throw Exception("ZlibInStream overrun: no underlying stream");
|
||||||
|
|
||||||
zs->next_out = (U8*)end;
|
zs->next_out = (U8*)end;
|
||||||
zs->avail_out = start + bufSize - end;
|
zs->avail_out = maxSize;
|
||||||
|
|
||||||
size_t n = underlying->check(1, 1, wait);
|
size_t n = underlying->check(1, wait);
|
||||||
if (n == 0) return false;
|
if (n == 0) return false;
|
||||||
zs->next_in = (U8*)underlying->getptr();
|
zs->next_in = (U8*)underlying->getptr();
|
||||||
zs->avail_in = underlying->getend() - underlying->getptr();
|
zs->avail_in = underlying->avail();
|
||||||
if (zs->avail_in > bytesIn)
|
if (zs->avail_in > bytesIn)
|
||||||
zs->avail_in = bytesIn;
|
zs->avail_in = bytesIn;
|
||||||
|
|
||||||
|
|||||||
@@ -24,38 +24,32 @@
|
|||||||
#ifndef __RDR_ZLIBINSTREAM_H__
|
#ifndef __RDR_ZLIBINSTREAM_H__
|
||||||
#define __RDR_ZLIBINSTREAM_H__
|
#define __RDR_ZLIBINSTREAM_H__
|
||||||
|
|
||||||
#include <rdr/InStream.h>
|
#include <rdr/BufferedInStream.h>
|
||||||
|
|
||||||
struct z_stream_s;
|
struct z_stream_s;
|
||||||
|
|
||||||
namespace rdr {
|
namespace rdr {
|
||||||
|
|
||||||
class ZlibInStream : public InStream {
|
class ZlibInStream : public BufferedInStream {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
ZlibInStream();
|
||||||
ZlibInStream(size_t bufSize=0);
|
|
||||||
virtual ~ZlibInStream();
|
virtual ~ZlibInStream();
|
||||||
|
|
||||||
void setUnderlying(InStream* is, size_t bytesIn);
|
void setUnderlying(InStream* is, size_t bytesIn);
|
||||||
void flushUnderlying();
|
void flushUnderlying();
|
||||||
size_t pos();
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
void deinit();
|
void deinit();
|
||||||
|
|
||||||
size_t overrun(size_t itemSize, size_t nItems, bool wait);
|
virtual bool fillBuffer(size_t maxSize, bool wait);
|
||||||
bool decompress(bool wait);
|
|
||||||
|
|
||||||
|
private:
|
||||||
InStream* underlying;
|
InStream* underlying;
|
||||||
size_t bufSize;
|
|
||||||
size_t offset;
|
|
||||||
z_stream_s* zs;
|
z_stream_s* zs;
|
||||||
size_t bytesIn;
|
size_t bytesIn;
|
||||||
U8* start;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end of namespace rdr
|
} // end of namespace rdr
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ using namespace rdr;
|
|||||||
|
|
||||||
enum { DEFAULT_BUF_SIZE = 16384 };
|
enum { DEFAULT_BUF_SIZE = 16384 };
|
||||||
|
|
||||||
ZlibOutStream::ZlibOutStream(OutStream* os, size_t bufSize_, int compressLevel)
|
ZlibOutStream::ZlibOutStream(OutStream* os, int compressLevel)
|
||||||
: underlying(os), compressionLevel(compressLevel), newLevel(compressLevel),
|
: underlying(os), compressionLevel(compressLevel), newLevel(compressLevel),
|
||||||
bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
|
bufSize(DEFAULT_BUF_SIZE), offset(0)
|
||||||
{
|
{
|
||||||
zs = new z_stream;
|
zs = new z_stream;
|
||||||
zs->zalloc = Z_NULL;
|
zs->zalloc = Z_NULL;
|
||||||
@@ -95,18 +95,18 @@ void ZlibOutStream::flush()
|
|||||||
ptr = start;
|
ptr = start;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ZlibOutStream::overrun(size_t itemSize, size_t nItems)
|
void ZlibOutStream::overrun(size_t needed)
|
||||||
{
|
{
|
||||||
#ifdef ZLIBOUT_DEBUG
|
#ifdef ZLIBOUT_DEBUG
|
||||||
fprintf(stderr,"zos overrun\n");
|
fprintf(stderr,"zos overrun\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (itemSize > bufSize)
|
if (needed > bufSize)
|
||||||
throw Exception("ZlibOutStream overrun: max itemSize exceeded");
|
throw Exception("ZlibOutStream overrun: buffer size exceeded");
|
||||||
|
|
||||||
checkCompressionLevel();
|
checkCompressionLevel();
|
||||||
|
|
||||||
while ((size_t)(end - ptr) < itemSize) {
|
while (avail() < needed) {
|
||||||
zs->next_in = start;
|
zs->next_in = start;
|
||||||
zs->avail_in = ptr - start;
|
zs->avail_in = ptr - start;
|
||||||
|
|
||||||
@@ -126,13 +126,6 @@ size_t ZlibOutStream::overrun(size_t itemSize, size_t nItems)
|
|||||||
ptr -= zs->next_in - start;
|
ptr -= zs->next_in - start;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t nAvail;
|
|
||||||
nAvail = (end - ptr) / itemSize;
|
|
||||||
if (nAvail < nItems)
|
|
||||||
return nAvail;
|
|
||||||
|
|
||||||
return nItems;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZlibOutStream::deflate(int flush)
|
void ZlibOutStream::deflate(int flush)
|
||||||
@@ -148,7 +141,7 @@ void ZlibOutStream::deflate(int flush)
|
|||||||
do {
|
do {
|
||||||
underlying->check(1);
|
underlying->check(1);
|
||||||
zs->next_out = underlying->getptr();
|
zs->next_out = underlying->getptr();
|
||||||
zs->avail_out = underlying->getend() - underlying->getptr();
|
zs->avail_out = underlying->avail();
|
||||||
|
|
||||||
#ifdef ZLIBOUT_DEBUG
|
#ifdef ZLIBOUT_DEBUG
|
||||||
fprintf(stderr,"zos: calling deflate, avail_in %d, avail_out %d\n",
|
fprintf(stderr,"zos: calling deflate, avail_in %d, avail_out %d\n",
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ namespace rdr {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ZlibOutStream(OutStream* os=0, size_t bufSize=0, int compressionLevel=-1);
|
ZlibOutStream(OutStream* os=0, int compressionLevel=-1);
|
||||||
virtual ~ZlibOutStream();
|
virtual ~ZlibOutStream();
|
||||||
|
|
||||||
void setUnderlying(OutStream* os);
|
void setUnderlying(OutStream* os);
|
||||||
@@ -45,7 +45,7 @@ namespace rdr {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
size_t overrun(size_t itemSize, size_t nItems);
|
virtual void overrun(size_t needed);
|
||||||
void deflate(int flush);
|
void deflate(int flush);
|
||||||
void checkCompressionLevel();
|
void checkCompressionLevel();
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <rfb/Exception.h>
|
#include <rfb/Exception.h>
|
||||||
|
#include <rfb/clipboardTypes.h>
|
||||||
#include <rfb/fenceTypes.h>
|
#include <rfb/fenceTypes.h>
|
||||||
#include <rfb/CMsgReader.h>
|
#include <rfb/CMsgReader.h>
|
||||||
#include <rfb/CMsgWriter.h>
|
#include <rfb/CMsgWriter.h>
|
||||||
@@ -42,7 +43,8 @@ CConnection::CConnection()
|
|||||||
: csecurity(0), is(0), os(0), reader_(0), writer_(0),
|
: csecurity(0), is(0), os(0), reader_(0), writer_(0),
|
||||||
shared(false),
|
shared(false),
|
||||||
state_(RFBSTATE_UNINITIALISED), useProtocol3_3(false),
|
state_(RFBSTATE_UNINITIALISED), useProtocol3_3(false),
|
||||||
framebuffer(NULL), decoder(this)
|
framebuffer(NULL), decoder(this),
|
||||||
|
serverClipboard(NULL), hasLocalClipboard(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +56,7 @@ CConnection::~CConnection()
|
|||||||
reader_ = 0;
|
reader_ = 0;
|
||||||
delete writer_;
|
delete writer_;
|
||||||
writer_ = 0;
|
writer_ = 0;
|
||||||
|
strFree(serverClipboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
|
void CConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
|
||||||
@@ -342,6 +345,79 @@ void CConnection::dataRect(const Rect& r, int encoding)
|
|||||||
decoder.decodeRect(r, encoding, framebuffer);
|
decoder.decodeRect(r, encoding, framebuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CConnection::serverCutText(const char* str)
|
||||||
|
{
|
||||||
|
hasLocalClipboard = false;
|
||||||
|
|
||||||
|
strFree(serverClipboard);
|
||||||
|
serverClipboard = NULL;
|
||||||
|
|
||||||
|
serverClipboard = latin1ToUTF8(str);
|
||||||
|
|
||||||
|
handleClipboardAnnounce(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CConnection::handleClipboardCaps(rdr::U32 flags,
|
||||||
|
const rdr::U32* lengths)
|
||||||
|
{
|
||||||
|
rdr::U32 sizes[] = { 0 };
|
||||||
|
|
||||||
|
CMsgHandler::handleClipboardCaps(flags, lengths);
|
||||||
|
|
||||||
|
writer()->writeClipboardCaps(rfb::clipboardUTF8 |
|
||||||
|
rfb::clipboardRequest |
|
||||||
|
rfb::clipboardPeek |
|
||||||
|
rfb::clipboardNotify |
|
||||||
|
rfb::clipboardProvide,
|
||||||
|
sizes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CConnection::handleClipboardRequest(rdr::U32 flags)
|
||||||
|
{
|
||||||
|
if (!(flags & rfb::clipboardUTF8))
|
||||||
|
return;
|
||||||
|
if (!hasLocalClipboard)
|
||||||
|
return;
|
||||||
|
handleClipboardRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CConnection::handleClipboardPeek(rdr::U32 flags)
|
||||||
|
{
|
||||||
|
if (!hasLocalClipboard)
|
||||||
|
return;
|
||||||
|
if (cp.clipboardFlags() & rfb::clipboardNotify)
|
||||||
|
writer()->writeClipboardNotify(rfb::clipboardUTF8);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CConnection::handleClipboardNotify(rdr::U32 flags)
|
||||||
|
{
|
||||||
|
strFree(serverClipboard);
|
||||||
|
serverClipboard = NULL;
|
||||||
|
|
||||||
|
if (flags & rfb::clipboardUTF8) {
|
||||||
|
hasLocalClipboard = false;
|
||||||
|
handleClipboardAnnounce(true);
|
||||||
|
} else {
|
||||||
|
handleClipboardAnnounce(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CConnection::handleClipboardProvide(rdr::U32 flags,
|
||||||
|
const size_t* lengths,
|
||||||
|
const rdr::U8* const* data)
|
||||||
|
{
|
||||||
|
if (!(flags & rfb::clipboardUTF8))
|
||||||
|
return;
|
||||||
|
|
||||||
|
strFree(serverClipboard);
|
||||||
|
serverClipboard = NULL;
|
||||||
|
|
||||||
|
serverClipboard = convertLF((const char*)data[0], lengths[0]);
|
||||||
|
|
||||||
|
// FIXME: Should probably verify that this data was actually requested
|
||||||
|
handleClipboardData(serverClipboard);
|
||||||
|
}
|
||||||
|
|
||||||
void CConnection::authSuccess()
|
void CConnection::authSuccess()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -364,3 +440,53 @@ void CConnection::fence(rdr::U32 flags, unsigned len, const char data[])
|
|||||||
|
|
||||||
writer()->writeFence(flags, len, data);
|
writer()->writeFence(flags, len, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CConnection::handleClipboardRequest()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CConnection::handleClipboardAnnounce(bool available)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CConnection::handleClipboardData(const char* data)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CConnection::requestClipboard()
|
||||||
|
{
|
||||||
|
if (serverClipboard != NULL) {
|
||||||
|
handleClipboardData(serverClipboard);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cp.clipboardFlags() & rfb::clipboardRequest)
|
||||||
|
writer()->writeClipboardRequest(rfb::clipboardUTF8);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CConnection::announceClipboard(bool available)
|
||||||
|
{
|
||||||
|
hasLocalClipboard = available;
|
||||||
|
|
||||||
|
if (cp.clipboardFlags() & rfb::clipboardNotify)
|
||||||
|
writer()->writeClipboardNotify(available ? rfb::clipboardUTF8 : 0);
|
||||||
|
else {
|
||||||
|
if (available)
|
||||||
|
handleClipboardRequest();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CConnection::sendClipboardData(const char* data)
|
||||||
|
{
|
||||||
|
if (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()->writeClientCutText(latin1.buf, strlen(latin1.buf));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -107,6 +107,17 @@ namespace rfb {
|
|||||||
virtual void framebufferUpdateEnd();
|
virtual void framebufferUpdateEnd();
|
||||||
virtual void dataRect(const Rect& r, int encoding);
|
virtual void dataRect(const Rect& r, int encoding);
|
||||||
|
|
||||||
|
virtual void serverCutText(const char* str);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
|
||||||
// Methods to be overridden in a derived class
|
// Methods to be overridden in a derived class
|
||||||
|
|
||||||
@@ -121,9 +132,43 @@ namespace rfb {
|
|||||||
// derived class must call on to CConnection::serverInit().
|
// derived class must call on to CConnection::serverInit().
|
||||||
virtual void serverInit();
|
virtual void serverInit();
|
||||||
|
|
||||||
|
// handleClipboardRequest() is called whenever the server requests
|
||||||
|
// the client to send over its clipboard data. It will only be
|
||||||
|
// called after the client has first announced a clipboard change
|
||||||
|
// via announceClipboard().
|
||||||
|
virtual void handleClipboardRequest();
|
||||||
|
|
||||||
|
// handleClipboardAnnounce() is called to indicate a change in the
|
||||||
|
// clipboard on the server. Call requestClipboard() to access the
|
||||||
|
// actual data.
|
||||||
|
virtual void handleClipboardAnnounce(bool available);
|
||||||
|
|
||||||
|
// handleClipboardData() is called when the server 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
|
||||||
|
// server received the request.
|
||||||
|
virtual void handleClipboardData(const char* data);
|
||||||
|
|
||||||
|
|
||||||
// Other methods
|
// Other methods
|
||||||
|
|
||||||
|
// requestClipboard() will result in a request to the server to
|
||||||
|
// transfer its clipboard data. A call to handleClipboardData()
|
||||||
|
// will be made once the data is available.
|
||||||
|
virtual void requestClipboard();
|
||||||
|
|
||||||
|
// announceClipboard() informs the server of changes to the
|
||||||
|
// clipboard on the client. The server may later request the
|
||||||
|
// clipboard data via handleClipboardRequest().
|
||||||
|
virtual void announceClipboard(bool available);
|
||||||
|
|
||||||
|
// sendClipboardData() transfers the clipboard data to the server
|
||||||
|
// and should be called whenever the server has requested the
|
||||||
|
// clipboard via handleClipboardRequest().
|
||||||
|
virtual void sendClipboardData(const char* data);
|
||||||
|
|
||||||
|
|
||||||
CMsgReader* reader() { return reader_; }
|
CMsgReader* reader() { return reader_; }
|
||||||
CMsgWriter* writer() { return writer_; }
|
CMsgWriter* writer() { return writer_; }
|
||||||
|
|
||||||
@@ -190,6 +235,9 @@ namespace rfb {
|
|||||||
|
|
||||||
ModifiablePixelBuffer* framebuffer;
|
ModifiablePixelBuffer* framebuffer;
|
||||||
DecodeManager decoder;
|
DecodeManager decoder;
|
||||||
|
|
||||||
|
char* serverClipboard;
|
||||||
|
bool hasLocalClipboard;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -92,3 +92,26 @@ void CMsgHandler::setLEDState(unsigned int state)
|
|||||||
{
|
{
|
||||||
cp.setLEDState(state);
|
cp.setLEDState(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CMsgHandler::handleClipboardCaps(rdr::U32 flags, const rdr::U32* lengths)
|
||||||
|
{
|
||||||
|
cp.setClipboardCaps(flags, lengths);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMsgHandler::handleClipboardRequest(rdr::U32 flags)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMsgHandler::handleClipboardPeek(rdr::U32 flags)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMsgHandler::handleClipboardNotify(rdr::U32 flags)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMsgHandler::handleClipboardProvide(rdr::U32 flags,
|
||||||
|
const size_t* lengths,
|
||||||
|
const rdr::U8* const* data)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ namespace rfb {
|
|||||||
int w, int h,
|
int w, int h,
|
||||||
const ScreenSet& layout);
|
const ScreenSet& layout);
|
||||||
virtual void setCursor(int width, int height, const Point& hotspot,
|
virtual void setCursor(int width, int height, const Point& hotspot,
|
||||||
const rdr::U8* data) = 0;
|
const rdr::U8* data, const bool resizing = false) = 0;
|
||||||
virtual void setPixelFormat(const PixelFormat& pf);
|
virtual void setPixelFormat(const PixelFormat& pf);
|
||||||
virtual void setName(const char* name);
|
virtual void setName(const char* name);
|
||||||
virtual void fence(rdr::U32 flags, unsigned len, const char data[]);
|
virtual void fence(rdr::U32 flags, unsigned len, const char data[]);
|
||||||
@@ -72,6 +72,15 @@ namespace rfb {
|
|||||||
|
|
||||||
virtual void setLEDState(unsigned int state);
|
virtual void setLEDState(unsigned int state);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
ConnParams cp;
|
ConnParams cp;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,14 @@
|
|||||||
*/
|
*/
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <rdr/OutStream.h>
|
#include <rdr/OutStream.h>
|
||||||
|
#include <rdr/MemOutStream.h>
|
||||||
|
#include <rdr/ZlibOutStream.h>
|
||||||
|
|
||||||
#include <rfb/msgTypes.h>
|
#include <rfb/msgTypes.h>
|
||||||
#include <rfb/fenceTypes.h>
|
#include <rfb/fenceTypes.h>
|
||||||
#include <rfb/encodings.h>
|
#include <rfb/encodings.h>
|
||||||
#include <rfb/qemuTypes.h>
|
#include <rfb/qemuTypes.h>
|
||||||
|
#include <rfb/clipboardTypes.h>
|
||||||
#include <rfb/Exception.h>
|
#include <rfb/Exception.h>
|
||||||
#include <rfb/PixelFormat.h>
|
#include <rfb/PixelFormat.h>
|
||||||
#include <rfb/Rect.h>
|
#include <rfb/Rect.h>
|
||||||
@@ -57,7 +61,7 @@ void CMsgWriter::writeSetPixelFormat(const PixelFormat& pf)
|
|||||||
void CMsgWriter::writeSetEncodings(int nEncodings, rdr::U32* encodings)
|
void CMsgWriter::writeSetEncodings(int nEncodings, rdr::U32* encodings)
|
||||||
{
|
{
|
||||||
startMsg(msgTypeSetEncodings);
|
startMsg(msgTypeSetEncodings);
|
||||||
os->skip(1);
|
os->pad(1);
|
||||||
os->writeU16(nEncodings);
|
os->writeU16(nEncodings);
|
||||||
for (int i = 0; i < nEncodings; i++)
|
for (int i = 0; i < nEncodings; i++)
|
||||||
os->writeU32(encodings[i]);
|
os->writeU32(encodings[i]);
|
||||||
@@ -265,6 +269,104 @@ void CMsgWriter::writeClientCutText(const char* str, rdr::U32 len)
|
|||||||
endMsg();
|
endMsg();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CMsgWriter::writeClipboardCaps(rdr::U32 caps,
|
||||||
|
const rdr::U32* lengths)
|
||||||
|
{
|
||||||
|
size_t i, count;
|
||||||
|
|
||||||
|
if (!(cp->clipboardFlags() & clipboardCaps))
|
||||||
|
throw Exception("Server does not support clipboard \"caps\" action");
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
for (i = 0;i < 16;i++) {
|
||||||
|
if (caps & (1 << i))
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
startMsg(msgTypeClientCutText);
|
||||||
|
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 CMsgWriter::writeClipboardRequest(rdr::U32 flags)
|
||||||
|
{
|
||||||
|
if (!(cp->clipboardFlags() & clipboardRequest))
|
||||||
|
throw Exception("Server does not support clipboard \"request\" action");
|
||||||
|
|
||||||
|
startMsg(msgTypeClientCutText);
|
||||||
|
os->pad(3);
|
||||||
|
os->writeS32(-4);
|
||||||
|
os->writeU32(flags | clipboardRequest);
|
||||||
|
endMsg();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMsgWriter::writeClipboardPeek(rdr::U32 flags)
|
||||||
|
{
|
||||||
|
if (!(cp->clipboardFlags() & clipboardPeek))
|
||||||
|
throw Exception("Server does not support clipboard \"peek\" action");
|
||||||
|
|
||||||
|
startMsg(msgTypeClientCutText);
|
||||||
|
os->pad(3);
|
||||||
|
os->writeS32(-4);
|
||||||
|
os->writeU32(flags | clipboardPeek);
|
||||||
|
endMsg();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMsgWriter::writeClipboardNotify(rdr::U32 flags)
|
||||||
|
{
|
||||||
|
if (!(cp->clipboardFlags() & clipboardNotify))
|
||||||
|
throw Exception("Server does not support clipboard \"notify\" action");
|
||||||
|
|
||||||
|
startMsg(msgTypeClientCutText);
|
||||||
|
os->pad(3);
|
||||||
|
os->writeS32(-4);
|
||||||
|
os->writeU32(flags | clipboardNotify);
|
||||||
|
endMsg();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMsgWriter::writeClipboardProvide(rdr::U32 flags,
|
||||||
|
const size_t* lengths,
|
||||||
|
const rdr::U8* const* data)
|
||||||
|
{
|
||||||
|
rdr::MemOutStream mos;
|
||||||
|
rdr::ZlibOutStream zos;
|
||||||
|
|
||||||
|
int i, count;
|
||||||
|
|
||||||
|
if (!(cp->clipboardFlags() & clipboardProvide))
|
||||||
|
throw Exception("Server 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(msgTypeClientCutText);
|
||||||
|
os->pad(3);
|
||||||
|
os->writeS32(-(4 + mos.length()));
|
||||||
|
os->writeU32(flags | clipboardProvide);
|
||||||
|
os->writeBytes(mos.data(), mos.length());
|
||||||
|
endMsg();
|
||||||
|
}
|
||||||
|
|
||||||
void CMsgWriter::startMsg(int type)
|
void CMsgWriter::startMsg(int type)
|
||||||
{
|
{
|
||||||
os->writeU8(type);
|
os->writeU8(type);
|
||||||
|
|||||||
@@ -56,6 +56,13 @@ namespace rfb {
|
|||||||
void writePointerEvent(const Point& pos, int buttonMask);
|
void writePointerEvent(const Point& pos, int buttonMask);
|
||||||
void writeClientCutText(const char* str, rdr::U32 len);
|
void writeClientCutText(const char* str, rdr::U32 len);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void startMsg(int type);
|
void startMsg(int type);
|
||||||
void endMsg();
|
void endMsg();
|
||||||
|
|||||||
@@ -695,6 +695,8 @@ bool ComparingUpdateTracker::compare(bool skipScrollDetection, const Region &ski
|
|||||||
std::vector<Rect> rects;
|
std::vector<Rect> rects;
|
||||||
std::vector<Rect>::iterator i;
|
std::vector<Rect>::iterator i;
|
||||||
|
|
||||||
|
changedPerc = 100;
|
||||||
|
|
||||||
if (!enabled)
|
if (!enabled)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -749,8 +751,13 @@ bool ComparingUpdateTracker::compare(bool skipScrollDetection, const Region &ski
|
|||||||
for (i = rects.begin(); i != rects.end(); i++)
|
for (i = rects.begin(); i != rects.end(); i++)
|
||||||
totalPixels += i->area();
|
totalPixels += i->area();
|
||||||
newChanged.get_rects(&rects);
|
newChanged.get_rects(&rects);
|
||||||
for (i = rects.begin(); i != rects.end(); i++)
|
unsigned newchangedarea = 0;
|
||||||
|
for (i = rects.begin(); i != rects.end(); i++) {
|
||||||
missedPixels += i->area();
|
missedPixels += i->area();
|
||||||
|
newchangedarea += i->area();
|
||||||
|
}
|
||||||
|
|
||||||
|
changedPerc = newchangedarea * 100 / fb->area();
|
||||||
|
|
||||||
if (changed.equals(newChanged))
|
if (changed.equals(newChanged))
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ namespace rfb {
|
|||||||
virtual void getUpdateInfo(UpdateInfo* info, const Region& cliprgn);
|
virtual void getUpdateInfo(UpdateInfo* info, const Region& cliprgn);
|
||||||
virtual void clear();
|
virtual void clear();
|
||||||
|
|
||||||
|
rdr::U8 changedPerc;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void compareRect(const Rect& r, Region* newchanged, const Region &skipCursorArea);
|
void compareRect(const Rect& r, Region* newchanged, const Region &skipCursorArea);
|
||||||
PixelBuffer* fb;
|
PixelBuffer* fb;
|
||||||
|
|||||||
@@ -298,6 +298,11 @@ size_t Congestion::getBandwidth()
|
|||||||
return congWindow * 1000 / safeBaseRTT;
|
return congWindow * 1000 / safeBaseRTT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned Congestion::getPingTime() const
|
||||||
|
{
|
||||||
|
return safeBaseRTT;
|
||||||
|
}
|
||||||
|
|
||||||
void Congestion::debugTrace(const char* filename, int fd)
|
void Congestion::debugTrace(const char* filename, int fd)
|
||||||
{
|
{
|
||||||
#ifdef CONGESTION_TRACE
|
#ifdef CONGESTION_TRACE
|
||||||
|
|||||||
@@ -51,6 +51,8 @@ namespace rfb {
|
|||||||
// per second.
|
// per second.
|
||||||
size_t getBandwidth();
|
size_t getBandwidth();
|
||||||
|
|
||||||
|
unsigned getPingTime() const;
|
||||||
|
|
||||||
// debugTrace() writes the current congestion window, as well as the
|
// debugTrace() writes the current congestion window, as well as the
|
||||||
// congestion window of the underlying TCP layer, to the specified
|
// congestion window of the underlying TCP layer, to the specified
|
||||||
// file
|
// file
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include <rfb/Exception.h>
|
#include <rfb/Exception.h>
|
||||||
#include <rfb/encodings.h>
|
#include <rfb/encodings.h>
|
||||||
#include <rfb/ledStates.h>
|
#include <rfb/ledStates.h>
|
||||||
|
#include <rfb/clipboardTypes.h>
|
||||||
#include <rfb/ConnParams.h>
|
#include <rfb/ConnParams.h>
|
||||||
#include <rfb/ServerCore.h>
|
#include <rfb/ServerCore.h>
|
||||||
#include <rfb/SMsgHandler.h>
|
#include <rfb/SMsgHandler.h>
|
||||||
@@ -36,19 +37,26 @@ ConnParams::ConnParams()
|
|||||||
width(0), height(0), useCopyRect(false),
|
width(0), height(0), useCopyRect(false),
|
||||||
supportsLocalCursor(false), supportsLocalXCursor(false),
|
supportsLocalCursor(false), supportsLocalXCursor(false),
|
||||||
supportsLocalCursorWithAlpha(false),
|
supportsLocalCursorWithAlpha(false),
|
||||||
|
supportsVMWareCursor(false),
|
||||||
|
supportsCursorPosition(false),
|
||||||
supportsDesktopResize(false), supportsExtendedDesktopSize(false),
|
supportsDesktopResize(false), supportsExtendedDesktopSize(false),
|
||||||
supportsDesktopRename(false), supportsLastRect(false),
|
supportsDesktopRename(false), supportsLastRect(false),
|
||||||
supportsLEDState(false), supportsQEMUKeyEvent(false),
|
supportsLEDState(false), supportsQEMUKeyEvent(false),
|
||||||
supportsWEBP(false),
|
supportsWEBP(false),
|
||||||
supportsSetDesktopSize(false), supportsFence(false),
|
supportsSetDesktopSize(false), supportsFence(false),
|
||||||
supportsContinuousUpdates(false),
|
supportsContinuousUpdates(false), supportsExtendedClipboard(false),
|
||||||
compressLevel(2), qualityLevel(-1), fineQualityLevel(-1),
|
compressLevel(2), qualityLevel(-1), fineQualityLevel(-1),
|
||||||
subsampling(subsampleUndefined), name_(0), verStrPos(0),
|
subsampling(subsampleUndefined), name_(0), cursorPos_(0, 0), verStrPos(0),
|
||||||
ledState_(ledUnknown), shandler(NULL)
|
ledState_(ledUnknown), shandler(NULL)
|
||||||
{
|
{
|
||||||
memset(kasmPassed, 0, KASM_NUM_SETTINGS);
|
memset(kasmPassed, 0, KASM_NUM_SETTINGS);
|
||||||
setName("");
|
setName("");
|
||||||
cursor_ = new Cursor(0, 0, Point(), NULL);
|
cursor_ = new Cursor(0, 0, Point(), NULL);
|
||||||
|
|
||||||
|
clipFlags = clipboardUTF8 | clipboardRTF | clipboardHTML |
|
||||||
|
clipboardRequest | clipboardNotify | clipboardProvide;
|
||||||
|
memset(clipSizes, 0, sizeof(clipSizes));
|
||||||
|
clipSizes[0] = 20 * 1024 * 1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnParams::~ConnParams()
|
ConnParams::~ConnParams()
|
||||||
@@ -101,6 +109,11 @@ void ConnParams::setCursor(const Cursor& other)
|
|||||||
cursor_ = new Cursor(other);
|
cursor_ = new Cursor(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConnParams::setCursorPos(const Point& pos)
|
||||||
|
{
|
||||||
|
cursorPos_ = pos;
|
||||||
|
}
|
||||||
|
|
||||||
bool ConnParams::supportsEncoding(rdr::S32 encoding) const
|
bool ConnParams::supportsEncoding(rdr::S32 encoding) const
|
||||||
{
|
{
|
||||||
return encodings_.count(encoding) != 0;
|
return encodings_.count(encoding) != 0;
|
||||||
@@ -111,6 +124,7 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
|
|||||||
useCopyRect = false;
|
useCopyRect = false;
|
||||||
supportsLocalCursor = false;
|
supportsLocalCursor = false;
|
||||||
supportsLocalCursorWithAlpha = false;
|
supportsLocalCursorWithAlpha = false;
|
||||||
|
supportsVMWareCursor = false;
|
||||||
supportsDesktopResize = false;
|
supportsDesktopResize = false;
|
||||||
supportsExtendedDesktopSize = false;
|
supportsExtendedDesktopSize = false;
|
||||||
supportsLocalXCursor = false;
|
supportsLocalXCursor = false;
|
||||||
@@ -141,12 +155,18 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
|
|||||||
case pseudoEncodingCursorWithAlpha:
|
case pseudoEncodingCursorWithAlpha:
|
||||||
supportsLocalCursorWithAlpha = true;
|
supportsLocalCursorWithAlpha = true;
|
||||||
break;
|
break;
|
||||||
|
case pseudoEncodingVMwareCursor:
|
||||||
|
supportsVMWareCursor = true;
|
||||||
|
break;
|
||||||
case pseudoEncodingDesktopSize:
|
case pseudoEncodingDesktopSize:
|
||||||
supportsDesktopResize = true;
|
supportsDesktopResize = true;
|
||||||
break;
|
break;
|
||||||
case pseudoEncodingExtendedDesktopSize:
|
case pseudoEncodingExtendedDesktopSize:
|
||||||
supportsExtendedDesktopSize = true;
|
supportsExtendedDesktopSize = true;
|
||||||
break;
|
break;
|
||||||
|
case pseudoEncodingVMwareCursorPosition:
|
||||||
|
supportsCursorPosition = true;
|
||||||
|
break;
|
||||||
case pseudoEncodingDesktopName:
|
case pseudoEncodingDesktopName:
|
||||||
supportsDesktopRename = true;
|
supportsDesktopRename = true;
|
||||||
break;
|
break;
|
||||||
@@ -168,6 +188,9 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
|
|||||||
case pseudoEncodingContinuousUpdates:
|
case pseudoEncodingContinuousUpdates:
|
||||||
supportsContinuousUpdates = true;
|
supportsContinuousUpdates = true;
|
||||||
break;
|
break;
|
||||||
|
case pseudoEncodingExtendedClipboard:
|
||||||
|
supportsExtendedClipboard = true;
|
||||||
|
break;
|
||||||
case pseudoEncodingSubsamp1X:
|
case pseudoEncodingSubsamp1X:
|
||||||
subsampling = subsampleNone;
|
subsampling = subsampleNone;
|
||||||
break;
|
break;
|
||||||
@@ -259,3 +282,17 @@ void ConnParams::setLEDState(unsigned int state)
|
|||||||
{
|
{
|
||||||
ledState_ = state;
|
ledState_ = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConnParams::setClipboardCaps(rdr::U32 flags, const rdr::U32* lengths)
|
||||||
|
{
|
||||||
|
int i, num;
|
||||||
|
|
||||||
|
clipFlags = flags;
|
||||||
|
|
||||||
|
num = 0;
|
||||||
|
for (i = 0;i < 16;i++) {
|
||||||
|
if (!(flags & (1 << i)))
|
||||||
|
continue;
|
||||||
|
clipSizes[i] = lengths[num++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -84,6 +84,9 @@ namespace rfb {
|
|||||||
const Cursor& cursor() const { return *cursor_; }
|
const Cursor& cursor() const { return *cursor_; }
|
||||||
void setCursor(const Cursor& cursor);
|
void setCursor(const Cursor& cursor);
|
||||||
|
|
||||||
|
const Point& cursorPos() const { return cursorPos_; }
|
||||||
|
void setCursorPos(const Point& pos);
|
||||||
|
|
||||||
bool supportsEncoding(rdr::S32 encoding) const;
|
bool supportsEncoding(rdr::S32 encoding) const;
|
||||||
|
|
||||||
void setEncodings(int nEncodings, const rdr::S32* encodings);
|
void setEncodings(int nEncodings, const rdr::S32* encodings);
|
||||||
@@ -91,11 +94,16 @@ namespace rfb {
|
|||||||
unsigned int ledState() { return ledState_; }
|
unsigned int ledState() { return ledState_; }
|
||||||
void setLEDState(unsigned int state);
|
void setLEDState(unsigned int state);
|
||||||
|
|
||||||
|
rdr::U32 clipboardFlags() const { return clipFlags; }
|
||||||
|
void setClipboardCaps(rdr::U32 flags, const rdr::U32* lengths);
|
||||||
|
|
||||||
bool useCopyRect;
|
bool useCopyRect;
|
||||||
|
|
||||||
bool supportsLocalCursor;
|
bool supportsLocalCursor;
|
||||||
bool supportsLocalXCursor;
|
bool supportsLocalXCursor;
|
||||||
bool supportsLocalCursorWithAlpha;
|
bool supportsLocalCursorWithAlpha;
|
||||||
|
bool supportsVMWareCursor;
|
||||||
|
bool supportsCursorPosition;
|
||||||
bool supportsDesktopResize;
|
bool supportsDesktopResize;
|
||||||
bool supportsExtendedDesktopSize;
|
bool supportsExtendedDesktopSize;
|
||||||
bool supportsDesktopRename;
|
bool supportsDesktopRename;
|
||||||
@@ -107,6 +115,7 @@ namespace rfb {
|
|||||||
bool supportsSetDesktopSize;
|
bool supportsSetDesktopSize;
|
||||||
bool supportsFence;
|
bool supportsFence;
|
||||||
bool supportsContinuousUpdates;
|
bool supportsContinuousUpdates;
|
||||||
|
bool supportsExtendedClipboard;
|
||||||
|
|
||||||
int compressLevel;
|
int compressLevel;
|
||||||
int qualityLevel;
|
int qualityLevel;
|
||||||
@@ -136,11 +145,14 @@ namespace rfb {
|
|||||||
PixelFormat pf_;
|
PixelFormat pf_;
|
||||||
char* name_;
|
char* name_;
|
||||||
Cursor* cursor_;
|
Cursor* cursor_;
|
||||||
|
Point cursorPos_;
|
||||||
std::set<rdr::S32> encodings_;
|
std::set<rdr::S32> encodings_;
|
||||||
char verStr[13];
|
char verStr[13];
|
||||||
int verStrPos;
|
int verStrPos;
|
||||||
unsigned int ledState_;
|
unsigned int ledState_;
|
||||||
SMsgHandler *shandler;
|
SMsgHandler *shandler;
|
||||||
|
rdr::U32 clipFlags;
|
||||||
|
rdr::U32 clipSizes[16];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -359,6 +359,8 @@ void EncodeManager::doUpdate(bool allowLossy, const Region& changed_,
|
|||||||
changed = changed_;
|
changed = changed_;
|
||||||
|
|
||||||
gettimeofday(&start, NULL);
|
gettimeofday(&start, NULL);
|
||||||
|
memset(&jpegstats, 0, sizeof(codecstats_t));
|
||||||
|
memset(&webpstats, 0, sizeof(codecstats_t));
|
||||||
|
|
||||||
if (allowLossy && activeEncoders[encoderFullColour] == encoderTightWEBP) {
|
if (allowLossy && activeEncoders[encoderFullColour] == encoderTightWEBP) {
|
||||||
const unsigned rate = 1024 * 1000 / rfb::Server::frameRate;
|
const unsigned rate = 1024 * 1000 / rfb::Server::frameRate;
|
||||||
@@ -1014,6 +1016,7 @@ void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb,
|
|||||||
std::vector<uint8_t> isWebp, fromCache;
|
std::vector<uint8_t> isWebp, fromCache;
|
||||||
std::vector<Palette> palettes;
|
std::vector<Palette> palettes;
|
||||||
std::vector<std::vector<uint8_t> > compresseds;
|
std::vector<std::vector<uint8_t> > compresseds;
|
||||||
|
std::vector<uint32_t> ms;
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
|
|
||||||
if (rfb::Server::rectThreads > 0)
|
if (rfb::Server::rectThreads > 0)
|
||||||
@@ -1078,9 +1081,13 @@ void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb,
|
|||||||
palettes.resize(subrects.size());
|
palettes.resize(subrects.size());
|
||||||
compresseds.resize(subrects.size());
|
compresseds.resize(subrects.size());
|
||||||
scaledrects.resize(subrects.size());
|
scaledrects.resize(subrects.size());
|
||||||
|
ms.resize(subrects.size());
|
||||||
|
|
||||||
// In case the current resolution is above the max video res, and video was detected,
|
// In case the current resolution is above the max video res, and video was detected,
|
||||||
// scale to that res, keeping aspect ratio
|
// scale to that res, keeping aspect ratio
|
||||||
|
struct timeval scalestart;
|
||||||
|
gettimeofday(&scalestart, NULL);
|
||||||
|
|
||||||
const PixelBuffer *scaledpb = NULL;
|
const PixelBuffer *scaledpb = NULL;
|
||||||
if (videoDetected &&
|
if (videoDetected &&
|
||||||
(maxVideoX < pb->getRect().width() || maxVideoY < pb->getRect().height())) {
|
(maxVideoX < pb->getRect().width() || maxVideoY < pb->getRect().height())) {
|
||||||
@@ -1129,15 +1136,25 @@ void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
scalingTime = msSince(&scalestart);
|
||||||
|
|
||||||
#pragma omp parallel for schedule(dynamic, 1)
|
#pragma omp parallel for schedule(dynamic, 1)
|
||||||
for (i = 0; i < subrects.size(); ++i) {
|
for (i = 0; i < subrects.size(); ++i) {
|
||||||
encoderTypes[i] = getEncoderType(subrects[i], pb, &palettes[i], compresseds[i],
|
encoderTypes[i] = getEncoderType(subrects[i], pb, &palettes[i], compresseds[i],
|
||||||
&isWebp[i], &fromCache[i],
|
&isWebp[i], &fromCache[i],
|
||||||
scaledpb, scaledrects[i]);
|
scaledpb, scaledrects[i], ms[i]);
|
||||||
checkWebpFallback(start);
|
checkWebpFallback(start);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < subrects.size(); ++i) {
|
||||||
|
if (encoderTypes[i] == encoderFullColour) {
|
||||||
|
if (isWebp[i])
|
||||||
|
webpstats.ms += ms[i];
|
||||||
|
else
|
||||||
|
jpegstats.ms += ms[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (start) {
|
if (start) {
|
||||||
encodingTime = msSince(start);
|
encodingTime = msSince(start);
|
||||||
|
|
||||||
@@ -1178,7 +1195,8 @@ void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb,
|
|||||||
uint8_t EncodeManager::getEncoderType(const Rect& rect, const PixelBuffer *pb,
|
uint8_t EncodeManager::getEncoderType(const Rect& rect, const PixelBuffer *pb,
|
||||||
Palette *pal, std::vector<uint8_t> &compressed,
|
Palette *pal, std::vector<uint8_t> &compressed,
|
||||||
uint8_t *isWebp, uint8_t *fromCache,
|
uint8_t *isWebp, uint8_t *fromCache,
|
||||||
const PixelBuffer *scaledpb, const Rect& scaledrect) const
|
const PixelBuffer *scaledpb, const Rect& scaledrect,
|
||||||
|
uint32_t &ms) const
|
||||||
{
|
{
|
||||||
struct RectInfo info;
|
struct RectInfo info;
|
||||||
unsigned int maxColours = 256;
|
unsigned int maxColours = 256;
|
||||||
@@ -1231,9 +1249,12 @@ uint8_t EncodeManager::getEncoderType(const Rect& rect, const PixelBuffer *pb,
|
|||||||
|
|
||||||
*isWebp = 0;
|
*isWebp = 0;
|
||||||
*fromCache = 0;
|
*fromCache = 0;
|
||||||
|
ms = 0;
|
||||||
if (type == encoderFullColour) {
|
if (type == encoderFullColour) {
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
const void *data;
|
const void *data;
|
||||||
|
struct timeval start;
|
||||||
|
gettimeofday(&start, NULL);
|
||||||
|
|
||||||
if (encCache->enabled &&
|
if (encCache->enabled &&
|
||||||
(data = encCache->get(activeEncoders[encoderFullColour],
|
(data = encCache->get(activeEncoders[encoderFullColour],
|
||||||
@@ -1274,6 +1295,8 @@ uint8_t EncodeManager::getEncoderType(const Rect& rect, const PixelBuffer *pb,
|
|||||||
compressed,
|
compressed,
|
||||||
videoDetected);
|
videoDetected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ms = msSince(&start);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete ppb;
|
delete ppb;
|
||||||
@@ -1292,10 +1315,15 @@ void EncodeManager::writeSubRect(const Rect& rect, const PixelBuffer *pb,
|
|||||||
encoder = startRect(rect, type, compressed.size() == 0, isWebp);
|
encoder = startRect(rect, type, compressed.size() == 0, isWebp);
|
||||||
|
|
||||||
if (compressed.size()) {
|
if (compressed.size()) {
|
||||||
if (isWebp)
|
if (isWebp) {
|
||||||
((TightWEBPEncoder *) encoder)->writeOnly(compressed);
|
((TightWEBPEncoder *) encoder)->writeOnly(compressed);
|
||||||
else
|
webpstats.area += rect.area();
|
||||||
|
webpstats.rects++;
|
||||||
|
} else {
|
||||||
((TightJPEGEncoder *) encoder)->writeOnly(compressed);
|
((TightJPEGEncoder *) encoder)->writeOnly(compressed);
|
||||||
|
jpegstats.area += rect.area();
|
||||||
|
jpegstats.rects++;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (encoder->flags & EncoderUseNativePF) {
|
if (encoder->flags & EncoderUseNativePF) {
|
||||||
ppb = preparePixelBuffer(rect, pb, false);
|
ppb = preparePixelBuffer(rect, pb, false);
|
||||||
|
|||||||
@@ -68,9 +68,24 @@ namespace rfb {
|
|||||||
const RenderedCursor* renderedCursor,
|
const RenderedCursor* renderedCursor,
|
||||||
size_t maxUpdateSize);
|
size_t maxUpdateSize);
|
||||||
|
|
||||||
|
void clearEncodingTime() {
|
||||||
|
encodingTime = 0;
|
||||||
|
};
|
||||||
|
|
||||||
unsigned getEncodingTime() const {
|
unsigned getEncodingTime() const {
|
||||||
return encodingTime;
|
return encodingTime;
|
||||||
};
|
};
|
||||||
|
unsigned getScalingTime() const {
|
||||||
|
return scalingTime;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct codecstats_t {
|
||||||
|
uint32_t ms;
|
||||||
|
uint32_t area;
|
||||||
|
uint32_t rects;
|
||||||
|
};
|
||||||
|
|
||||||
|
codecstats_t jpegstats, webpstats;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void doUpdate(bool allowLossy, const Region& changed,
|
void doUpdate(bool allowLossy, const Region& changed,
|
||||||
@@ -105,7 +120,8 @@ namespace rfb {
|
|||||||
uint8_t getEncoderType(const Rect& rect, const PixelBuffer *pb, Palette *pal,
|
uint8_t getEncoderType(const Rect& rect, const PixelBuffer *pb, Palette *pal,
|
||||||
std::vector<uint8_t> &compressed, uint8_t *isWebp,
|
std::vector<uint8_t> &compressed, uint8_t *isWebp,
|
||||||
uint8_t *fromCache,
|
uint8_t *fromCache,
|
||||||
const PixelBuffer *scaledpb, const Rect& scaledrect) const;
|
const PixelBuffer *scaledpb, const Rect& scaledrect,
|
||||||
|
uint32_t &ms) const;
|
||||||
virtual bool handleTimeout(Timer* t);
|
virtual bool handleTimeout(Timer* t);
|
||||||
|
|
||||||
bool checkSolidTile(const Rect& r, const rdr::U8* colourValue,
|
bool checkSolidTile(const Rect& r, const rdr::U8* colourValue,
|
||||||
@@ -183,6 +199,7 @@ namespace rfb {
|
|||||||
bool webpTookTooLong;
|
bool webpTookTooLong;
|
||||||
unsigned encodingTime;
|
unsigned encodingTime;
|
||||||
unsigned maxEncodingTime, framesSinceEncPrint;
|
unsigned maxEncodingTime, framesSinceEncPrint;
|
||||||
|
unsigned scalingTime;
|
||||||
|
|
||||||
EncCache *encCache;
|
EncCache *encCache;
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ JpegInitDestination(j_compress_ptr cinfo)
|
|||||||
|
|
||||||
jc->clear();
|
jc->clear();
|
||||||
dest->pub.next_output_byte = jc->getptr();
|
dest->pub.next_output_byte = jc->getptr();
|
||||||
dest->pub.free_in_buffer = jc->getend() - jc->getptr();
|
dest->pub.free_in_buffer = jc->avail();
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean
|
static boolean
|
||||||
@@ -95,9 +95,9 @@ JpegEmptyOutputBuffer(j_compress_ptr cinfo)
|
|||||||
JpegCompressor *jc = dest->instance;
|
JpegCompressor *jc = dest->instance;
|
||||||
|
|
||||||
jc->setptr(jc->getend());
|
jc->setptr(jc->getend());
|
||||||
jc->overrun(jc->getend() - jc->getstart(), 1);
|
jc->check(jc->length());
|
||||||
dest->pub.next_output_byte = jc->getptr();
|
dest->pub.next_output_byte = jc->getptr();
|
||||||
dest->pub.free_in_buffer = jc->getend() - jc->getptr();
|
dest->pub.free_in_buffer = jc->avail();
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,12 +47,6 @@ namespace rfb {
|
|||||||
|
|
||||||
void writeBytes(const void*, int);
|
void writeBytes(const void*, int);
|
||||||
|
|
||||||
inline rdr::U8* getstart() { return start; }
|
|
||||||
|
|
||||||
virtual inline size_t overrun(size_t itemSize, size_t nItems) {
|
|
||||||
return MemOutStream::overrun(itemSize, nItems);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
struct jpeg_compress_struct *cinfo;
|
struct jpeg_compress_struct *cinfo;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <rfb/Exception.h>
|
#include <rfb/Exception.h>
|
||||||
#include <rfb/Security.h>
|
#include <rfb/Security.h>
|
||||||
|
#include <rfb/clipboardTypes.h>
|
||||||
#include <rfb/msgTypes.h>
|
#include <rfb/msgTypes.h>
|
||||||
#include <rfb/fenceTypes.h>
|
#include <rfb/fenceTypes.h>
|
||||||
#include <rfb/SMsgReader.h>
|
#include <rfb/SMsgReader.h>
|
||||||
@@ -52,7 +53,8 @@ SConnection::SConnection()
|
|||||||
: readyForSetColourMapEntries(false),
|
: readyForSetColourMapEntries(false),
|
||||||
is(0), os(0), reader_(0), writer_(0),
|
is(0), os(0), reader_(0), writer_(0),
|
||||||
ssecurity(0), state_(RFBSTATE_UNINITIALISED),
|
ssecurity(0), state_(RFBSTATE_UNINITIALISED),
|
||||||
preferredEncoding(encodingRaw)
|
preferredEncoding(encodingRaw),
|
||||||
|
clientClipboard(NULL), hasLocalClipboard(false)
|
||||||
{
|
{
|
||||||
defaultMajorVersion = 3;
|
defaultMajorVersion = 3;
|
||||||
defaultMinorVersion = 8;
|
defaultMinorVersion = 8;
|
||||||
@@ -69,6 +71,7 @@ SConnection::~SConnection()
|
|||||||
reader_ = 0;
|
reader_ = 0;
|
||||||
delete writer_;
|
delete writer_;
|
||||||
writer_ = 0;
|
writer_ = 0;
|
||||||
|
strFree(clientClipboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
|
void SConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
|
||||||
@@ -281,6 +284,73 @@ void SConnection::setEncodings(int nEncodings, const rdr::S32* encodings)
|
|||||||
}
|
}
|
||||||
|
|
||||||
SMsgHandler::setEncodings(nEncodings, 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::clientCutText(const char* str, int len)
|
||||||
|
{
|
||||||
|
hasLocalClipboard = false;
|
||||||
|
|
||||||
|
strFree(clientClipboard);
|
||||||
|
clientClipboard = NULL;
|
||||||
|
|
||||||
|
clientClipboard = latin1ToUTF8(str);
|
||||||
|
|
||||||
|
handleClipboardAnnounce(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SConnection::handleClipboardRequest(rdr::U32 flags)
|
||||||
|
{
|
||||||
|
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()
|
void SConnection::supportsQEMUKeyEvent()
|
||||||
@@ -375,6 +445,58 @@ 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)
|
void SConnection::writeFakeColourMap(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|||||||
@@ -73,6 +73,15 @@ namespace rfb {
|
|||||||
|
|
||||||
virtual void setEncodings(int nEncodings, const rdr::S32* encodings);
|
virtual void setEncodings(int nEncodings, const rdr::S32* encodings);
|
||||||
|
|
||||||
|
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();
|
virtual void supportsQEMUKeyEvent();
|
||||||
|
|
||||||
// Methods to be overridden in a derived class
|
// Methods to be overridden in a derived class
|
||||||
@@ -118,6 +127,25 @@ namespace rfb {
|
|||||||
virtual void enableContinuousUpdates(bool enable,
|
virtual void enableContinuousUpdates(bool enable,
|
||||||
int x, int y, int w, int h);
|
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() {}
|
virtual void add_changed_all() {}
|
||||||
|
|
||||||
// setAccessRights() allows a security package to limit the access rights
|
// setAccessRights() allows a security package to limit the access rights
|
||||||
@@ -138,6 +166,22 @@ namespace rfb {
|
|||||||
|
|
||||||
// Other methods
|
// 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
|
// authenticated() returns true if the client has authenticated
|
||||||
// successfully.
|
// successfully.
|
||||||
bool authenticated() { return (state_ == RFBSTATE_INITIALISATION ||
|
bool authenticated() { return (state_ == RFBSTATE_INITIALISATION ||
|
||||||
@@ -203,6 +247,12 @@ namespace rfb {
|
|||||||
SSecurity* ssecurity;
|
SSecurity* ssecurity;
|
||||||
stateEnum state_;
|
stateEnum state_;
|
||||||
rdr::S32 preferredEncoding;
|
rdr::S32 preferredEncoding;
|
||||||
|
|
||||||
|
char* clientClipboard;
|
||||||
|
bool hasLocalClipboard;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define WRITER_PERMS (AccessKeyEvents | AccessPtrEvents | AccessCutText | AccessSetDesktopSize)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -77,6 +77,25 @@ namespace rfb {
|
|||||||
// pointerEvent(), keyEvent() and clientCutText() are called in response to
|
// pointerEvent(), keyEvent() and clientCutText() are called in response to
|
||||||
// the relevant RFB protocol messages from clients.
|
// the relevant RFB protocol messages from clients.
|
||||||
// See InputHandler for method signatures.
|
// 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) {}
|
||||||
|
|
||||||
|
// 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:
|
protected:
|
||||||
virtual ~SDesktop() {}
|
virtual ~SDesktop() {}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -64,6 +64,29 @@ void SMsgHandler::setEncodings(int nEncodings, const rdr::S32* encodings)
|
|||||||
supportsQEMUKeyEvent();
|
supportsQEMUKeyEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SMsgHandler::handleClipboardCaps(rdr::U32 flags, const rdr::U32* lengths)
|
||||||
|
{
|
||||||
|
cp.setClipboardCaps(flags, lengths);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SMsgHandler::handleClipboardRequest(rdr::U32 flags)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SMsgHandler::handleClipboardPeek(rdr::U32 flags)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SMsgHandler::handleClipboardNotify(rdr::U32 flags)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SMsgHandler::handleClipboardProvide(rdr::U32 flags,
|
||||||
|
const size_t* lengths,
|
||||||
|
const rdr::U8* const* data)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void SMsgHandler::supportsLocalCursor()
|
void SMsgHandler::supportsLocalCursor()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,17 @@ namespace rfb {
|
|||||||
virtual void enableContinuousUpdates(bool enable,
|
virtual void enableContinuousUpdates(bool enable,
|
||||||
int x, int y, int w, int h) = 0;
|
int x, int y, int w, int h) = 0;
|
||||||
|
|
||||||
virtual void sendStats() = 0;
|
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;
|
||||||
|
|
||||||
virtual bool canChangeKasmSettings() const = 0;
|
virtual bool canChangeKasmSettings() const = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,11 @@
|
|||||||
*/
|
*/
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <rdr/InStream.h>
|
#include <rdr/InStream.h>
|
||||||
|
#include <rdr/ZlibInStream.h>
|
||||||
|
|
||||||
#include <rfb/msgTypes.h>
|
#include <rfb/msgTypes.h>
|
||||||
#include <rfb/qemuTypes.h>
|
#include <rfb/qemuTypes.h>
|
||||||
|
#include <rfb/clipboardTypes.h>
|
||||||
#include <rfb/Exception.h>
|
#include <rfb/Exception.h>
|
||||||
#include <rfb/util.h>
|
#include <rfb/util.h>
|
||||||
#include <rfb/SMsgHandler.h>
|
#include <rfb/SMsgHandler.h>
|
||||||
@@ -77,6 +80,9 @@ void SMsgReader::readMsg()
|
|||||||
case msgTypeRequestStats:
|
case msgTypeRequestStats:
|
||||||
readRequestStats();
|
readRequestStats();
|
||||||
break;
|
break;
|
||||||
|
case msgTypeFrameStats:
|
||||||
|
readFrameStats();
|
||||||
|
break;
|
||||||
case msgTypeKeyEvent:
|
case msgTypeKeyEvent:
|
||||||
readKeyEvent();
|
readKeyEvent();
|
||||||
break;
|
break;
|
||||||
@@ -224,11 +230,15 @@ void SMsgReader::readPointerEvent()
|
|||||||
void SMsgReader::readClientCutText()
|
void SMsgReader::readClientCutText()
|
||||||
{
|
{
|
||||||
is->skip(3);
|
is->skip(3);
|
||||||
int len = is->readU32();
|
rdr::U32 len = is->readU32();
|
||||||
if (len < 0) {
|
|
||||||
throw Exception("Cut text too long.");
|
if (len & 0x80000000) {
|
||||||
|
rdr::S32 slen = len;
|
||||||
|
slen = -slen;
|
||||||
|
readExtendedClipboard(slen);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (len > maxCutText) {
|
if (len > (size_t)maxCutText) {
|
||||||
is->skip(len);
|
is->skip(len);
|
||||||
vlog.error("Cut text too long (%d bytes) - ignoring", len);
|
vlog.error("Cut text too long (%d bytes) - ignoring", len);
|
||||||
return;
|
return;
|
||||||
@@ -239,12 +249,114 @@ void SMsgReader::readClientCutText()
|
|||||||
handler->clientCutText(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");
|
||||||
|
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()
|
void SMsgReader::readRequestStats()
|
||||||
{
|
{
|
||||||
is->skip(3);
|
is->skip(3);
|
||||||
handler->sendStats();
|
handler->sendStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SMsgReader::readFrameStats()
|
||||||
|
{
|
||||||
|
is->skip(3);
|
||||||
|
rdr::U32 all = is->readU32();
|
||||||
|
rdr::U32 render = is->readU32();
|
||||||
|
handler->handleFrameStats(all, render);
|
||||||
|
}
|
||||||
|
|
||||||
void SMsgReader::readQEMUMessage()
|
void SMsgReader::readQEMUMessage()
|
||||||
{
|
{
|
||||||
int subType = is->readU8();
|
int subType = is->readU8();
|
||||||
|
|||||||
@@ -55,7 +55,9 @@ namespace rfb {
|
|||||||
void readKeyEvent();
|
void readKeyEvent();
|
||||||
void readPointerEvent();
|
void readPointerEvent();
|
||||||
void readClientCutText();
|
void readClientCutText();
|
||||||
|
void readExtendedClipboard(rdr::S32 len);
|
||||||
void readRequestStats();
|
void readRequestStats();
|
||||||
|
void readFrameStats();
|
||||||
|
|
||||||
void readQEMUMessage();
|
void readQEMUMessage();
|
||||||
void readQEMUKeyEvent();
|
void readQEMUKeyEvent();
|
||||||
|
|||||||
@@ -19,8 +19,12 @@
|
|||||||
*/
|
*/
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <rdr/OutStream.h>
|
#include <rdr/OutStream.h>
|
||||||
|
#include <rdr/MemOutStream.h>
|
||||||
|
#include <rdr/ZlibOutStream.h>
|
||||||
|
|
||||||
#include <rfb/msgTypes.h>
|
#include <rfb/msgTypes.h>
|
||||||
#include <rfb/fenceTypes.h>
|
#include <rfb/fenceTypes.h>
|
||||||
|
#include <rfb/clipboardTypes.h>
|
||||||
#include <rfb/Exception.h>
|
#include <rfb/Exception.h>
|
||||||
#include <rfb/ConnParams.h>
|
#include <rfb/ConnParams.h>
|
||||||
#include <rfb/UpdateTracker.h>
|
#include <rfb/UpdateTracker.h>
|
||||||
@@ -39,6 +43,8 @@ SMsgWriter::SMsgWriter(ConnParams* cp_, rdr::OutStream* os_)
|
|||||||
needSetDesktopSize(false), needExtendedDesktopSize(false),
|
needSetDesktopSize(false), needExtendedDesktopSize(false),
|
||||||
needSetDesktopName(false), needSetCursor(false),
|
needSetDesktopName(false), needSetCursor(false),
|
||||||
needSetXCursor(false), needSetCursorWithAlpha(false),
|
needSetXCursor(false), needSetCursorWithAlpha(false),
|
||||||
|
needSetVMWareCursor(false),
|
||||||
|
needCursorPos(false),
|
||||||
needLEDState(false), needQEMUKeyEvent(false)
|
needLEDState(false), needQEMUKeyEvent(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -88,6 +94,112 @@ void SMsgWriter::writeServerCutText(const char* str, int len)
|
|||||||
endMsg();
|
endMsg();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SMsgWriter::writeClipboardCaps(rdr::U32 caps,
|
||||||
|
const rdr::U32* lengths)
|
||||||
|
{
|
||||||
|
size_t i, count;
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
void SMsgWriter::writeStats(const char* str, int len)
|
void SMsgWriter::writeStats(const char* str, int len)
|
||||||
{
|
{
|
||||||
startMsg(msgTypeStats);
|
startMsg(msgTypeStats);
|
||||||
@@ -97,6 +209,12 @@ void SMsgWriter::writeStats(const char* str, int len)
|
|||||||
endMsg();
|
endMsg();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SMsgWriter::writeRequestFrameStats()
|
||||||
|
{
|
||||||
|
startMsg(msgTypeRequestFrameStats);
|
||||||
|
endMsg();
|
||||||
|
}
|
||||||
|
|
||||||
void SMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[])
|
void SMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[])
|
||||||
{
|
{
|
||||||
if (!cp->supportsFence)
|
if (!cp->supportsFence)
|
||||||
@@ -204,6 +322,24 @@ bool SMsgWriter::writeSetCursorWithAlpha()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SMsgWriter::writeSetVMwareCursor()
|
||||||
|
{
|
||||||
|
if (!cp->supportsVMWareCursor)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
needSetVMWareCursor = true;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SMsgWriter::writeCursorPos()
|
||||||
|
{
|
||||||
|
if (!cp->supportsEncoding(pseudoEncodingVMwareCursorPosition))
|
||||||
|
throw Exception("Client does not support cursor position");
|
||||||
|
|
||||||
|
needCursorPos = true;
|
||||||
|
}
|
||||||
|
|
||||||
bool SMsgWriter::writeLEDState()
|
bool SMsgWriter::writeLEDState()
|
||||||
{
|
{
|
||||||
if (!cp->supportsLEDState)
|
if (!cp->supportsLEDState)
|
||||||
@@ -230,7 +366,9 @@ bool SMsgWriter::needFakeUpdate()
|
|||||||
{
|
{
|
||||||
if (needSetDesktopName)
|
if (needSetDesktopName)
|
||||||
return true;
|
return true;
|
||||||
if (needSetCursor || needSetXCursor || needSetCursorWithAlpha)
|
if (needSetCursor || needSetXCursor || needSetCursorWithAlpha || needSetVMWareCursor)
|
||||||
|
return true;
|
||||||
|
if (needCursorPos)
|
||||||
return true;
|
return true;
|
||||||
if (needLEDState)
|
if (needLEDState)
|
||||||
return true;
|
return true;
|
||||||
@@ -284,6 +422,10 @@ void SMsgWriter::writeFramebufferUpdateStart(int nRects)
|
|||||||
nRects++;
|
nRects++;
|
||||||
if (needSetCursorWithAlpha)
|
if (needSetCursorWithAlpha)
|
||||||
nRects++;
|
nRects++;
|
||||||
|
if (needSetVMWareCursor)
|
||||||
|
nRects++;
|
||||||
|
if (needCursorPos)
|
||||||
|
nRects++;
|
||||||
if (needLEDState)
|
if (needLEDState)
|
||||||
nRects++;
|
nRects++;
|
||||||
if (needQEMUKeyEvent)
|
if (needQEMUKeyEvent)
|
||||||
@@ -399,6 +541,27 @@ void SMsgWriter::writePseudoRects()
|
|||||||
needSetCursorWithAlpha = false;
|
needSetCursorWithAlpha = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (needSetVMWareCursor) {
|
||||||
|
const Cursor& cursor = cp->cursor();
|
||||||
|
|
||||||
|
writeSetVMwareCursorRect(cursor.width(), cursor.height(),
|
||||||
|
cursor.hotspot().x, cursor.hotspot().y,
|
||||||
|
cursor.getBuffer());
|
||||||
|
needSetVMWareCursor = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needCursorPos) {
|
||||||
|
const Point& cursorPos = cp->cursorPos();
|
||||||
|
|
||||||
|
if (cp->supportsEncoding(pseudoEncodingVMwareCursorPosition)) {
|
||||||
|
writeSetVMwareCursorPositionRect(cursorPos.x, cursorPos.y);
|
||||||
|
} else {
|
||||||
|
throw Exception("Client does not support cursor position");
|
||||||
|
}
|
||||||
|
|
||||||
|
needCursorPos = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (needSetDesktopName) {
|
if (needSetDesktopName) {
|
||||||
writeSetDesktopNameRect(cp->name());
|
writeSetDesktopNameRect(cp->name());
|
||||||
needSetDesktopName = false;
|
needSetDesktopName = false;
|
||||||
@@ -577,6 +740,42 @@ void SMsgWriter::writeSetCursorWithAlphaRect(int width, int height,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SMsgWriter::writeSetVMwareCursorRect(int width, int height,
|
||||||
|
int hotspotX, int hotspotY,
|
||||||
|
const rdr::U8* data)
|
||||||
|
{
|
||||||
|
if (!cp->supportsVMWareCursor)
|
||||||
|
throw Exception("Client does not support local cursors");
|
||||||
|
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
|
||||||
|
throw Exception("SMsgWriter::writeSetVMwareCursorRect: nRects out of sync");
|
||||||
|
|
||||||
|
os->writeS16(hotspotX);
|
||||||
|
os->writeS16(hotspotY);
|
||||||
|
os->writeU16(width);
|
||||||
|
os->writeU16(height);
|
||||||
|
os->writeU32(pseudoEncodingVMwareCursor);
|
||||||
|
|
||||||
|
os->writeU8(1); // Alpha cursor
|
||||||
|
os->pad(1);
|
||||||
|
|
||||||
|
// FIXME: Should alpha be premultiplied?
|
||||||
|
os->writeBytes(data, width*height*4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SMsgWriter::writeSetVMwareCursorPositionRect(int hotspotX, int hotspotY)
|
||||||
|
{
|
||||||
|
if (!cp->supportsEncoding(pseudoEncodingVMwareCursorPosition))
|
||||||
|
throw Exception("Client does not support cursor position");
|
||||||
|
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
|
||||||
|
throw Exception("SMsgWriter::writeSetVMwareCursorRect: nRects out of sync");
|
||||||
|
|
||||||
|
os->writeS16(hotspotX);
|
||||||
|
os->writeS16(hotspotY);
|
||||||
|
os->writeU16(0);
|
||||||
|
os->writeU16(0);
|
||||||
|
os->writeU32(pseudoEncodingVMwareCursorPosition);
|
||||||
|
}
|
||||||
|
|
||||||
void SMsgWriter::writeLEDStateRect(rdr::U8 state)
|
void SMsgWriter::writeLEDStateRect(rdr::U8 state)
|
||||||
{
|
{
|
||||||
if (!cp->supportsLEDState)
|
if (!cp->supportsLEDState)
|
||||||
|
|||||||
@@ -55,8 +55,18 @@ namespace rfb {
|
|||||||
// writeBell() and writeServerCutText() do the obvious thing.
|
// writeBell() and writeServerCutText() do the obvious thing.
|
||||||
void writeBell();
|
void writeBell();
|
||||||
void writeServerCutText(const char* str, int len);
|
void writeServerCutText(const char* str, int len);
|
||||||
|
|
||||||
|
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);
|
void writeStats(const char* str, int len);
|
||||||
|
|
||||||
|
void writeRequestFrameStats();
|
||||||
|
|
||||||
// writeFence() sends a new fence request or response to the client.
|
// writeFence() sends a new fence request or response to the client.
|
||||||
void writeFence(rdr::U32 flags, unsigned len, const char data[]);
|
void writeFence(rdr::U32 flags, unsigned len, const char data[]);
|
||||||
|
|
||||||
@@ -82,6 +92,10 @@ namespace rfb {
|
|||||||
bool writeSetCursor();
|
bool writeSetCursor();
|
||||||
bool writeSetXCursor();
|
bool writeSetXCursor();
|
||||||
bool writeSetCursorWithAlpha();
|
bool writeSetCursorWithAlpha();
|
||||||
|
bool writeSetVMwareCursor();
|
||||||
|
|
||||||
|
// Notifies the client that the cursor pointer was moved by the server.
|
||||||
|
void writeCursorPos();
|
||||||
|
|
||||||
// Same for LED state message
|
// Same for LED state message
|
||||||
bool writeLEDState();
|
bool writeLEDState();
|
||||||
@@ -138,6 +152,10 @@ namespace rfb {
|
|||||||
void writeSetCursorWithAlphaRect(int width, int height,
|
void writeSetCursorWithAlphaRect(int width, int height,
|
||||||
int hotspotX, int hotspotY,
|
int hotspotX, int hotspotY,
|
||||||
const rdr::U8* data);
|
const rdr::U8* data);
|
||||||
|
void writeSetVMwareCursorRect(int width, int height,
|
||||||
|
int hotspotX, int hotspotY,
|
||||||
|
const rdr::U8* data);
|
||||||
|
void writeSetVMwareCursorPositionRect(int hotspotX, int hotspotY);
|
||||||
void writeLEDStateRect(rdr::U8 state);
|
void writeLEDStateRect(rdr::U8 state);
|
||||||
void writeQEMUKeyEventRect();
|
void writeQEMUKeyEventRect();
|
||||||
|
|
||||||
@@ -153,6 +171,8 @@ namespace rfb {
|
|||||||
bool needSetCursor;
|
bool needSetCursor;
|
||||||
bool needSetXCursor;
|
bool needSetXCursor;
|
||||||
bool needSetCursorWithAlpha;
|
bool needSetCursorWithAlpha;
|
||||||
|
bool needSetVMWareCursor;
|
||||||
|
bool needCursorPos;
|
||||||
bool needLEDState;
|
bool needLEDState;
|
||||||
bool needQEMUKeyEvent;
|
bool needQEMUKeyEvent;
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ namespace rfb {
|
|||||||
dispatch elapsed Timer callbacks and to determine how long to wait in select() for
|
dispatch elapsed Timer callbacks and to determine how long to wait in select() for
|
||||||
the next timeout to occur.
|
the next timeout to occur.
|
||||||
|
|
||||||
|
For classes that can be derived it's best to use MethodTimer which can call a specific
|
||||||
|
method on the class, thus avoiding conflicts when subclassing.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct Timer {
|
struct Timer {
|
||||||
@@ -101,6 +103,19 @@ namespace rfb {
|
|||||||
static std::list<Timer*> pending;
|
static std::list<Timer*> pending;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<class T> class MethodTimer
|
||||||
|
: public Timer, public Timer::Callback {
|
||||||
|
public:
|
||||||
|
MethodTimer(T* obj_, bool (T::*cb_)(Timer*))
|
||||||
|
: Timer(this), obj(obj_), cb(cb_) {}
|
||||||
|
|
||||||
|
virtual bool handleTimeout(Timer* t) { return (obj->*cb)(t); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
T* obj;
|
||||||
|
bool (T::*cb)(Timer*);
|
||||||
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
* USA.
|
* USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <network/GetAPI.h>
|
||||||
#include <network/TcpSocket.h>
|
#include <network/TcpSocket.h>
|
||||||
|
|
||||||
#include <rfb/ComparingUpdateTracker.h>
|
#include <rfb/ComparingUpdateTracker.h>
|
||||||
@@ -61,7 +62,7 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
|
|||||||
continuousUpdates(false), encodeManager(this, &server_->encCache),
|
continuousUpdates(false), encodeManager(this, &server_->encCache),
|
||||||
needsPermCheck(false), pointerEventTime(0),
|
needsPermCheck(false), pointerEventTime(0),
|
||||||
clientHasCursor(false),
|
clientHasCursor(false),
|
||||||
accessRights(AccessDefault), startTime(time(0))
|
accessRights(AccessDefault), startTime(time(0)), frameTracking(false)
|
||||||
{
|
{
|
||||||
setStreams(&sock->inStream(), &sock->outStream());
|
setStreams(&sock->inStream(), &sock->outStream());
|
||||||
peerEndpoint.buf = sock->getPeerEndpoint();
|
peerEndpoint.buf = sock->getPeerEndpoint();
|
||||||
@@ -86,8 +87,9 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool write, owner;
|
bool write, owner;
|
||||||
if (!getPerms(write, owner) || !write)
|
if (!getPerms(write, owner) || !write) {
|
||||||
accessRights = (accessRights & ~(AccessPtrEvents | AccessKeyEvents | AccessSetDesktopSize));
|
accessRights &= ~WRITER_PERMS;
|
||||||
|
}
|
||||||
|
|
||||||
// Configure the socket
|
// Configure the socket
|
||||||
setSocketTimeouts();
|
setSocketTimeouts();
|
||||||
@@ -97,6 +99,9 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
|
|||||||
gettimeofday(&lastKeyEvent, NULL);
|
gettimeofday(&lastKeyEvent, NULL);
|
||||||
|
|
||||||
server->clients.push_front(this);
|
server->clients.push_front(this);
|
||||||
|
|
||||||
|
if (server->apimessager)
|
||||||
|
server->apimessager->mainUpdateUserInfo(checkOwnerConn(), server->clients.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -127,6 +132,11 @@ VNCSConnectionST::~VNCSConnectionST()
|
|||||||
server->clients.remove(this);
|
server->clients.remove(this);
|
||||||
|
|
||||||
delete [] fenceData;
|
delete [] fenceData;
|
||||||
|
|
||||||
|
if (server->apimessager) {
|
||||||
|
server->apimessager->mainUpdateUserInfo(checkOwnerConn(), server->clients.size());
|
||||||
|
server->apimessager->mainClearBottleneckStats(peerEndpoint.buf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -155,6 +165,17 @@ void VNCSConnectionST::close(const char* reason)
|
|||||||
server->lastDisconnectTime = time(0);
|
server->lastDisconnectTime = time(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (sock->outStream().bufferUsage() > 0) {
|
||||||
|
sock->cork(false);
|
||||||
|
sock->outStream().flush();
|
||||||
|
if (sock->outStream().bufferUsage() > 0)
|
||||||
|
vlog.error("Failed to flush remaining socket data on close");
|
||||||
|
}
|
||||||
|
} catch (rdr::Exception& e) {
|
||||||
|
vlog.error("Failed to flush remaining socket data on close: %s", e.str());
|
||||||
|
}
|
||||||
|
|
||||||
// Just shutdown the socket and mark our state as closing. Eventually the
|
// Just shutdown the socket and mark our state as closing. Eventually the
|
||||||
// calling code will call VNCServerST's removeSocket() method causing us to
|
// calling code will call VNCServerST's removeSocket() method causing us to
|
||||||
// be deleted.
|
// be deleted.
|
||||||
@@ -392,7 +413,31 @@ static void keylog(unsigned keysym, const char *client) {
|
|||||||
flushKeylog(client);
|
flushKeylog(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VNCSConnectionST::serverCutTextOrClose(const char *str, int len)
|
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 {
|
||||||
|
if (!(accessRights & AccessCutText)) return;
|
||||||
|
if (!rfb::Server::sendCutText) return;
|
||||||
|
if (state() != RFBSTATE_NORMAL) return;
|
||||||
|
announceClipboard(available);
|
||||||
|
} catch(rdr::Exception& e) {
|
||||||
|
close(e.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VNCSConnectionST::sendClipboardDataOrClose(const char* data)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (!(accessRights & AccessCutText)) return;
|
if (!(accessRights & AccessCutText)) return;
|
||||||
@@ -402,19 +447,19 @@ void VNCSConnectionST::serverCutTextOrClose(const char *str, int len)
|
|||||||
sock->getPeerAddress());
|
sock->getPeerAddress());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
int len = strlen(data);
|
||||||
const int origlen = len;
|
const int origlen = len;
|
||||||
if (rfb::Server::DLP_ClipSendMax && len > rfb::Server::DLP_ClipSendMax)
|
if (rfb::Server::DLP_ClipSendMax && len > rfb::Server::DLP_ClipSendMax)
|
||||||
len = rfb::Server::DLP_ClipSendMax;
|
len = rfb::Server::DLP_ClipSendMax;
|
||||||
cliplog(str, len, origlen, "sent", sock->getPeerAddress());
|
cliplog(data, len, origlen, "sent", sock->getPeerAddress());
|
||||||
if (state() == RFBSTATE_NORMAL)
|
if (state() != RFBSTATE_NORMAL) return;
|
||||||
writer()->writeServerCutText(str, len);
|
sendClipboardData(data, len);
|
||||||
gettimeofday(&lastClipboardOp, NULL);
|
gettimeofday(&lastClipboardOp, NULL);
|
||||||
} catch(rdr::Exception& e) {
|
} catch(rdr::Exception& e) {
|
||||||
close(e.str());
|
close(e.str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void VNCSConnectionST::setDesktopNameOrClose(const char *name)
|
void VNCSConnectionST::setDesktopNameOrClose(const char *name)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -506,6 +551,15 @@ void VNCSConnectionST::renderedCursorChange()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cursorPositionChange() is called whenever the cursor has changed position by
|
||||||
|
// the server. If the client supports being informed about these changes then
|
||||||
|
// it will arrange for the new cursor position to be sent to the client.
|
||||||
|
|
||||||
|
void VNCSConnectionST::cursorPositionChange()
|
||||||
|
{
|
||||||
|
setCursorPos();
|
||||||
|
}
|
||||||
|
|
||||||
// needRenderedCursor() returns true if this client needs the server-side
|
// needRenderedCursor() returns true if this client needs the server-side
|
||||||
// rendered cursor. This may be because it does not support local cursor or
|
// rendered cursor. This may be because it does not support local cursor or
|
||||||
// because the current cursor position has not been set by this client.
|
// because the current cursor position has not been set by this client.
|
||||||
@@ -522,6 +576,7 @@ bool VNCSConnectionST::needRenderedCursor()
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!cp.supportsLocalCursorWithAlpha &&
|
if (!cp.supportsLocalCursorWithAlpha &&
|
||||||
|
!cp.supportsVMWareCursor &&
|
||||||
!cp.supportsLocalCursor && !cp.supportsLocalXCursor)
|
!cp.supportsLocalCursor && !cp.supportsLocalXCursor)
|
||||||
return true;
|
return true;
|
||||||
if (!server->cursorPos.equals(pointerEventPos) &&
|
if (!server->cursorPos.equals(pointerEventPos) &&
|
||||||
@@ -826,24 +881,6 @@ void VNCSConnectionST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) {
|
|||||||
server->desktop->keyEvent(keysym, keycode, down);
|
server->desktop->keyEvent(keysym, keycode, down);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VNCSConnectionST::clientCutText(const char* str, int len)
|
|
||||||
{
|
|
||||||
if (!(accessRights & AccessCutText)) return;
|
|
||||||
if (!rfb::Server::acceptCutText) return;
|
|
||||||
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(str, len, origlen, "received", sock->getPeerAddress());
|
|
||||||
|
|
||||||
gettimeofday(&lastClipboardOp, NULL);
|
|
||||||
server->desktop->clientCutText(str, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental)
|
void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental)
|
||||||
{
|
{
|
||||||
Rect safeRect;
|
Rect safeRect;
|
||||||
@@ -863,7 +900,7 @@ void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental)
|
|||||||
|
|
||||||
// Just update the requested region.
|
// Just update the requested region.
|
||||||
// Framebuffer update will be sent a bit later, see processMessages().
|
// Framebuffer update will be sent a bit later, see processMessages().
|
||||||
Region reqRgn(r);
|
Region reqRgn(safeRect);
|
||||||
if (!incremental || !continuousUpdates)
|
if (!incremental || !continuousUpdates)
|
||||||
requested.assign_union(reqRgn);
|
requested.assign_union(reqRgn);
|
||||||
|
|
||||||
@@ -977,6 +1014,38 @@ void VNCSConnectionST::enableContinuousUpdates(bool enable,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VNCSConnectionST::handleClipboardRequest()
|
||||||
|
{
|
||||||
|
if (!(accessRights & AccessCutText)) return;
|
||||||
|
server->handleClipboardRequest(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VNCSConnectionST::handleClipboardAnnounce(bool available)
|
||||||
|
{
|
||||||
|
if (!(accessRights & AccessCutText)) return;
|
||||||
|
if (!rfb::Server::acceptCutText) return;
|
||||||
|
server->handleClipboardAnnounce(this, available);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VNCSConnectionST::handleClipboardData(const char* data, int len)
|
||||||
|
{
|
||||||
|
if (!(accessRights & AccessCutText)) return;
|
||||||
|
if (!rfb::Server::acceptCutText) return;
|
||||||
|
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
|
// supportsLocalCursor() is called whenever the status of
|
||||||
// cp.supportsLocalCursor has changed. If the client does now support local
|
// 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
|
// cursor, we make sure that the old server-side rendered cursor is cleaned up
|
||||||
@@ -1125,6 +1194,7 @@ bool VNCSConnectionST::isCongested()
|
|||||||
void VNCSConnectionST::writeFramebufferUpdate()
|
void VNCSConnectionST::writeFramebufferUpdate()
|
||||||
{
|
{
|
||||||
congestion.updatePosition(sock->outStream().length());
|
congestion.updatePosition(sock->outStream().length());
|
||||||
|
encodeManager.clearEncodingTime();
|
||||||
|
|
||||||
// We're in the middle of processing a command that's supposed to be
|
// We're in the middle of processing a command that's supposed to be
|
||||||
// synchronised. Allowing an update to slip out right now might violate
|
// synchronised. Allowing an update to slip out right now might violate
|
||||||
@@ -1158,9 +1228,9 @@ void VNCSConnectionST::writeFramebufferUpdate()
|
|||||||
close("User was deleted");
|
close("User was deleted");
|
||||||
return;
|
return;
|
||||||
} else if (!write) {
|
} else if (!write) {
|
||||||
accessRights = (accessRights & ~(AccessPtrEvents | AccessKeyEvents | AccessSetDesktopSize));
|
accessRights &= ~WRITER_PERMS;
|
||||||
} else {
|
} else {
|
||||||
accessRights |= AccessPtrEvents | AccessKeyEvents | AccessSetDesktopSize;
|
accessRights |= WRITER_PERMS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1170,6 +1240,9 @@ void VNCSConnectionST::writeFramebufferUpdate()
|
|||||||
// window.
|
// window.
|
||||||
sock->cork(true);
|
sock->cork(true);
|
||||||
|
|
||||||
|
if (frameTracking)
|
||||||
|
writer()->writeRequestFrameStats();
|
||||||
|
|
||||||
// First take care of any updates that cannot contain framebuffer data
|
// First take care of any updates that cannot contain framebuffer data
|
||||||
// changes.
|
// changes.
|
||||||
writeNoDataUpdate();
|
writeNoDataUpdate();
|
||||||
@@ -1400,7 +1473,7 @@ static void pruneStatList(std::list<struct timeval> &list, const struct timeval
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VNCSConnectionST::sendStats() {
|
void VNCSConnectionST::sendStats(const bool toClient) {
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
struct timeval now;
|
struct timeval now;
|
||||||
|
|
||||||
@@ -1439,8 +1512,28 @@ void VNCSConnectionST::sendStats() {
|
|||||||
|
|
||||||
#undef ten
|
#undef ten
|
||||||
|
|
||||||
vlog.info("Sending client stats:\n%s\n", buf);
|
if (toClient) {
|
||||||
writer()->writeStats(buf, strlen(buf));
|
vlog.info("Sending client stats:\n%s\n", buf);
|
||||||
|
writer()->writeStats(buf, strlen(buf));
|
||||||
|
} else if (server->apimessager) {
|
||||||
|
server->apimessager->mainUpdateBottleneckStats(peerEndpoint.buf, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VNCSConnectionST::handleFrameStats(rdr::U32 all, rdr::U32 render)
|
||||||
|
{
|
||||||
|
if (server->apimessager) {
|
||||||
|
const char *at = strchr(peerEndpoint.buf, '@');
|
||||||
|
if (!at)
|
||||||
|
at = peerEndpoint.buf;
|
||||||
|
else
|
||||||
|
at++;
|
||||||
|
|
||||||
|
server->apimessager->mainUpdateClientFrameStats(at, render, all,
|
||||||
|
congestion.getPingTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
frameTracking = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// setCursor() is called whenever the cursor has changed shape or pixel format.
|
// setCursor() is called whenever the cursor has changed shape or pixel format.
|
||||||
@@ -1461,16 +1554,33 @@ void VNCSConnectionST::setCursor()
|
|||||||
clientHasCursor = true;
|
clientHasCursor = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!writer()->writeSetCursorWithAlpha()) {
|
if (!writer()->writeSetVMwareCursor()) {
|
||||||
if (!writer()->writeSetCursor()) {
|
if (!writer()->writeSetCursorWithAlpha()) {
|
||||||
if (!writer()->writeSetXCursor()) {
|
if (!writer()->writeSetCursor()) {
|
||||||
// No client support
|
if (!writer()->writeSetXCursor()) {
|
||||||
return;
|
// No client support
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setCursorPos() is called whenever the cursor has changed position by the
|
||||||
|
// server. If the client supports being informed about these changes then it
|
||||||
|
// will arrange for the new cursor position to be sent to the client.
|
||||||
|
|
||||||
|
void VNCSConnectionST::setCursorPos()
|
||||||
|
{
|
||||||
|
if (state() != RFBSTATE_NORMAL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (cp.supportsCursorPosition) {
|
||||||
|
cp.setCursorPos(server->cursorPos);
|
||||||
|
writer()->writeCursorPos();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void VNCSConnectionST::setDesktopName(const char *name)
|
void VNCSConnectionST::setDesktopName(const char *name)
|
||||||
{
|
{
|
||||||
cp.setName(name);
|
cp.setName(name);
|
||||||
@@ -1537,3 +1647,15 @@ int VNCSConnectionST::getStatus()
|
|||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VNCSConnectionST::checkOwnerConn() const
|
||||||
|
{
|
||||||
|
std::list<VNCSConnectionST*>::const_iterator it;
|
||||||
|
|
||||||
|
for (it = server->clients.begin(); it != server->clients.end(); it++) {
|
||||||
|
bool write, owner;
|
||||||
|
if ((*it)->getPerms(write, owner) && owner)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|||||||
@@ -75,9 +75,11 @@ namespace rfb {
|
|||||||
void screenLayoutChangeOrClose(rdr::U16 reason);
|
void screenLayoutChangeOrClose(rdr::U16 reason);
|
||||||
void setCursorOrClose();
|
void setCursorOrClose();
|
||||||
void bellOrClose();
|
void bellOrClose();
|
||||||
void serverCutTextOrClose(const char *str, int len);
|
|
||||||
void setDesktopNameOrClose(const char *name);
|
void setDesktopNameOrClose(const char *name);
|
||||||
void setLEDStateOrClose(unsigned int state);
|
void setLEDStateOrClose(unsigned int state);
|
||||||
|
void requestClipboardOrClose();
|
||||||
|
void announceClipboardOrClose(bool available);
|
||||||
|
void sendClipboardDataOrClose(const char* data);
|
||||||
|
|
||||||
// checkIdleTimeout() returns the number of milliseconds left until the
|
// checkIdleTimeout() returns the number of milliseconds left until the
|
||||||
// idle timeout expires. If it has expired, the connection is closed and
|
// idle timeout expires. If it has expired, the connection is closed and
|
||||||
@@ -97,6 +99,11 @@ namespace rfb {
|
|||||||
// cursor.
|
// cursor.
|
||||||
void renderedCursorChange();
|
void renderedCursorChange();
|
||||||
|
|
||||||
|
// cursorPositionChange() is called whenever the cursor has changed position by
|
||||||
|
// the server. If the client supports being informed about these changes then
|
||||||
|
// it will arrange for the new cursor position to be sent to the client.
|
||||||
|
void cursorPositionChange();
|
||||||
|
|
||||||
// needRenderedCursor() returns true if this client needs the server-side
|
// needRenderedCursor() returns true if this client needs the server-side
|
||||||
// rendered cursor. This may be because it does not support local cursor
|
// rendered cursor. This may be because it does not support local cursor
|
||||||
// or because the current cursor position has not been set by this client.
|
// or because the current cursor position has not been set by this client.
|
||||||
@@ -157,6 +164,35 @@ namespace rfb {
|
|||||||
void setStatus(int status);
|
void setStatus(int status);
|
||||||
int getStatus();
|
int getStatus();
|
||||||
|
|
||||||
|
virtual void sendStats(const bool toClient = true);
|
||||||
|
virtual void handleFrameStats(rdr::U32 all, rdr::U32 render);
|
||||||
|
|
||||||
|
bool is_owner() const {
|
||||||
|
bool write, owner;
|
||||||
|
if (getPerms(write, owner) && owner)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setFrameTracking() {
|
||||||
|
frameTracking = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
EncodeManager::codecstats_t getJpegStats() const {
|
||||||
|
return encodeManager.jpegstats;
|
||||||
|
}
|
||||||
|
|
||||||
|
EncodeManager::codecstats_t getWebpStats() const {
|
||||||
|
return encodeManager.webpstats;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned getEncodingTime() const {
|
||||||
|
return encodeManager.getEncodingTime();
|
||||||
|
}
|
||||||
|
unsigned getScalingTime() const {
|
||||||
|
return encodeManager.getScalingTime();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// SConnection callbacks
|
// SConnection callbacks
|
||||||
|
|
||||||
@@ -170,19 +206,20 @@ namespace rfb {
|
|||||||
virtual void setPixelFormat(const PixelFormat& pf);
|
virtual void setPixelFormat(const PixelFormat& pf);
|
||||||
virtual void pointerEvent(const Point& pos, int buttonMask, const bool skipClick, const bool skipRelease);
|
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 keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down);
|
||||||
virtual void clientCutText(const char* str, int len);
|
|
||||||
virtual void framebufferUpdateRequest(const Rect& r, bool incremental);
|
virtual void framebufferUpdateRequest(const Rect& r, bool incremental);
|
||||||
virtual void setDesktopSize(int fb_width, int fb_height,
|
virtual void setDesktopSize(int fb_width, int fb_height,
|
||||||
const ScreenSet& layout);
|
const ScreenSet& layout);
|
||||||
virtual void fence(rdr::U32 flags, unsigned len, const char data[]);
|
virtual void fence(rdr::U32 flags, unsigned len, const char data[]);
|
||||||
virtual void enableContinuousUpdates(bool enable,
|
virtual void enableContinuousUpdates(bool enable,
|
||||||
int x, int y, int w, int h);
|
int x, int y, int w, int h);
|
||||||
|
virtual void handleClipboardRequest();
|
||||||
|
virtual void handleClipboardAnnounce(bool available);
|
||||||
|
virtual void handleClipboardData(const char* data, int len);
|
||||||
virtual void supportsLocalCursor();
|
virtual void supportsLocalCursor();
|
||||||
virtual void supportsFence();
|
virtual void supportsFence();
|
||||||
virtual void supportsContinuousUpdates();
|
virtual void supportsContinuousUpdates();
|
||||||
virtual void supportsLEDState();
|
virtual void supportsLEDState();
|
||||||
|
|
||||||
virtual void sendStats();
|
|
||||||
virtual bool canChangeKasmSettings() const {
|
virtual bool canChangeKasmSettings() const {
|
||||||
return (accessRights & (AccessPtrEvents | AccessKeyEvents)) ==
|
return (accessRights & (AccessPtrEvents | AccessKeyEvents)) ==
|
||||||
(AccessPtrEvents | AccessKeyEvents);
|
(AccessPtrEvents | AccessKeyEvents);
|
||||||
@@ -197,7 +234,7 @@ namespace rfb {
|
|||||||
|
|
||||||
bool write, owner;
|
bool write, owner;
|
||||||
if (!getPerms(write, owner) || !write)
|
if (!getPerms(write, owner) || !write)
|
||||||
accessRights = (accessRights & ~(AccessPtrEvents | AccessKeyEvents));
|
accessRights &= ~WRITER_PERMS;
|
||||||
needsPermCheck = false;
|
needsPermCheck = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,6 +247,8 @@ namespace rfb {
|
|||||||
|
|
||||||
bool getPerms(bool &write, bool &owner) const;
|
bool getPerms(bool &write, bool &owner) const;
|
||||||
|
|
||||||
|
bool checkOwnerConn() const;
|
||||||
|
|
||||||
// Congestion control
|
// Congestion control
|
||||||
void writeRTTPing();
|
void writeRTTPing();
|
||||||
bool isCongested();
|
bool isCongested();
|
||||||
@@ -223,6 +262,7 @@ namespace rfb {
|
|||||||
|
|
||||||
void screenLayoutChange(rdr::U16 reason);
|
void screenLayoutChange(rdr::U16 reason);
|
||||||
void setCursor();
|
void setCursor();
|
||||||
|
void setCursorPos();
|
||||||
void setDesktopName(const char *name);
|
void setDesktopName(const char *name);
|
||||||
void setLEDState(unsigned int state);
|
void setLEDState(unsigned int state);
|
||||||
void setSocketTimeouts();
|
void setSocketTimeouts();
|
||||||
@@ -284,6 +324,8 @@ namespace rfb {
|
|||||||
time_t startTime;
|
time_t startTime;
|
||||||
|
|
||||||
std::vector<CopyPassRect> copypassed;
|
std::vector<CopyPassRect> copypassed;
|
||||||
|
|
||||||
|
bool frameTracking;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -52,9 +52,21 @@ namespace rfb {
|
|||||||
// getPixelBuffer() returns a pointer to the PixelBuffer object.
|
// getPixelBuffer() returns a pointer to the PixelBuffer object.
|
||||||
virtual PixelBuffer* getPixelBuffer() const = 0;
|
virtual PixelBuffer* getPixelBuffer() const = 0;
|
||||||
|
|
||||||
// serverCutText() tells the server that the cut text has changed. This
|
// requestClipboard() will result in a request to a client to
|
||||||
// will normally be sent to all clients.
|
// transfer its clipboard data. A call to
|
||||||
virtual void serverCutText(const char* str, int len) = 0;
|
// 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.
|
// bell() tells the server that it should make all clients make a bell sound.
|
||||||
virtual void bell() = 0;
|
virtual void bell() = 0;
|
||||||
@@ -67,10 +79,12 @@ namespace rfb {
|
|||||||
// cursorData argument contains width*height rgba quadruplets with
|
// cursorData argument contains width*height rgba quadruplets with
|
||||||
// non-premultiplied alpha.
|
// non-premultiplied alpha.
|
||||||
virtual void setCursor(int width, int height, const Point& hotspot,
|
virtual void setCursor(int width, int height, const Point& hotspot,
|
||||||
const rdr::U8* cursorData) = 0;
|
const rdr::U8* cursorData, const bool resizing = false) = 0;
|
||||||
|
|
||||||
// setCursorPos() tells the server the current position of the cursor.
|
// setCursorPos() tells the server the current position of the cursor, and
|
||||||
virtual void setCursorPos(const Point& p) = 0;
|
// whether the server initiated that change (e.g. through another X11
|
||||||
|
// client calling XWarpPointer()).
|
||||||
|
virtual void setCursorPos(const Point& p, bool warped) = 0;
|
||||||
|
|
||||||
// setName() tells the server what desktop title to supply to clients
|
// setName() tells the server what desktop title to supply to clients
|
||||||
virtual void setName(const char* name) = 0;
|
virtual void setName(const char* name) = 0;
|
||||||
|
|||||||
@@ -123,12 +123,12 @@ static void parseRegionPart(const bool percents, rdr::U16 &pcdest, int &dest,
|
|||||||
VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
|
VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
|
||||||
: blHosts(&blacklist), desktop(desktop_), desktopStarted(false),
|
: blHosts(&blacklist), desktop(desktop_), desktopStarted(false),
|
||||||
blockCounter(0), pb(0), blackedpb(0), ledState(ledUnknown),
|
blockCounter(0), pb(0), blackedpb(0), ledState(ledUnknown),
|
||||||
name(strDup(name_)), pointerClient(0), comparer(0),
|
name(strDup(name_)), pointerClient(0), clipboardClient(0),
|
||||||
cursor(new Cursor(0, 0, Point(), NULL)),
|
comparer(0), cursor(new Cursor(0, 0, Point(), NULL)),
|
||||||
renderedCursorInvalid(false),
|
renderedCursorInvalid(false),
|
||||||
queryConnectionHandler(0), keyRemapper(&KeyRemapper::defInstance),
|
queryConnectionHandler(0), keyRemapper(&KeyRemapper::defInstance),
|
||||||
lastConnectionTime(0), disableclients(false),
|
lastConnectionTime(0), disableclients(false),
|
||||||
frameTimer(this), apimessager(NULL)
|
frameTimer(this), apimessager(NULL), trackingFrameStats(0)
|
||||||
{
|
{
|
||||||
lastUserInputTime = lastDisconnectTime = time(0);
|
lastUserInputTime = lastDisconnectTime = time(0);
|
||||||
slog.debug("creating single-threaded server %s", name.buf);
|
slog.debug("creating single-threaded server %s", name.buf);
|
||||||
@@ -210,6 +210,8 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
|
|||||||
if (inotify_add_watch(inotifyfd, kasmpasswdpath, IN_CLOSE_WRITE | IN_DELETE_SELF) < 0)
|
if (inotify_add_watch(inotifyfd, kasmpasswdpath, IN_CLOSE_WRITE | IN_DELETE_SELF) < 0)
|
||||||
slog.error("Failed to set watch");
|
slog.error("Failed to set watch");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trackingClient[0] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
VNCServerST::~VNCServerST()
|
VNCServerST::~VNCServerST()
|
||||||
@@ -271,6 +273,11 @@ void VNCServerST::removeSocket(network::Socket* sock) {
|
|||||||
std::list<VNCSConnectionST*>::iterator ci;
|
std::list<VNCSConnectionST*>::iterator ci;
|
||||||
for (ci = clients.begin(); ci != clients.end(); ci++) {
|
for (ci = clients.begin(); ci != clients.end(); ci++) {
|
||||||
if ((*ci)->getSock() == sock) {
|
if ((*ci)->getSock() == sock) {
|
||||||
|
|
||||||
|
if (clipboardClient == *ci)
|
||||||
|
handleClipboardAnnounce(*ci, false);
|
||||||
|
clipboardRequestors.remove(*ci);
|
||||||
|
|
||||||
// - Delete the per-Socket resources
|
// - Delete the per-Socket resources
|
||||||
delete *ci;
|
delete *ci;
|
||||||
|
|
||||||
@@ -502,6 +509,45 @@ 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;
|
||||||
|
|
||||||
|
if (available)
|
||||||
|
clipboardClient = NULL;
|
||||||
|
|
||||||
|
clipboardRequestors.clear();
|
||||||
|
|
||||||
|
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
|
||||||
|
ci_next = ci; ci_next++;
|
||||||
|
(*ci)->announceClipboard(available);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VNCServerST::sendClipboardData(const char* data)
|
||||||
|
{
|
||||||
|
std::list<VNCSConnectionST*>::iterator ci, ci_next;
|
||||||
|
|
||||||
|
if (strchr(data, '\r') != NULL)
|
||||||
|
throw Exception("Invalid carriage return in clipboard data");
|
||||||
|
|
||||||
|
for (ci = clipboardRequestors.begin();
|
||||||
|
ci != clipboardRequestors.end(); ci = ci_next) {
|
||||||
|
ci_next = ci; ci_next++;
|
||||||
|
(*ci)->sendClipboardDataOrClose(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
clipboardRequestors.clear();
|
||||||
|
}
|
||||||
|
|
||||||
void VNCServerST::bell()
|
void VNCServerST::bell()
|
||||||
{
|
{
|
||||||
std::list<VNCSConnectionST*>::iterator ci, ci_next;
|
std::list<VNCSConnectionST*>::iterator ci, ci_next;
|
||||||
@@ -511,15 +557,6 @@ void VNCServerST::bell()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VNCServerST::serverCutText(const char* str, int len)
|
|
||||||
{
|
|
||||||
std::list<VNCSConnectionST*>::iterator ci, ci_next;
|
|
||||||
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
|
|
||||||
ci_next = ci; ci_next++;
|
|
||||||
(*ci)->serverCutTextOrClose(str, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VNCServerST::setName(const char* name_)
|
void VNCServerST::setName(const char* name_)
|
||||||
{
|
{
|
||||||
name.replaceBuf(strDup(name_));
|
name.replaceBuf(strDup(name_));
|
||||||
@@ -549,7 +586,7 @@ void VNCServerST::add_copied(const Region& dest, const Point& delta)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VNCServerST::setCursor(int width, int height, const Point& newHotspot,
|
void VNCServerST::setCursor(int width, int height, const Point& newHotspot,
|
||||||
const rdr::U8* data)
|
const rdr::U8* data, const bool resizing)
|
||||||
{
|
{
|
||||||
delete cursor;
|
delete cursor;
|
||||||
cursor = new Cursor(width, height, newHotspot, data);
|
cursor = new Cursor(width, height, newHotspot, data);
|
||||||
@@ -557,6 +594,13 @@ void VNCServerST::setCursor(int width, int height, const Point& newHotspot,
|
|||||||
|
|
||||||
renderedCursorInvalid = true;
|
renderedCursorInvalid = true;
|
||||||
|
|
||||||
|
// If an app has an animated cursor on the resized edge, X internals
|
||||||
|
// will call for it to be rendered. Unlucky for us, the VNC screen
|
||||||
|
// is currently pointing to freed memory, and a cursor change
|
||||||
|
// would want to send a screen update. So, don't do that.
|
||||||
|
if (resizing)
|
||||||
|
return;
|
||||||
|
|
||||||
std::list<VNCSConnectionST*>::iterator ci, ci_next;
|
std::list<VNCSConnectionST*>::iterator ci, ci_next;
|
||||||
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
|
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
|
||||||
ci_next = ci; ci_next++;
|
ci_next = ci; ci_next++;
|
||||||
@@ -565,14 +609,17 @@ void VNCServerST::setCursor(int width, int height, const Point& newHotspot,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VNCServerST::setCursorPos(const Point& pos)
|
void VNCServerST::setCursorPos(const Point& pos, bool warped)
|
||||||
{
|
{
|
||||||
if (!cursorPos.equals(pos)) {
|
if (!cursorPos.equals(pos)) {
|
||||||
cursorPos = pos;
|
cursorPos = pos;
|
||||||
renderedCursorInvalid = true;
|
renderedCursorInvalid = true;
|
||||||
std::list<VNCSConnectionST*>::iterator ci;
|
std::list<VNCSConnectionST*>::iterator ci;
|
||||||
for (ci = clients.begin(); ci != clients.end(); ci++)
|
for (ci = clients.begin(); ci != clients.end(); ci++) {
|
||||||
(*ci)->renderedCursorChange();
|
(*ci)->renderedCursorChange();
|
||||||
|
if (warped)
|
||||||
|
(*ci)->cursorPositionChange();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -734,7 +781,8 @@ int VNCServerST::msToNextUpdate()
|
|||||||
return frameTimer.getRemainingMs();
|
return frameTimer.getRemainingMs();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void checkAPIMessages(network::GetAPIMessager *apimessager)
|
static void checkAPIMessages(network::GetAPIMessager *apimessager,
|
||||||
|
rdr::U8 &trackingFrameStats, char trackingClient[])
|
||||||
{
|
{
|
||||||
if (pthread_mutex_lock(&apimessager->userMutex))
|
if (pthread_mutex_lock(&apimessager->userMutex))
|
||||||
return;
|
return;
|
||||||
@@ -787,6 +835,20 @@ static void checkAPIMessages(network::GetAPIMessager *apimessager)
|
|||||||
slog.error("Tried to give control to nonexistent user %s", act.data.user);
|
slog.error("Tried to give control to nonexistent user %s", act.data.user);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case network::GetAPIMessager::WANT_FRAME_STATS_SERVERONLY:
|
||||||
|
trackingFrameStats = act.action;
|
||||||
|
break;
|
||||||
|
case network::GetAPIMessager::WANT_FRAME_STATS_ALL:
|
||||||
|
trackingFrameStats = act.action;
|
||||||
|
break;
|
||||||
|
case network::GetAPIMessager::WANT_FRAME_STATS_OWNER:
|
||||||
|
trackingFrameStats = act.action;
|
||||||
|
break;
|
||||||
|
case network::GetAPIMessager::WANT_FRAME_STATS_SPECIFIC:
|
||||||
|
trackingFrameStats = act.action;
|
||||||
|
memcpy(trackingClient, act.data.password, 128);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (set) {
|
if (set) {
|
||||||
@@ -883,6 +945,9 @@ void VNCServerST::writeUpdate()
|
|||||||
assert(blockCounter == 0);
|
assert(blockCounter == 0);
|
||||||
assert(desktopStarted);
|
assert(desktopStarted);
|
||||||
|
|
||||||
|
struct timeval start;
|
||||||
|
gettimeofday(&start, NULL);
|
||||||
|
|
||||||
if (DLPRegion.enabled) {
|
if (DLPRegion.enabled) {
|
||||||
comparer->enable_copyrect(false);
|
comparer->enable_copyrect(false);
|
||||||
blackOut();
|
blackOut();
|
||||||
@@ -909,6 +974,9 @@ void VNCServerST::writeUpdate()
|
|||||||
else
|
else
|
||||||
comparer->disable();
|
comparer->disable();
|
||||||
|
|
||||||
|
struct timeval beforeAnalysis;
|
||||||
|
gettimeofday(&beforeAnalysis, NULL);
|
||||||
|
|
||||||
// Skip scroll detection if the client is slow, and didn't get the previous one yet
|
// Skip scroll detection if the client is slow, and didn't get the previous one yet
|
||||||
if (comparer->compare(clients.size() == 1 && (*clients.begin())->has_copypassed(),
|
if (comparer->compare(clients.size() == 1 && (*clients.begin())->has_copypassed(),
|
||||||
cursorReg))
|
cursorReg))
|
||||||
@@ -916,6 +984,8 @@ void VNCServerST::writeUpdate()
|
|||||||
|
|
||||||
comparer->clear();
|
comparer->clear();
|
||||||
|
|
||||||
|
const unsigned analysisMs = msSince(&beforeAnalysis);
|
||||||
|
|
||||||
encCache.clear();
|
encCache.clear();
|
||||||
encCache.enabled = clients.size() > 1;
|
encCache.enabled = clients.size() > 1;
|
||||||
|
|
||||||
@@ -941,11 +1011,22 @@ void VNCServerST::writeUpdate()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned shottime = 0;
|
||||||
if (apimessager) {
|
if (apimessager) {
|
||||||
|
struct timeval shotstart;
|
||||||
|
gettimeofday(&shotstart, NULL);
|
||||||
apimessager->mainUpdateScreen(pb);
|
apimessager->mainUpdateScreen(pb);
|
||||||
|
shottime = msSince(&shotstart);
|
||||||
|
|
||||||
checkAPIMessages(apimessager);
|
trackingFrameStats = 0;
|
||||||
|
checkAPIMessages(apimessager, trackingFrameStats, trackingClient);
|
||||||
}
|
}
|
||||||
|
const rdr::U8 origtrackingFrameStats = trackingFrameStats;
|
||||||
|
|
||||||
|
EncodeManager::codecstats_t jpegstats, webpstats;
|
||||||
|
unsigned enctime = 0, scaletime = 0;
|
||||||
|
memset(&jpegstats, 0, sizeof(EncodeManager::codecstats_t));
|
||||||
|
memset(&webpstats, 0, sizeof(EncodeManager::codecstats_t));
|
||||||
|
|
||||||
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
|
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
|
||||||
ci_next = ci; ci_next++;
|
ci_next = ci; ci_next++;
|
||||||
@@ -953,10 +1034,68 @@ void VNCServerST::writeUpdate()
|
|||||||
if (permcheck)
|
if (permcheck)
|
||||||
(*ci)->recheckPerms();
|
(*ci)->recheckPerms();
|
||||||
|
|
||||||
|
if (trackingFrameStats == network::GetAPIMessager::WANT_FRAME_STATS_ALL ||
|
||||||
|
(trackingFrameStats == network::GetAPIMessager::WANT_FRAME_STATS_OWNER &&
|
||||||
|
(*ci)->is_owner()) ||
|
||||||
|
(trackingFrameStats == network::GetAPIMessager::WANT_FRAME_STATS_SPECIFIC &&
|
||||||
|
strstr((*ci)->getPeerEndpoint(), trackingClient))) {
|
||||||
|
|
||||||
|
(*ci)->setFrameTracking();
|
||||||
|
|
||||||
|
// Only one owner
|
||||||
|
if (trackingFrameStats == network::GetAPIMessager::WANT_FRAME_STATS_OWNER)
|
||||||
|
trackingFrameStats = network::GetAPIMessager::WANT_FRAME_STATS_SERVERONLY;
|
||||||
|
}
|
||||||
|
|
||||||
(*ci)->add_copied(ui.copied, ui.copy_delta);
|
(*ci)->add_copied(ui.copied, ui.copy_delta);
|
||||||
(*ci)->add_copypassed(ui.copypassed);
|
(*ci)->add_copypassed(ui.copypassed);
|
||||||
(*ci)->add_changed(ui.changed);
|
(*ci)->add_changed(ui.changed);
|
||||||
(*ci)->writeFramebufferUpdateOrClose();
|
(*ci)->writeFramebufferUpdateOrClose();
|
||||||
|
|
||||||
|
if (apimessager) {
|
||||||
|
(*ci)->sendStats(false);
|
||||||
|
const EncodeManager::codecstats_t subjpeg = (*ci)->getJpegStats();
|
||||||
|
const EncodeManager::codecstats_t subwebp = (*ci)->getWebpStats();
|
||||||
|
|
||||||
|
jpegstats.ms += subjpeg.ms;
|
||||||
|
jpegstats.area += subjpeg.area;
|
||||||
|
jpegstats.rects += subjpeg.rects;
|
||||||
|
|
||||||
|
webpstats.ms += subwebp.ms;
|
||||||
|
webpstats.area += subwebp.area;
|
||||||
|
webpstats.rects += subwebp.rects;
|
||||||
|
|
||||||
|
enctime += (*ci)->getEncodingTime();
|
||||||
|
scaletime += (*ci)->getScalingTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trackingFrameStats) {
|
||||||
|
if (enctime) {
|
||||||
|
const unsigned totalMs = msSince(&start);
|
||||||
|
|
||||||
|
if (apimessager)
|
||||||
|
apimessager->mainUpdateServerFrameStats(comparer->changedPerc, totalMs,
|
||||||
|
jpegstats.ms, webpstats.ms,
|
||||||
|
analysisMs,
|
||||||
|
jpegstats.area, webpstats.area,
|
||||||
|
jpegstats.rects, webpstats.rects,
|
||||||
|
enctime, scaletime, shottime,
|
||||||
|
pb->getRect().width(),
|
||||||
|
pb->getRect().height());
|
||||||
|
} else {
|
||||||
|
// Zero encoding time means this was a no-data frame; restore the stats request
|
||||||
|
if (apimessager && pthread_mutex_lock(&apimessager->userMutex) == 0) {
|
||||||
|
|
||||||
|
network::GetAPIMessager::action_data act;
|
||||||
|
act.action = (network::GetAPIMessager::USER_ACTION) origtrackingFrameStats;
|
||||||
|
memcpy(act.data.password, trackingClient, 128);
|
||||||
|
|
||||||
|
apimessager->actionQueue.push_back(act);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&apimessager->userMutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1049,3 +1188,32 @@ bool VNCServerST::getComparerState()
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VNCServerST::handleClipboardRequest(VNCSConnectionST* client)
|
||||||
|
{
|
||||||
|
clipboardRequestors.push_back(client);
|
||||||
|
if (clipboardRequestors.size() == 1)
|
||||||
|
desktop->handleClipboardRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VNCServerST::handleClipboardAnnounce(VNCSConnectionST* client,
|
||||||
|
bool available)
|
||||||
|
{
|
||||||
|
if (available)
|
||||||
|
clipboardClient = client;
|
||||||
|
else {
|
||||||
|
if (client != clipboardClient)
|
||||||
|
return;
|
||||||
|
clipboardClient = NULL;
|
||||||
|
}
|
||||||
|
desktop->handleClipboardAnnounce(available);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VNCServerST::handleClipboardData(VNCSConnectionST* client,
|
||||||
|
const char* data, int len)
|
||||||
|
{
|
||||||
|
if (client != clipboardClient)
|
||||||
|
return;
|
||||||
|
desktop->handleClipboardData(data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,12 +96,14 @@ namespace rfb {
|
|||||||
virtual void setPixelBuffer(PixelBuffer* pb);
|
virtual void setPixelBuffer(PixelBuffer* pb);
|
||||||
virtual void setScreenLayout(const ScreenSet& layout);
|
virtual void setScreenLayout(const ScreenSet& layout);
|
||||||
virtual PixelBuffer* getPixelBuffer() const { if (DLPRegion.enabled && blackedpb) return blackedpb; else return pb; }
|
virtual PixelBuffer* getPixelBuffer() const { if (DLPRegion.enabled && blackedpb) return blackedpb; else return pb; }
|
||||||
virtual void serverCutText(const char* str, int len);
|
virtual void requestClipboard();
|
||||||
|
virtual void announceClipboard(bool available);
|
||||||
|
virtual void sendClipboardData(const char* data);
|
||||||
virtual void add_changed(const Region ®ion);
|
virtual void add_changed(const Region ®ion);
|
||||||
virtual void add_copied(const Region &dest, const Point &delta);
|
virtual void add_copied(const Region &dest, const Point &delta);
|
||||||
virtual void setCursor(int width, int height, const Point& hotspot,
|
virtual void setCursor(int width, int height, const Point& hotspot,
|
||||||
const rdr::U8* data);
|
const rdr::U8* data, const bool resizing = false);
|
||||||
virtual void setCursorPos(const Point& p);
|
virtual void setCursorPos(const Point& p, bool warped);
|
||||||
virtual void setLEDState(unsigned state);
|
virtual void setLEDState(unsigned state);
|
||||||
|
|
||||||
virtual void bell();
|
virtual void bell();
|
||||||
@@ -189,6 +191,10 @@ namespace rfb {
|
|||||||
|
|
||||||
void setAPIMessager(network::GetAPIMessager *msgr) { apimessager = msgr; }
|
void setAPIMessager(network::GetAPIMessager *msgr) { apimessager = msgr; }
|
||||||
|
|
||||||
|
void handleClipboardRequest(VNCSConnectionST* client);
|
||||||
|
void handleClipboardAnnounce(VNCSConnectionST* client, bool available);
|
||||||
|
void handleClipboardData(VNCSConnectionST* client, const char* data, int len);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
friend class VNCSConnectionST;
|
friend class VNCSConnectionST;
|
||||||
@@ -217,6 +223,8 @@ namespace rfb {
|
|||||||
|
|
||||||
std::list<VNCSConnectionST*> clients;
|
std::list<VNCSConnectionST*> clients;
|
||||||
VNCSConnectionST* pointerClient;
|
VNCSConnectionST* pointerClient;
|
||||||
|
VNCSConnectionST* clipboardClient;
|
||||||
|
std::list<VNCSConnectionST*> clipboardRequestors;
|
||||||
std::list<network::Socket*> closingSockets;
|
std::list<network::Socket*> closingSockets;
|
||||||
|
|
||||||
static EncCache encCache;
|
static EncCache encCache;
|
||||||
@@ -259,6 +267,9 @@ namespace rfb {
|
|||||||
|
|
||||||
network::GetAPIMessager *apimessager;
|
network::GetAPIMessager *apimessager;
|
||||||
|
|
||||||
|
rdr::U8 trackingFrameStats;
|
||||||
|
char trackingClient[128];
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool enabled;
|
bool enabled;
|
||||||
int x1, y1, x2, y2;
|
int x1, y1, x2, y2;
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ IntParameter zlibLevel("ZlibLevel","Zlib compression level",-1);
|
|||||||
|
|
||||||
ZRLEEncoder::ZRLEEncoder(SConnection* conn)
|
ZRLEEncoder::ZRLEEncoder(SConnection* conn)
|
||||||
: Encoder(conn, encodingZRLE, EncoderPlain, 127),
|
: Encoder(conn, encodingZRLE, EncoderPlain, 127),
|
||||||
zos(0,0,zlibLevel), mos(129*1024)
|
zos(0,zlibLevel), mos(129*1024)
|
||||||
{
|
{
|
||||||
zos.setUnderlying(&mos);
|
zos.setUnderlying(&mos);
|
||||||
}
|
}
|
||||||
|
|||||||
41
common/rfb/clipboardTypes.h
Normal file
41
common/rfb/clipboardTypes.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/* Copyright 2019 Pierre Ossman for Cendio AB
|
||||||
|
*
|
||||||
|
* 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_CLIPBOARDTYPES_H__
|
||||||
|
#define __RFB_CLIPBOARDTYPES_H__
|
||||||
|
|
||||||
|
namespace rfb {
|
||||||
|
|
||||||
|
// Formats
|
||||||
|
const unsigned int clipboardUTF8 = 1 << 0;
|
||||||
|
const unsigned int clipboardRTF = 1 << 1;
|
||||||
|
const unsigned int clipboardHTML = 1 << 2;
|
||||||
|
const unsigned int clipboardDIB = 1 << 3;
|
||||||
|
const unsigned int clipboardFiles = 1 << 4;
|
||||||
|
|
||||||
|
const unsigned int clipboardFormatMask = 0x0000ffff;
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
const unsigned int clipboardCaps = 1 << 24;
|
||||||
|
const unsigned int clipboardRequest = 1 << 25;
|
||||||
|
const unsigned int clipboardPeek = 1 << 26;
|
||||||
|
const unsigned int clipboardNotify = 1 << 27;
|
||||||
|
const unsigned int clipboardProvide = 1 << 28;
|
||||||
|
|
||||||
|
const unsigned int clipboardActionMask = 0xff000000;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -85,6 +85,13 @@ namespace rfb {
|
|||||||
const int pseudoEncodingVideoOutTimeLevel1 = -1986;
|
const int pseudoEncodingVideoOutTimeLevel1 = -1986;
|
||||||
const int pseudoEncodingVideoOutTimeLevel100 = -1887;
|
const int pseudoEncodingVideoOutTimeLevel100 = -1887;
|
||||||
|
|
||||||
|
// VMware-specific
|
||||||
|
const int pseudoEncodingVMwareCursor = 0x574d5664;
|
||||||
|
const int pseudoEncodingVMwareCursorPosition = 0x574d5666;
|
||||||
|
|
||||||
|
// UltraVNC-specific
|
||||||
|
const int pseudoEncodingExtendedClipboard = 0xC0A1E5CE;
|
||||||
|
|
||||||
int encodingNum(const char* name);
|
int encodingNum(const char* name);
|
||||||
const char* encodingName(int num);
|
const char* encodingName(int num);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ namespace rfb {
|
|||||||
|
|
||||||
// kasm
|
// kasm
|
||||||
const int msgTypeStats = 178;
|
const int msgTypeStats = 178;
|
||||||
|
const int msgTypeRequestFrameStats = 179;
|
||||||
|
|
||||||
const int msgTypeServerFence = 248;
|
const int msgTypeServerFence = 248;
|
||||||
|
|
||||||
@@ -47,6 +48,7 @@ namespace rfb {
|
|||||||
|
|
||||||
// kasm
|
// kasm
|
||||||
const int msgTypeRequestStats = 178;
|
const int msgTypeRequestStats = 178;
|
||||||
|
const int msgTypeFrameStats = 179;
|
||||||
|
|
||||||
const int msgTypeClientFence = 248;
|
const int msgTypeClientFence = 248;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||||
|
* Copyright 2011-2019 Pierre Ossman for Cendio AB
|
||||||
*
|
*
|
||||||
* This is free software; you can redistribute it and/or modify
|
* This is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -63,6 +64,10 @@ namespace rfb {
|
|||||||
delete [] s;
|
delete [] s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void strFree(wchar_t* s) {
|
||||||
|
delete [] s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool strSplit(const char* src, const char limiter, char** out1, char** out2, bool fromEnd) {
|
bool strSplit(const char* src, const char limiter, char** out1, char** out2, bool fromEnd) {
|
||||||
CharArray out1old, out2old;
|
CharArray out1old, out2old;
|
||||||
@@ -107,6 +112,444 @@ namespace rfb {
|
|||||||
dest[src ? destlen-1 : 0] = 0;
|
dest[src ? destlen-1 : 0] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char* convertLF(const char* src, size_t bytes)
|
||||||
|
{
|
||||||
|
char* buffer;
|
||||||
|
size_t sz;
|
||||||
|
|
||||||
|
char* out;
|
||||||
|
const char* in;
|
||||||
|
size_t in_len;
|
||||||
|
|
||||||
|
// Always include space for a NULL
|
||||||
|
sz = 1;
|
||||||
|
|
||||||
|
// Compute output size
|
||||||
|
in = src;
|
||||||
|
in_len = bytes;
|
||||||
|
while ((in_len > 0) && (*in != '\0')) {
|
||||||
|
if (*in != '\r') {
|
||||||
|
sz++;
|
||||||
|
in++;
|
||||||
|
in_len--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((in_len < 2) || (*(in+1) != '\n'))
|
||||||
|
sz++;
|
||||||
|
|
||||||
|
in++;
|
||||||
|
in_len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alloc
|
||||||
|
buffer = new char[sz];
|
||||||
|
memset(buffer, 0, sz);
|
||||||
|
|
||||||
|
// And convert
|
||||||
|
out = buffer;
|
||||||
|
in = src;
|
||||||
|
in_len = bytes;
|
||||||
|
while ((in_len > 0) && (*in != '\0')) {
|
||||||
|
if (*in != '\r') {
|
||||||
|
*out++ = *in++;
|
||||||
|
in_len--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((in_len < 2) || (*(in+1) != '\n'))
|
||||||
|
*out++ = '\n';
|
||||||
|
|
||||||
|
in++;
|
||||||
|
in_len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* convertCRLF(const char* src, size_t bytes)
|
||||||
|
{
|
||||||
|
char* buffer;
|
||||||
|
size_t sz;
|
||||||
|
|
||||||
|
char* out;
|
||||||
|
const char* in;
|
||||||
|
size_t in_len;
|
||||||
|
|
||||||
|
// Always include space for a NULL
|
||||||
|
sz = 1;
|
||||||
|
|
||||||
|
// Compute output size
|
||||||
|
in = src;
|
||||||
|
in_len = bytes;
|
||||||
|
while ((in_len > 0) && (*in != '\0')) {
|
||||||
|
sz++;
|
||||||
|
|
||||||
|
if (*in == '\r') {
|
||||||
|
if ((in_len < 2) || (*(in+1) != '\n'))
|
||||||
|
sz++;
|
||||||
|
} else if (*in == '\n') {
|
||||||
|
if ((in == src) || (*(in-1) != '\r'))
|
||||||
|
sz++;
|
||||||
|
}
|
||||||
|
|
||||||
|
in++;
|
||||||
|
in_len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alloc
|
||||||
|
buffer = new char[sz];
|
||||||
|
memset(buffer, 0, sz);
|
||||||
|
|
||||||
|
// And convert
|
||||||
|
out = buffer;
|
||||||
|
in = src;
|
||||||
|
in_len = bytes;
|
||||||
|
while ((in_len > 0) && (*in != '\0')) {
|
||||||
|
if (*in == '\n') {
|
||||||
|
if ((in == src) || (*(in-1) != '\r'))
|
||||||
|
*out++ = '\r';
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = *in;
|
||||||
|
|
||||||
|
if (*in == '\r') {
|
||||||
|
if ((in_len < 2) || (*(in+1) != '\n')) {
|
||||||
|
out++;
|
||||||
|
*out = '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out++;
|
||||||
|
in++;
|
||||||
|
in_len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ucs4ToUTF8(unsigned src, char* dst) {
|
||||||
|
if (src < 0x80) {
|
||||||
|
*dst++ = src;
|
||||||
|
*dst++ = '\0';
|
||||||
|
return 1;
|
||||||
|
} else if (src < 0x800) {
|
||||||
|
*dst++ = 0xc0 | (src >> 6);
|
||||||
|
*dst++ = 0x80 | (src & 0x3f);
|
||||||
|
*dst++ = '\0';
|
||||||
|
return 2;
|
||||||
|
} else if (src < 0x10000) {
|
||||||
|
*dst++ = 0xe0 | (src >> 12);
|
||||||
|
*dst++ = 0x80 | ((src >> 6) & 0x3f);
|
||||||
|
*dst++ = 0x80 | (src & 0x3f);
|
||||||
|
*dst++ = '\0';
|
||||||
|
return 3;
|
||||||
|
} else if (src < 0x110000) {
|
||||||
|
*dst++ = 0xf0 | (src >> 18);
|
||||||
|
*dst++ = 0x80 | ((src >> 12) & 0x3f);
|
||||||
|
*dst++ = 0x80 | ((src >> 6) & 0x3f);
|
||||||
|
*dst++ = 0x80 | (src & 0x3f);
|
||||||
|
*dst++ = '\0';
|
||||||
|
return 4;
|
||||||
|
} else {
|
||||||
|
return ucs4ToUTF8(0xfffd, dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t utf8ToUCS4(const char* src, size_t max, unsigned* dst) {
|
||||||
|
size_t count, consumed;
|
||||||
|
|
||||||
|
*dst = 0xfffd;
|
||||||
|
|
||||||
|
if (max == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
consumed = 1;
|
||||||
|
|
||||||
|
if ((*src & 0x80) == 0) {
|
||||||
|
*dst = *src;
|
||||||
|
count = 0;
|
||||||
|
} else if ((*src & 0xe0) == 0xc0) {
|
||||||
|
*dst = *src & 0x1f;
|
||||||
|
count = 1;
|
||||||
|
} else if ((*src & 0xf0) == 0xe0) {
|
||||||
|
*dst = *src & 0x0f;
|
||||||
|
count = 2;
|
||||||
|
} else if ((*src & 0xf8) == 0xf0) {
|
||||||
|
*dst = *src & 0x07;
|
||||||
|
count = 3;
|
||||||
|
} else {
|
||||||
|
// Invalid sequence, consume all continuation characters
|
||||||
|
src++;
|
||||||
|
max--;
|
||||||
|
while ((max-- > 0) && ((*src++ & 0xc0) == 0x80))
|
||||||
|
consumed++;
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
src++;
|
||||||
|
max--;
|
||||||
|
|
||||||
|
while (count--) {
|
||||||
|
consumed++;
|
||||||
|
|
||||||
|
// Invalid or truncated sequence?
|
||||||
|
if ((max == 0) || ((*src & 0xc0) != 0x80)) {
|
||||||
|
*dst = 0xfffd;
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
*dst <<= 6;
|
||||||
|
*dst |= *src & 0x3f;
|
||||||
|
|
||||||
|
src++;
|
||||||
|
max--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ucs4ToUTF16(unsigned src, wchar_t* dst) {
|
||||||
|
if ((src < 0xd800) || ((src >= 0xe000) && (src < 0x10000))) {
|
||||||
|
*dst++ = src;
|
||||||
|
*dst++ = L'\0';
|
||||||
|
return 1;
|
||||||
|
} else if ((src >= 0x10000) && (src < 0x110000)) {
|
||||||
|
src -= 0x10000;
|
||||||
|
*dst++ = 0xd800 | ((src >> 10) & 0x03ff);
|
||||||
|
*dst++ = 0xdc00 | (src & 0x03ff);
|
||||||
|
*dst++ = L'\0';
|
||||||
|
return 2;
|
||||||
|
} else {
|
||||||
|
return ucs4ToUTF16(0xfffd, dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t utf16ToUCS4(const wchar_t* src, size_t max, unsigned* dst) {
|
||||||
|
*dst = 0xfffd;
|
||||||
|
|
||||||
|
if (max == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if ((*src < 0xd800) || (*src >= 0xe000)) {
|
||||||
|
*dst = *src;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*src & 0x0400) {
|
||||||
|
size_t consumed;
|
||||||
|
|
||||||
|
// Invalid sequence, consume all continuation characters
|
||||||
|
consumed = 0;
|
||||||
|
while ((max > 0) && (*src & 0x0400)) {
|
||||||
|
src++;
|
||||||
|
max--;
|
||||||
|
consumed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
*dst = *src++;
|
||||||
|
max--;
|
||||||
|
|
||||||
|
// Invalid or truncated sequence?
|
||||||
|
if ((max == 0) || ((*src & 0xfc00) != 0xdc00)) {
|
||||||
|
*dst = 0xfffd;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*dst = 0x10000 + ((*dst & 0x03ff) << 10);
|
||||||
|
*dst |= *src & 0x3ff;
|
||||||
|
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* latin1ToUTF8(const char* src, size_t bytes) {
|
||||||
|
char* buffer;
|
||||||
|
size_t sz;
|
||||||
|
|
||||||
|
char* out;
|
||||||
|
const char* in;
|
||||||
|
size_t in_len;
|
||||||
|
|
||||||
|
// Always include space for a NULL
|
||||||
|
sz = 1;
|
||||||
|
|
||||||
|
// Compute output size
|
||||||
|
in = src;
|
||||||
|
in_len = bytes;
|
||||||
|
while ((in_len > 0) && (*in != '\0')) {
|
||||||
|
char buf[5];
|
||||||
|
sz += ucs4ToUTF8(*(const unsigned char*)in, buf);
|
||||||
|
in++;
|
||||||
|
in_len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alloc
|
||||||
|
buffer = new char[sz];
|
||||||
|
memset(buffer, 0, sz);
|
||||||
|
|
||||||
|
// And convert
|
||||||
|
out = buffer;
|
||||||
|
in = src;
|
||||||
|
in_len = bytes;
|
||||||
|
while ((in_len > 0) && (*in != '\0')) {
|
||||||
|
out += ucs4ToUTF8(*(const unsigned char*)in, out);
|
||||||
|
in++;
|
||||||
|
in_len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* utf8ToLatin1(const char* src, size_t bytes) {
|
||||||
|
char* buffer;
|
||||||
|
size_t sz;
|
||||||
|
|
||||||
|
char* out;
|
||||||
|
const char* in;
|
||||||
|
size_t in_len;
|
||||||
|
|
||||||
|
// Always include space for a NULL
|
||||||
|
sz = 1;
|
||||||
|
|
||||||
|
// Compute output size
|
||||||
|
in = src;
|
||||||
|
in_len = bytes;
|
||||||
|
while ((in_len > 0) && (*in != '\0')) {
|
||||||
|
size_t len;
|
||||||
|
unsigned ucs;
|
||||||
|
|
||||||
|
len = utf8ToUCS4(in, in_len, &ucs);
|
||||||
|
in += len;
|
||||||
|
in_len -= len;
|
||||||
|
sz++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alloc
|
||||||
|
buffer = new char[sz];
|
||||||
|
memset(buffer, 0, sz);
|
||||||
|
|
||||||
|
// And convert
|
||||||
|
out = buffer;
|
||||||
|
in = src;
|
||||||
|
in_len = bytes;
|
||||||
|
while ((in_len > 0) && (*in != '\0')) {
|
||||||
|
size_t len;
|
||||||
|
unsigned ucs;
|
||||||
|
|
||||||
|
len = utf8ToUCS4(in, in_len, &ucs);
|
||||||
|
in += len;
|
||||||
|
in_len -= len;
|
||||||
|
|
||||||
|
if (ucs > 0xff)
|
||||||
|
*out++ = '?';
|
||||||
|
else
|
||||||
|
*out++ = (unsigned char)ucs;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* utf16ToUTF8(const wchar_t* src, size_t units)
|
||||||
|
{
|
||||||
|
char* buffer;
|
||||||
|
size_t sz;
|
||||||
|
|
||||||
|
char* out;
|
||||||
|
const wchar_t* in;
|
||||||
|
size_t in_len;
|
||||||
|
|
||||||
|
// Always include space for a NULL
|
||||||
|
sz = 1;
|
||||||
|
|
||||||
|
// Compute output size
|
||||||
|
in = src;
|
||||||
|
in_len = units;
|
||||||
|
while ((in_len > 0) && (*in != '\0')) {
|
||||||
|
size_t len;
|
||||||
|
unsigned ucs;
|
||||||
|
char buf[5];
|
||||||
|
|
||||||
|
len = utf16ToUCS4(in, in_len, &ucs);
|
||||||
|
in += len;
|
||||||
|
in_len -= len;
|
||||||
|
|
||||||
|
sz += ucs4ToUTF8(ucs, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alloc
|
||||||
|
buffer = new char[sz];
|
||||||
|
memset(buffer, 0, sz);
|
||||||
|
|
||||||
|
// And convert
|
||||||
|
out = buffer;
|
||||||
|
in = src;
|
||||||
|
in_len = units;
|
||||||
|
while ((in_len > 0) && (*in != '\0')) {
|
||||||
|
size_t len;
|
||||||
|
unsigned ucs;
|
||||||
|
|
||||||
|
len = utf16ToUCS4(in, in_len, &ucs);
|
||||||
|
in += len;
|
||||||
|
in_len -= len;
|
||||||
|
|
||||||
|
out += ucs4ToUTF8(ucs, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
wchar_t* utf8ToUTF16(const char* src, size_t bytes)
|
||||||
|
{
|
||||||
|
wchar_t* buffer;
|
||||||
|
size_t sz;
|
||||||
|
|
||||||
|
wchar_t* out;
|
||||||
|
const char* in;
|
||||||
|
size_t in_len;
|
||||||
|
|
||||||
|
// Always include space for a NULL
|
||||||
|
sz = 1;
|
||||||
|
|
||||||
|
// Compute output size
|
||||||
|
in = src;
|
||||||
|
in_len = bytes;
|
||||||
|
while ((in_len > 0) && (*in != '\0')) {
|
||||||
|
size_t len;
|
||||||
|
unsigned ucs;
|
||||||
|
wchar_t buf[3];
|
||||||
|
|
||||||
|
len = utf8ToUCS4(in, in_len, &ucs);
|
||||||
|
in += len;
|
||||||
|
in_len -= len;
|
||||||
|
|
||||||
|
sz += ucs4ToUTF16(ucs, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alloc
|
||||||
|
buffer = new wchar_t[sz];
|
||||||
|
memset(buffer, 0, sz * sizeof(wchar_t));
|
||||||
|
|
||||||
|
// And convert
|
||||||
|
out = buffer;
|
||||||
|
in = src;
|
||||||
|
in_len = bytes;
|
||||||
|
while ((in_len > 0) && (*in != '\0')) {
|
||||||
|
size_t len;
|
||||||
|
unsigned ucs;
|
||||||
|
|
||||||
|
len = utf8ToUCS4(in, in_len, &ucs);
|
||||||
|
in += len;
|
||||||
|
in_len -= len;
|
||||||
|
|
||||||
|
out += ucs4ToUTF16(ucs, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned msBetween(const struct timeval *first,
|
unsigned msBetween(const struct timeval *first,
|
||||||
const struct timeval *second)
|
const struct timeval *second)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||||
|
* Copyright 2011-2019 Pierre Ossman for Cendio AB
|
||||||
*
|
*
|
||||||
* This is free software; you can redistribute it and/or modify
|
* This is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -67,6 +68,7 @@ namespace rfb {
|
|||||||
|
|
||||||
char* strDup(const char* s);
|
char* strDup(const char* s);
|
||||||
void strFree(char* s);
|
void strFree(char* s);
|
||||||
|
void strFree(wchar_t* s);
|
||||||
|
|
||||||
// Returns true if split successful. Returns false otherwise.
|
// Returns true if split successful. Returns false otherwise.
|
||||||
// ALWAYS *copies* first part of string to out1 buffer.
|
// ALWAYS *copies* first part of string to out1 buffer.
|
||||||
@@ -83,6 +85,25 @@ namespace rfb {
|
|||||||
// Copies src to dest, up to specified length-1, and guarantees termination
|
// Copies src to dest, up to specified length-1, and guarantees termination
|
||||||
void strCopy(char* dest, const char* src, int destlen);
|
void strCopy(char* dest, const char* src, int destlen);
|
||||||
|
|
||||||
|
// Makes sure line endings are in a certain format
|
||||||
|
|
||||||
|
char* convertLF(const char* src, size_t bytes = (size_t)-1);
|
||||||
|
char* convertCRLF(const char* src, size_t bytes = (size_t)-1);
|
||||||
|
|
||||||
|
// Convertions between various Unicode formats. The returned strings are
|
||||||
|
// always null terminated and must be freed using strFree().
|
||||||
|
|
||||||
|
size_t ucs4ToUTF8(unsigned src, char* dst);
|
||||||
|
size_t utf8ToUCS4(const char* src, size_t max, unsigned* dst);
|
||||||
|
|
||||||
|
size_t ucs4ToUTF16(unsigned src, wchar_t* dst);
|
||||||
|
size_t utf16ToUCS4(const wchar_t* src, size_t max, unsigned* dst);
|
||||||
|
|
||||||
|
char* latin1ToUTF8(const char* src, size_t bytes = (size_t)-1);
|
||||||
|
char* utf8ToLatin1(const char* src, size_t bytes = (size_t)-1);
|
||||||
|
|
||||||
|
char* utf16ToUTF8(const wchar_t* src, size_t units = (size_t)-1);
|
||||||
|
wchar_t* utf8ToUTF16(const char* src, size_t bytes = (size_t)-1);
|
||||||
|
|
||||||
// HELPER functions for timeout handling
|
// HELPER functions for timeout handling
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ TigerVNC is
|
|||||||
All Rights Reserved.
|
All Rights Reserved.
|
||||||
|
|
||||||
This software is distributed under the GNU General Public Licence as published
|
This software is distributed under the GNU General Public Licence as published
|
||||||
by the Free Software Foundation. See the file LICENCE.TXT for the conditions
|
by the Free Software Foundation. See the file LICENSE.TXT for the conditions
|
||||||
under which this software is made available. TigerVNC also contains code from
|
under which this software is made available. TigerVNC also contains code from
|
||||||
other sources. See the Acknowledgements section below, and the individual
|
other sources. See the Acknowledgements section below, and the individual
|
||||||
source files, for details of the conditions under which they are made
|
source files, for details of the conditions under which they are made
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ TigerVNC is
|
|||||||
All Rights Reserved.
|
All Rights Reserved.
|
||||||
|
|
||||||
This software is distributed under the GNU General Public Licence as published
|
This software is distributed under the GNU General Public Licence as published
|
||||||
by the Free Software Foundation. See the file LICENCE.TXT for the conditions
|
by the Free Software Foundation. See the file LICENSE.TXT for the conditions
|
||||||
under which this software is made available. TigerVNC also contains code from
|
under which this software is made available. TigerVNC also contains code from
|
||||||
other sources. See the Acknowledgements section below, and the individual
|
other sources. See the Acknowledgements section below, and the individual
|
||||||
source files, for details of the conditions under which they are made
|
source files, for details of the conditions under which they are made
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ TigerVNC is
|
|||||||
All Rights Reserved.
|
All Rights Reserved.
|
||||||
|
|
||||||
This software is distributed under the GNU General Public Licence as published
|
This software is distributed under the GNU General Public Licence as published
|
||||||
by the Free Software Foundation. See the file LICENCE.TXT for the conditions
|
by the Free Software Foundation. See the file LICENSE.TXT for the conditions
|
||||||
under which this software is made available. TigerVNC also contains code from
|
under which this software is made available. TigerVNC also contains code from
|
||||||
other sources. See the Acknowledgements section below, and the individual
|
other sources. See the Acknowledgements section below, and the individual
|
||||||
source files, for details of the conditions under which they are made
|
source files, for details of the conditions under which they are made
|
||||||
|
|||||||
@@ -1031,7 +1031,7 @@ fi
|
|||||||
|
|
||||||
%files license
|
%files license
|
||||||
%defattr(-,root,root,-)
|
%defattr(-,root,root,-)
|
||||||
%doc LICENCE.TXT
|
%doc LICENSE.TXT
|
||||||
|
|
||||||
%files icons
|
%files icons
|
||||||
%defattr(-,root,root,-)
|
%defattr(-,root,root,-)
|
||||||
|
|||||||
@@ -444,7 +444,7 @@ fi
|
|||||||
|
|
||||||
%files license
|
%files license
|
||||||
%defattr(-,root,root,-)
|
%defattr(-,root,root,-)
|
||||||
%doc LICENCE.TXT
|
%doc LICENSE.TXT
|
||||||
|
|
||||||
%files icons
|
%files icons
|
||||||
%defattr(-,root,root,-)
|
%defattr(-,root,root,-)
|
||||||
|
|||||||
@@ -376,7 +376,7 @@ fi
|
|||||||
%{_datadir}/vnc/classes/*
|
%{_datadir}/vnc/classes/*
|
||||||
|
|
||||||
%files license
|
%files license
|
||||||
%doc %{_docdir}/%{name}-%{version}/LICENCE.TXT
|
%doc %{_docdir}/%{name}-%{version}/LICENSE.TXT
|
||||||
|
|
||||||
%files icons
|
%files icons
|
||||||
%defattr(-,root,root,-)
|
%defattr(-,root,root,-)
|
||||||
|
|||||||
1
debian/Makefile.to_fakebuild_tar_package
vendored
1
debian/Makefile.to_fakebuild_tar_package
vendored
@@ -16,6 +16,7 @@ install: unpack_tarball
|
|||||||
cp $(SRC_BIN)/vncserver $(DESTDIR)/usr/bin/kasmvncserver
|
cp $(SRC_BIN)/vncserver $(DESTDIR)/usr/bin/kasmvncserver
|
||||||
cp $(SRC_BIN)/vncconfig $(DESTDIR)/usr/bin/kasmvncconfig
|
cp $(SRC_BIN)/vncconfig $(DESTDIR)/usr/bin/kasmvncconfig
|
||||||
cp $(SRC_BIN)/kasmvncpasswd $(DESTDIR)/usr/bin/
|
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/
|
cp -r $(SRC)/share/doc/kasmvnc*/* $(DESTDIR)/usr/share/doc/kasmvncserver/
|
||||||
rsync -r --exclude '.git*' --exclude po2js --exclude xgettext-html \
|
rsync -r --exclude '.git*' --exclude po2js --exclude xgettext-html \
|
||||||
--exclude www/utils/ --exclude .eslintrc \
|
--exclude www/utils/ --exclude .eslintrc \
|
||||||
|
|||||||
2
debian/control
vendored
2
debian/control
vendored
@@ -3,7 +3,7 @@ Section: x11
|
|||||||
Priority: optional
|
Priority: optional
|
||||||
Maintainer: Kasm Technologies LLC <info@kasmweb.com>
|
Maintainer: Kasm Technologies LLC <info@kasmweb.com>
|
||||||
Build-Depends: debhelper (>= 11), rsync, libjpeg-dev, libjpeg-dev, libpng-dev,
|
Build-Depends: debhelper (>= 11), rsync, libjpeg-dev, libjpeg-dev, libpng-dev,
|
||||||
libtiff-dev, libgif-dev, libavcodec-dev, libssl-dev, libgl1, libxfont2, libsm6
|
libtiff-dev, libgif-dev, libavcodec-dev, libssl-dev, libgl1, libxfont2, libsm6, libxext-dev, libxrandr-dev, libxtst-dev, libxcursor-dev
|
||||||
Standards-Version: 4.1.3
|
Standards-Version: 4.1.3
|
||||||
Homepage: https://github.com/kasmtech/KasmVNC
|
Homepage: https://github.com/kasmtech/KasmVNC
|
||||||
#Vcs-Browser: https://salsa.debian.org/debian/kasmvnc
|
#Vcs-Browser: https://salsa.debian.org/debian/kasmvnc
|
||||||
|
|||||||
2
debian/postinst
vendored
2
debian/postinst
vendored
@@ -21,7 +21,7 @@ case "$1" in
|
|||||||
configure)
|
configure)
|
||||||
bindir=/usr/bin
|
bindir=/usr/bin
|
||||||
mandir=/usr/share/man
|
mandir=/usr/share/man
|
||||||
commands="kasmvncserver kasmvncpasswd kasmvncconfig Xkasmvnc"
|
commands="kasmvncserver kasmvncpasswd kasmvncconfig Xkasmvnc kasmxproxy"
|
||||||
|
|
||||||
for kasm_command in $commands; do
|
for kasm_command in $commands; do
|
||||||
generic_command=`echo "$kasm_command" | sed -e 's/kasm//'`;
|
generic_command=`echo "$kasm_command" | sed -e 's/kasm//'`;
|
||||||
|
|||||||
2
debian/prerm
vendored
2
debian/prerm
vendored
@@ -21,7 +21,7 @@ case "$1" in
|
|||||||
remove)
|
remove)
|
||||||
bindir=/usr/bin
|
bindir=/usr/bin
|
||||||
mandir=/usr/share/man
|
mandir=/usr/share/man
|
||||||
commands="kasmvncserver kasmvncpasswd kasmvncconfig Xkasmvnc"
|
commands="kasmvncserver kasmvncpasswd kasmvncconfig Xkasmvnc kasmxproxy"
|
||||||
|
|
||||||
for kasm_command in $commands; do
|
for kasm_command in $commands; do
|
||||||
generic_command=`echo "$kasm_command" | sed -e 's/kasm//'`;
|
generic_command=`echo "$kasm_command" | sed -e 's/kasm//'`;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user