diff --git a/base/Dockerfile b/base/Dockerfile deleted file mode 100644 index a65e9ed..0000000 --- a/base/Dockerfile +++ /dev/null @@ -1,50 +0,0 @@ -# syntax = docker/dockerfile:1 -FROM quay.io/archlinux/archlinux:base-devel -USER root - -# Build ARG for additional packages to install (eg, openssh) -ARG addpkg - -# Build ARG: use "root" for debugging -ARG debug - -# Copy mirrorlist -COPY mirrorlist /etc/pacman.d/mirrorlist - -# Copy app folder -COPY app /app - -# Install packages -RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman \ - pacman-key --init \ - && sed -i '/ParallelDownloads/c ParallelDownloads = 10' /etc/pacman.conf \ - && pacman -Sy --ask 4 archlinux-keyring \ - && pacman -Su --ask 4 --needed ${addpkg} \ - sudo bash bash-completion \ - git inetutils python-numpy python-setuptools \ - nginx-mainline \ - && pacman -Scc --ask 4 \ - && useradd --create-home --gid users --shell /usr/bin/bash user \ - && printf '%s\n' 'user ALL=(ALL:ALL) NOPASSWD: MISCELLANEOUS, /usr/bin/nginx' \ - 'Defaults lecture = never' >/etc/sudoers.d/zz-DOCKER \ - && passwd -l root >/dev/null 2>&1 \ - && sed '/^http {/a\ \ - include /app/nginx/\*.conf;\n\ \ - types_hash_max_size 4096;\n\ \ - server_names_hash_bucket_size 128;\n' -i /etc/nginx/nginx.conf \ - && mkdir -p /app/nginx /app/logs \ - && chown -R user:users /app - -# Default environment -USER ${debug:-user} -WORKDIR /home/user -ENV HOME=/home/user -ENV DISPLAY=:0 -ENV SHELL=/usr/bin/bash -ENV PS1="[\u@\h \W \$?]\$ " - -# Expose nginx port for VNC webui -EXPOSE 6900 - -# Docker entrypoint -ENTRYPOINT ["/app/entrypoint"] diff --git a/base/app/entrypoint b/base/app/entrypoint deleted file mode 100755 index b1ea375..0000000 --- a/base/app/entrypoint +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash -# Export all variables -set -a - -# Abort if an error is encountered -set -e - -# SSH config -if [ -f ~/.ssh/id_ed25519 ] -then - chmod 0600 ~/.ssh/id_ed25519 -fi - -# Run all scripts in init folder -for file in /app/init.d/*.sh -do - /usr/bin/bash -c ${file} >>/app/logs/$(echo ${file} | sed 's|/app/init.d/||;s|\.sh$||').log -done - -# Read cli parameters -exec "${@}" & - -# Monitor log -tail -f /app/logs/*.log diff --git a/base/app/init.d/01-init.sh b/base/app/init.d/01-init.sh deleted file mode 100755 index 20bf29d..0000000 --- a/base/app/init.d/01-init.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash -# Create base directories -for dir in ssh config/tigervnc -do - [ -d ~/.${dir} ] || mkdir -p ~/.${dir} -done diff --git a/base/app/init.d/10-nginx.sh b/base/app/init.d/10-nginx.sh deleted file mode 100755 index 41d180b..0000000 --- a/base/app/init.d/10-nginx.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bash -# Reformat proxy path -if [[ ${MYVNC_PROXYPATH} == "/" ]] -then - unset _MYVNC_PROXYPATH -elif [ ${MYVNC_PROXYPATH} ] -then - _MYVNC_PROXYPATH=$(echo ${MYVNC_PROXYPATH} | sed "s|^/*||g;s|/*$||g;s|/*/|/|g;s|^|/|") -fi - -# Add novnc virtual proxy conf -cat >/app/nginx/novnc.conf <<- novnc -upstream vnc_proxy { - server 127.0.0.1:6080; -} - -server { - listen 6900; - - location ${_MYVNC_PROXYPATH}/websockify { - proxy_http_version 1.1; - proxy_pass http://vnc_proxy/; - proxy_set_header Upgrade \$http_upgrade; - proxy_set_header Connection "upgrade"; - - # VNC connection timeout - proxy_read_timeout 3600s; - proxy_send_timeout 3600s; - - # Disable cache - proxy_buffering off; - } - - location ${_MYVNC_PROXYPATH}/ { - index vnc.html; - alias /app/novnc/; - try_files \$uri \$uri/ /vnc.html; - - # In the location block related to noVNC - add_header Cache-Control no-cache; - } -} -novnc - -# Start nginx -sudo /usr/bin/nginx -g "daemon off;" & diff --git a/base/app/init.d/10-novnc.sh b/base/app/init.d/10-novnc.sh deleted file mode 100755 index 4a70526..0000000 --- a/base/app/init.d/10-novnc.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bash -# Turn vnc sharing on/off -if [[ ${MYVNC_VNCSHARING} == "true" ]] || [[ ${MYVNC_VNCSHARING} == "1" ]] -then - _MYVNC_VNCSHARING=true -else - _MYVNC_VNCSHARING=false -fi - -# Always default to remote scaling -sed -i "/UI.initSetting\|resize/ s/resize', '.*');/resize', 'scale');/" /app/novnc/app/ui.js - -# Always default to autoconnect=true -sed -i "/let autoconnect/ s/autoconnect', .*);/autoconnect', true);/" /app/novnc/app/ui.js - -# Change vnc shared view settings (defaults to false/off) -sed -i "/UI.initSetting\|shared/ s/shared', .*);/shared', ${_MYVNC_VNCSHARING});/" /app/novnc/app/ui.js - -# NoVNC custom title -sed -i "/noVNC<\/title>/ s/noVNC/${MYVNC_CUSTOM_TITLE:-noVNC}/g" /app/novnc/*.html - -# Apply subpath to websocket -if [[ ${MYVNC_PROXYPATH} == "/" ]] -then - unset _MYVNC_PROXYPATH -elif [ ${MYVNC_PROXYPATH} ] -then - _MYVNC_PROXYPATH=$(echo ${MYVNC_PROXYPATH} | sed "s|^/*||g;s|/*$||g;s|/*/|/|g") - sed -i "/UI.initSetting/ s|websockify|${_MYVNC_PROXYPATH}/&|" /app/novnc/app/ui.js -fi - -# Start NoVNC -/app/novnc/utils/novnc_proxy \ - --vnc ${MYVNC_VNCLISTEN_HOST:-localhost}:${MYVNC_VNCLISTEN_PORT:-5900} \ - --file-only & diff --git a/base/app/novnc/.github/ISSUE_TEMPLATE/bug_report.md b/base/app/novnc/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 94ac6f8..0000000 --- a/base/app/novnc/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Client (please complete the following information):** - - OS: [e.g. iOS] - - Browser: [e.g. chrome, safari] - - Browser version: [e.g. 22] - -**Server (please complete the following information):** - - noVNC version: [e.g. 1.0.0 or git commit id] - - VNC server: [e.g. QEMU, TigerVNC] - - WebSocket proxy: [e.g. websockify] - -**Additional context** -Add any other context about the problem here. diff --git a/base/app/novnc/.github/ISSUE_TEMPLATE/config.yml b/base/app/novnc/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index cbd35aa..0000000 --- a/base/app/novnc/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,5 +0,0 @@ -blank_issues_enabled: false -contact_links: - - name: Question or discussion - url: https://groups.google.com/forum/?fromgroups#!forum/novnc - about: Ask a question or start a discussion diff --git a/base/app/novnc/.github/ISSUE_TEMPLATE/feature_request.md b/base/app/novnc/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 066b2d9..0000000 --- a/base/app/novnc/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/base/app/novnc/.github/workflows/deploy.yml b/base/app/novnc/.github/workflows/deploy.yml deleted file mode 100644 index a11d3d0..0000000 --- a/base/app/novnc/.github/workflows/deploy.yml +++ /dev/null @@ -1,97 +0,0 @@ -name: Publish - -on: - push: - pull_request: - release: - types: [published] - -jobs: - npm: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - run: | - GITREV=$(git rev-parse --short HEAD) - echo $GITREV - sed -i "s/^\(.*\"version\".*\)\"\([^\"]\+\)\"\(.*\)\$/\1\"\2-g$GITREV\"\3/" package.json - if: github.event_name != 'release' - - uses: actions/setup-node@v4 - with: - # Needs to be explicitly specified for auth to work - registry-url: 'https://registry.npmjs.org' - - run: npm install - - uses: actions/upload-artifact@v4 - with: - name: npm - path: lib - - run: npm publish --access public - env: - NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} - if: | - github.repository == 'novnc/noVNC' && - github.event_name == 'release' && - !github.event.release.prerelease - - run: npm publish --access public --tag beta - env: - NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} - if: | - github.repository == 'novnc/noVNC' && - github.event_name == 'release' && - github.event.release.prerelease - - run: npm publish --access public --tag dev - env: - NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} - if: | - github.repository == 'novnc/noVNC' && - github.event_name == 'push' && - github.event.ref == 'refs/heads/master' - snap: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - run: | - GITREV=$(git rev-parse --short HEAD) - echo $GITREV - sed -i "s/^\(.*\"version\".*\)\"\([^\"]\+\)\"\(.*\)\$/\1\"\2-g$GITREV\"\3/" package.json - if: github.event_name != 'release' - - run: | - VERSION=$(grep '"version"' package.json | cut -d '"' -f 4) - echo $VERSION - sed -i "s/^version:.*/version: '$VERSION'/" snap/snapcraft.yaml - - uses: snapcore/action-build@v1 - id: snapcraft - - uses: actions/upload-artifact@v4 - with: - name: snap - path: ${{ steps.snapcraft.outputs.snap }} - - uses: snapcore/action-publish@v1 - with: - snap: ${{ steps.snapcraft.outputs.snap }} - release: stable - env: - SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_LOGIN }} - if: | - github.repository == 'novnc/noVNC' && - github.event_name == 'release' && - !github.event.release.prerelease - - uses: snapcore/action-publish@v1 - with: - snap: ${{ steps.snapcraft.outputs.snap }} - release: beta - env: - SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_LOGIN }} - if: | - github.repository == 'novnc/noVNC' && - github.event_name == 'release' && - github.event.release.prerelease - - uses: snapcore/action-publish@v1 - with: - snap: ${{ steps.snapcraft.outputs.snap }} - release: edge - env: - SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_LOGIN }} - if: | - github.repository == 'novnc/noVNC' && - github.event_name == 'push' && - github.event.ref == 'refs/heads/master' diff --git a/base/app/novnc/.github/workflows/lint.yml b/base/app/novnc/.github/workflows/lint.yml deleted file mode 100644 index 540bb99..0000000 --- a/base/app/novnc/.github/workflows/lint.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Lint - -on: [push, pull_request] - -jobs: - eslint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - - run: npm update - - run: npm run lint - html: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - - run: npm update - - run: git ls-tree --name-only -r HEAD | grep -E "[.](html|css)$" | xargs ./utils/validate diff --git a/base/app/novnc/.github/workflows/test.yml b/base/app/novnc/.github/workflows/test.yml deleted file mode 100644 index b72195b..0000000 --- a/base/app/novnc/.github/workflows/test.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Test - -on: [push, pull_request] - -jobs: - test: - strategy: - matrix: - os: - - ubuntu-latest - - windows-latest - browser: - - ChromeHeadless - - FirefoxHeadless - include: - - os: macos-latest - browser: Safari - - os: windows-latest - browser: EdgeHeadless - fail-fast: false - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - - run: npm update - - run: npm run test - env: - TEST_BROWSER_NAME: ${{ matrix.browser }} diff --git a/base/app/novnc/.github/workflows/translate.yml b/base/app/novnc/.github/workflows/translate.yml deleted file mode 100644 index a4da9cb..0000000 --- a/base/app/novnc/.github/workflows/translate.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: Translate - -on: [push, pull_request] - -jobs: - translate: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - - run: npm update - - run: sudo apt-get install gettext - - run: make -C po update-pot - - run: make -C po update-po - - run: make -C po update-js diff --git a/base/app/novnc/.gitmodules b/base/app/novnc/.gitmodules deleted file mode 100644 index e69de29..0000000 diff --git a/base/app/novnc/AUTHORS b/base/app/novnc/AUTHORS deleted file mode 100644 index e8fb124..0000000 --- a/base/app/novnc/AUTHORS +++ /dev/null @@ -1,13 +0,0 @@ -maintainers: -- Samuel Mannehed for Cendio AB (@samhed) -- Pierre Ossman for Cendio AB (@CendioOssman) -maintainersEmeritus: -- Joel Martin (@kanaka) -- Solly Ross (@directxman12) -- @astrand -contributors: -# There are a bunch of people that should be here. -# If you want to be on this list, feel free send a PR -# to add yourself. -- jalf <git@jalf.dk> -- NTT corp. diff --git a/base/app/novnc/LICENSE.txt b/base/app/novnc/LICENSE.txt deleted file mode 100644 index 37efdcd..0000000 --- a/base/app/novnc/LICENSE.txt +++ /dev/null @@ -1,62 +0,0 @@ -noVNC is Copyright (C) 2022 The noVNC Authors -(./AUTHORS) - -The noVNC core library files are licensed under the MPL 2.0 (Mozilla -Public License 2.0). The noVNC core library is composed of the -Javascript code necessary for full noVNC operation. This includes (but -is not limited to): - - core/**/*.js - app/*.js - test/playback.js - -The HTML, CSS, font and images files that included with the noVNC -source distibution (or repository) are not considered part of the -noVNC core library and are licensed under more permissive licenses. -The intent is to allow easy integration of noVNC into existing web -sites and web applications. - -The HTML, CSS, font and image files are licensed as follows: - - *.html : 2-Clause BSD license - - app/styles/*.css : 2-Clause BSD license - - app/styles/Orbitron* : SIL Open Font License 1.1 - (Copyright 2009 Matt McInerney) - - app/images/ : Creative Commons Attribution-ShareAlike - http://creativecommons.org/licenses/by-sa/3.0/ - -Some portions of noVNC are copyright to their individual authors. -Please refer to the individual source files and/or to the noVNC commit -history: https://github.com/novnc/noVNC/commits/master - -The are several files and projects that have been incorporated into -the noVNC core library. Here is a list of those files and the original -licenses (all MPL 2.0 compatible): - - core/base64.js : MPL 2.0 - - core/des.js : Various BSD style licenses - - vendor/pako/ : MIT - -Any other files not mentioned above are typically marked with -a copyright/license header at the top of the file. The default noVNC -license is MPL-2.0. - -The following license texts are included: - - docs/LICENSE.MPL-2.0 - docs/LICENSE.OFL-1.1 - docs/LICENSE.BSD-3-Clause (New BSD) - docs/LICENSE.BSD-2-Clause (Simplified BSD / FreeBSD) - vendor/pako/LICENSE (MIT) - -Or alternatively the license texts may be found here: - - http://www.mozilla.org/MPL/2.0/ - http://scripts.sil.org/OFL - http://en.wikipedia.org/wiki/BSD_licenses - https://opensource.org/licenses/MIT diff --git a/base/app/novnc/README.md b/base/app/novnc/README.md deleted file mode 100644 index b95d15e..0000000 --- a/base/app/novnc/README.md +++ /dev/null @@ -1,227 +0,0 @@ -## noVNC: HTML VNC Client Library and Application - -[![Test Status](https://github.com/novnc/noVNC/workflows/Test/badge.svg)](https://github.com/novnc/noVNC/actions?query=workflow%3ATest) -[![Lint Status](https://github.com/novnc/noVNC/workflows/Lint/badge.svg)](https://github.com/novnc/noVNC/actions?query=workflow%3ALint) - -### Description - -noVNC is both a HTML VNC client JavaScript library and an application built on -top of that library. noVNC runs well in any modern browser including mobile -browsers (iOS and Android). - -Many companies, projects and products have integrated noVNC including -[OpenStack](http://www.openstack.org), -[OpenNebula](http://opennebula.org/), -[LibVNCServer](http://libvncserver.sourceforge.net), and -[ThinLinc](https://cendio.com/thinlinc). See -[the Projects and Companies wiki page](https://github.com/novnc/noVNC/wiki/Projects-and-companies-using-noVNC) -for a more complete list with additional info and links. - -### Table of Contents - -- [News/help/contact](#newshelpcontact) -- [Features](#features) -- [Screenshots](#screenshots) -- [Browser Requirements](#browser-requirements) -- [Server Requirements](#server-requirements) -- [Quick Start](#quick-start) -- [Installation from Snap Package](#installation-from-snap-package) -- [Integration and Deployment](#integration-and-deployment) -- [Authors/Contributors](#authorscontributors) - -### News/help/contact - -The project website is found at [novnc.com](http://novnc.com). - -If you are a noVNC developer/integrator/user (or want to be) please join the -[noVNC discussion group](https://groups.google.com/forum/?fromgroups#!forum/novnc). - -Bugs and feature requests can be submitted via -[github issues](https://github.com/novnc/noVNC/issues). If you have questions -about using noVNC then please first use the -[discussion group](https://groups.google.com/forum/?fromgroups#!forum/novnc). -We also have a [wiki](https://github.com/novnc/noVNC/wiki/) with lots of -helpful information. - -If you are looking for a place to start contributing to noVNC, a good place to -start would be the issues that are marked as -["patchwelcome"](https://github.com/novnc/noVNC/issues?labels=patchwelcome). -Please check our -[contribution guide](https://github.com/novnc/noVNC/wiki/Contributing) though. - -If you want to show appreciation for noVNC you could donate to a great non- -profits such as: -[Compassion International](http://www.compassion.com/), -[SIL](http://www.sil.org), -[Habitat for Humanity](http://www.habitat.org), -[Electronic Frontier Foundation](https://www.eff.org/), -[Against Malaria Foundation](http://www.againstmalaria.com/), -[Nothing But Nets](http://www.nothingbutnets.net/), etc. - - -### Features - -* Supports all modern browsers including mobile (iOS, Android) -* Supported authentication methods: none, classical VNC, RealVNC's - RSA-AES, Tight, VeNCrypt Plain, XVP, Apple's Diffie-Hellman, - UltraVNC's MSLogonII -* Supported VNC encodings: raw, copyrect, rre, hextile, tight, tightPNG, - ZRLE, JPEG -* Supports scaling, clipping and resizing the desktop -* Local cursor rendering -* Clipboard copy/paste with full Unicode support -* Translations -* Touch gestures for emulating common mouse actions -* Licensed mainly under the [MPL 2.0](http://www.mozilla.org/MPL/2.0/), see - [the license document](LICENSE.txt) for details - -### Screenshots - -Running in Firefox before and after connecting: - -<img src="http://novnc.com/img/noVNC-1-login.png" width=400>  -<img src="http://novnc.com/img/noVNC-3-connected.png" width=400> - -See more screenshots -[here](http://novnc.com/screenshots.html). - - -### Browser Requirements - -noVNC uses many modern web technologies so a formal requirement list is -not available. However these are the minimum versions we are currently -aware of: - -* Chrome 64, Firefox 79, Safari 13.4, Opera 51, Edge 79 - - -### Server Requirements - -noVNC follows the standard VNC protocol, but unlike other VNC clients it does -require WebSockets support. Many servers include support (e.g. -[x11vnc/libvncserver](http://libvncserver.sourceforge.net/), -[QEMU](http://www.qemu.org/), and -[MobileVNC](http://www.smartlab.at/mobilevnc/)), but for the others you need to -use a WebSockets to TCP socket proxy. noVNC has a sister project -[websockify](https://github.com/novnc/websockify) that provides a simple such -proxy. - - -### Quick Start - -* Use the `novnc_proxy` script to automatically download and start websockify, which - includes a mini-webserver and the WebSockets proxy. The `--vnc` option is - used to specify the location of a running VNC server: - - `./utils/novnc_proxy --vnc localhost:5901` - -* If you don't need to expose the web server to public internet, you can - bind to localhost: - - `./utils/novnc_proxy --vnc localhost:5901 --listen localhost:6081` - -* Point your browser to the cut-and-paste URL that is output by the `novnc_proxy` - script. Hit the Connect button, enter a password if the VNC server has one - configured, and enjoy! - -### Installation from Snap Package -Running the command below will install the latest release of noVNC from Snap: - -`sudo snap install novnc` - -#### Running noVNC from Snap Directly - -You can run the Snap-package installed novnc directly with, for example: - -`novnc --listen 6081 --vnc localhost:5901 # /snap/bin/novnc if /snap/bin is not in your PATH` - -If you want to use certificate files, due to standard Snap confinement restrictions you need to have them in the /home/\<user\>/snap/novnc/current/ directory. If your username is jsmith an example command would be: - - `novnc --listen 8443 --cert ~jsmith/snap/novnc/current/self.crt --key ~jsmith/snap/novnc/current/self.key --vnc ubuntu.example.com:5901` - -#### Running noVNC from Snap as a Service (Daemon) -The Snap package also has the capability to run a 'novnc' service which can be -configured to listen on multiple ports connecting to multiple VNC servers -(effectively a service runing multiple instances of novnc). -Instructions (with example values): - -List current services (out-of-box this will be blank): - -``` -sudo snap get novnc services -Key Value -services.n6080 {...} -services.n6081 {...} -``` - -Create a new service that listens on port 6082 and connects to the VNC server -running on port 5902 on localhost: - -`sudo snap set novnc services.n6082.listen=6082 services.n6082.vnc=localhost:5902` - -(Any services you define with 'snap set' will be automatically started) -Note that the name of the service, 'n6082' in this example, can be anything -as long as it doesn't start with a number or contain spaces/special characters. - -View the configuration of the service just created: - -``` -sudo snap get novnc services.n6082 -Key Value -services.n6082.listen 6082 -services.n6082.vnc localhost:5902 -``` - -Disable a service (note that because of a limitation in Snap it's currently not -possible to unset config variables, setting them to blank values is the way -to disable a service): - -`sudo snap set novnc services.n6082.listen='' services.n6082.vnc=''` - -(Any services you set to blank with 'snap set' like this will be automatically stopped) - -Verify that the service is disabled (blank values): - -``` -sudo snap get novnc services.n6082 -Key Value -services.n6082.listen -services.n6082.vnc -``` - -### Integration and Deployment - -Please see our other documents for how to integrate noVNC in your own software, -or deploying the noVNC application in production environments: - -* [Embedding](docs/EMBEDDING.md) - For the noVNC application -* [Library](docs/LIBRARY.md) - For the noVNC JavaScript library - - -### Authors/Contributors - -See [AUTHORS](AUTHORS) for a (full-ish) list of authors. If you're not on -that list and you think you should be, feel free to send a PR to fix that. - -* Core team: - * [Samuel Mannehed](https://github.com/samhed) (Cendio) - * [Pierre Ossman](https://github.com/CendioOssman) (Cendio) - -* Previous core contributors: - * [Joel Martin](https://github.com/kanaka) (Project founder) - * [Solly Ross](https://github.com/DirectXMan12) (Red Hat / OpenStack) - -* Notable contributions: - * UI and Icons : Pierre Ossman, Chris Gordon - * Original Logo : Michael Sersen - * tight encoding : Michael Tinglof (Mercuri.ca) - * RealVNC RSA AES authentication : USTC Vlab Team - -* Included libraries: - * base64 : Martijn Pieters (Digital Creations 2), Samuel Sieb (sieb.net) - * DES : Dave Zimmerman (Widget Workshop), Jef Poskanzer (ACME Labs) - * Pako : Vitaly Puzrin (https://github.com/nodeca/pako) - -Do you want to be on this list? Check out our -[contribution guide](https://github.com/novnc/noVNC/wiki/Contributing) and -start hacking! diff --git a/base/app/novnc/app/error-handler.js b/base/app/novnc/app/error-handler.js deleted file mode 100644 index 67b6372..0000000 --- a/base/app/novnc/app/error-handler.js +++ /dev/null @@ -1,79 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2019 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - */ - -// Fallback for all uncought errors -function handleError(event, err) { - try { - const msg = document.getElementById('noVNC_fallback_errormsg'); - - // Work around Firefox bug: - // https://bugzilla.mozilla.org/show_bug.cgi?id=1685038 - if (event.message === "ResizeObserver loop completed with undelivered notifications.") { - return false; - } - - // Only show the initial error - if (msg.hasChildNodes()) { - return false; - } - - let div = document.createElement("div"); - div.classList.add('noVNC_message'); - div.appendChild(document.createTextNode(event.message)); - msg.appendChild(div); - - if (event.filename) { - div = document.createElement("div"); - div.className = 'noVNC_location'; - let text = event.filename; - if (event.lineno !== undefined) { - text += ":" + event.lineno; - if (event.colno !== undefined) { - text += ":" + event.colno; - } - } - div.appendChild(document.createTextNode(text)); - msg.appendChild(div); - } - - if (err && err.stack) { - div = document.createElement("div"); - div.className = 'noVNC_stack'; - div.appendChild(document.createTextNode(err.stack)); - msg.appendChild(div); - } - - document.getElementById('noVNC_fallback_error') - .classList.add("noVNC_open"); - - } catch (exc) { - document.write("noVNC encountered an error."); - } - - // Try to disable keyboard interaction, best effort - try { - // Remove focus from the currently focused element in order to - // prevent keyboard interaction from continuing - if (document.activeElement) { document.activeElement.blur(); } - - // Don't let any element be focusable when showing the error - let keyboardFocusable = 'a[href], button, input, textarea, select, details, [tabindex]'; - document.querySelectorAll(keyboardFocusable).forEach((elem) => { - elem.setAttribute("tabindex", "-1"); - }); - } catch (exc) { - // Do nothing - } - - // Don't return true since this would prevent the error - // from being printed to the browser console. - return false; -} - -window.addEventListener('error', evt => handleError(evt, evt.error)); -window.addEventListener('unhandledrejection', evt => handleError(evt.reason, evt.reason)); diff --git a/base/app/novnc/app/images/alt.svg b/base/app/novnc/app/images/alt.svg deleted file mode 100644 index e5bb461..0000000 --- a/base/app/novnc/app/images/alt.svg +++ /dev/null @@ -1,92 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="25" - height="25" - viewBox="0 0 25 25" - id="svg2" - version="1.1" - inkscape:version="0.91 r13725" - sodipodi:docname="alt.svg" - inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> - <defs - id="defs4" /> - <sodipodi:namedview - id="base" - pagecolor="#959595" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:zoom="16" - inkscape:cx="18.205425" - inkscape:cy="17.531398" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="false" - units="px" - inkscape:snap-bbox="true" - inkscape:bbox-paths="true" - inkscape:bbox-nodes="true" - inkscape:snap-bbox-edge-midpoints="true" - inkscape:object-paths="true" - showguides="true" - inkscape:window-width="1920" - inkscape:window-height="1136" - inkscape:window-x="1920" - inkscape:window-y="27" - inkscape:window-maximized="1" - inkscape:snap-smooth-nodes="true" - inkscape:object-nodes="true" - inkscape:snap-intersection-paths="true" - inkscape:snap-nodes="true" - inkscape:snap-global="true"> - <inkscape:grid - type="xygrid" - id="grid4136" /> - </sodipodi:namedview> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(0,-1027.3622)"> - <g - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:48px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'Sans Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="text5290"> - <path - d="m 9.9560547,1042.3329 -2.9394531,0 -0.4638672,1.3281 -1.8896485,0 2.7001953,-7.29 2.241211,0 2.7001958,7.29 -1.889649,0 -0.4589843,-1.3281 z m -2.4707031,-1.3526 1.9970703,0 -0.9960938,-2.9003 -1.0009765,2.9003 z" - style="font-size:10px;fill:#ffffff;fill-opacity:1" - id="path5340" /> - <path - d="m 13.188477,1036.0634 1.748046,0 0,7.5976 -1.748046,0 0,-7.5976 z" - style="font-size:10px;fill:#ffffff;fill-opacity:1" - id="path5342" /> - <path - d="m 18.535156,1036.6395 0,1.5528 1.801758,0 0,1.25 -1.801758,0 0,2.3193 q 0,0.3809 0.151367,0.5176 0.151368,0.1318 0.600586,0.1318 l 0.898438,0 0,1.25 -1.499024,0 q -1.035156,0 -1.469726,-0.4297 -0.429688,-0.4345 -0.429688,-1.4697 l 0,-2.3193 -0.86914,0 0,-1.25 0.86914,0 0,-1.5528 1.748047,0 z" - style="font-size:10px;fill:#ffffff;fill-opacity:1" - id="path5344" /> - </g> - </g> -</svg> diff --git a/base/app/novnc/app/images/clipboard.svg b/base/app/novnc/app/images/clipboard.svg deleted file mode 100644 index 79af275..0000000 --- a/base/app/novnc/app/images/clipboard.svg +++ /dev/null @@ -1,106 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="25" - height="25" - viewBox="0 0 25 25" - id="svg2" - version="1.1" - inkscape:version="0.91 r13725" - sodipodi:docname="clipboard.svg" - inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> - <defs - id="defs4" /> - <sodipodi:namedview - id="base" - pagecolor="#959595" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:zoom="1" - inkscape:cx="15.366606" - inkscape:cy="16.42981" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="false" - units="px" - inkscape:snap-bbox="true" - inkscape:bbox-paths="true" - inkscape:bbox-nodes="true" - inkscape:snap-bbox-edge-midpoints="true" - inkscape:object-paths="true" - showguides="true" - inkscape:window-width="1920" - inkscape:window-height="1136" - inkscape:window-x="1920" - inkscape:window-y="27" - inkscape:window-maximized="1" - inkscape:snap-smooth-nodes="true" - inkscape:object-nodes="true" - inkscape:snap-intersection-paths="true" - inkscape:snap-nodes="true" - inkscape:snap-global="true"> - <inkscape:grid - type="xygrid" - id="grid4136" /> - </sodipodi:namedview> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(0,-1027.3622)"> - <path - style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="M 9,6 6,6 C 5.4459889,6 5,6.4459889 5,7 l 0,13 c 0,0.554011 0.4459889,1 1,1 l 13,0 c 0.554011,0 1,-0.445989 1,-1 L 20,7 C 20,6.4459889 19.554011,6 19,6 l -3,0" - transform="translate(0,1027.3622)" - id="rect6083" - inkscape:connector-curvature="0" - sodipodi:nodetypes="cssssssssc" /> - <rect - style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect6085" - width="7" - height="4" - x="9" - y="1031.3622" - ry="1.00002" /> - <path - style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.50196081" - d="m 8.5071212,1038.8622 7.9999998,0" - id="path6087" - inkscape:connector-curvature="0" /> - <path - style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.50196081" - d="m 8.5071212,1041.8622 3.9999998,0" - id="path6089" - inkscape:connector-curvature="0" /> - <path - style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.50196081" - d="m 8.5071212,1044.8622 5.9999998,0" - id="path6091" - inkscape:connector-curvature="0" /> - </g> -</svg> diff --git a/base/app/novnc/app/images/connect.svg b/base/app/novnc/app/images/connect.svg deleted file mode 100644 index 56cde41..0000000 --- a/base/app/novnc/app/images/connect.svg +++ /dev/null @@ -1,96 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="25" - height="25" - viewBox="0 0 25 25" - id="svg2" - version="1.1" - inkscape:version="0.91 r13725" - sodipodi:docname="connect.svg" - inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> - <defs - id="defs4" /> - <sodipodi:namedview - id="base" - pagecolor="#959595" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:zoom="1" - inkscape:cx="37.14834" - inkscape:cy="1.9525926" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="false" - units="px" - inkscape:snap-bbox="true" - inkscape:bbox-paths="true" - inkscape:bbox-nodes="true" - inkscape:snap-bbox-edge-midpoints="true" - inkscape:object-paths="true" - showguides="true" - inkscape:window-width="1920" - inkscape:window-height="1136" - inkscape:window-x="1920" - inkscape:window-y="27" - inkscape:window-maximized="1" - inkscape:snap-smooth-nodes="true" - inkscape:object-nodes="true" - inkscape:snap-intersection-paths="true" - inkscape:snap-nodes="true"> - <inkscape:grid - type="xygrid" - id="grid4136" /> - </sodipodi:namedview> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(0,-1027.3622)"> - <g - id="g5103" - transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,-729.15757,315.8823)"> - <path - sodipodi:nodetypes="cssssc" - inkscape:connector-curvature="0" - id="rect5096" - d="m 11,1040.3622 -5,0 c -1.108,0 -2,-0.892 -2,-2 l 0,-4 c 0,-1.108 0.892,-2 2,-2 l 5,0" - style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> - <path - style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 14,1032.3622 5,0 c 1.108,0 2,0.892 2,2 l 0,4 c 0,1.108 -0.892,2 -2,2 l -5,0" - id="path5099" - inkscape:connector-curvature="0" - sodipodi:nodetypes="cssssc" /> - <path - inkscape:connector-curvature="0" - id="path5101" - d="m 9,1036.3622 7,0" - style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> - </g> - </g> -</svg> diff --git a/base/app/novnc/app/images/ctrl.svg b/base/app/novnc/app/images/ctrl.svg deleted file mode 100644 index 856e939..0000000 --- a/base/app/novnc/app/images/ctrl.svg +++ /dev/null @@ -1,96 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="25" - height="25" - viewBox="0 0 25 25" - id="svg2" - version="1.1" - inkscape:version="0.91 r13725" - sodipodi:docname="ctrl.svg" - inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> - <defs - id="defs4" /> - <sodipodi:namedview - id="base" - pagecolor="#959595" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:zoom="16" - inkscape:cx="18.205425" - inkscape:cy="17.531398" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="false" - units="px" - inkscape:snap-bbox="true" - inkscape:bbox-paths="true" - inkscape:bbox-nodes="true" - inkscape:snap-bbox-edge-midpoints="true" - inkscape:object-paths="true" - showguides="true" - inkscape:window-width="1920" - inkscape:window-height="1136" - inkscape:window-x="1920" - inkscape:window-y="27" - inkscape:window-maximized="1" - inkscape:snap-smooth-nodes="true" - inkscape:object-nodes="true" - inkscape:snap-intersection-paths="true" - inkscape:snap-nodes="true" - inkscape:snap-global="true"> - <inkscape:grid - type="xygrid" - id="grid4136" /> - </sodipodi:namedview> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(0,-1027.3622)"> - <g - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:48px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'Sans Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="text5290"> - <path - d="m 9.1210938,1043.1898 q -0.5175782,0.2686 -1.0791016,0.4053 -0.5615235,0.1367 -1.171875,0.1367 -1.8212891,0 -2.8857422,-1.0156 -1.0644531,-1.0205 -1.0644531,-2.7637 0,-1.748 1.0644531,-2.7637 1.0644531,-1.0205 2.8857422,-1.0205 0.6103515,0 1.171875,0.1368 0.5615234,0.1367 1.0791016,0.4052 l 0,1.5088 q -0.522461,-0.3564 -1.0302735,-0.5224 -0.5078125,-0.1661 -1.0693359,-0.1661 -1.0058594,0 -1.5820313,0.6446 -0.5761719,0.6445 -0.5761719,1.7773 0,1.1279 0.5761719,1.7725 0.5761719,0.6445 1.5820313,0.6445 0.5615234,0 1.0693359,-0.166 0.5078125,-0.166 1.0302735,-0.5225 l 0,1.5088 z" - style="font-size:10px;fill:#ffffff;fill-opacity:1" - id="path5370" /> - <path - d="m 12.514648,1036.5687 0,1.5528 1.801758,0 0,1.25 -1.801758,0 0,2.3193 q 0,0.3809 0.151368,0.5176 0.151367,0.1318 0.600586,0.1318 l 0.898437,0 0,1.25 -1.499023,0 q -1.035157,0 -1.469727,-0.4297 -0.429687,-0.4345 -0.429687,-1.4697 l 0,-2.3193 -0.8691411,0 0,-1.25 0.8691411,0 0,-1.5528 1.748046,0 z" - style="font-size:10px;fill:#ffffff;fill-opacity:1" - id="path5372" /> - <path - d="m 19.453125,1039.6107 q -0.229492,-0.1074 -0.458984,-0.1562 -0.22461,-0.054 -0.454102,-0.054 -0.673828,0 -1.040039,0.4345 -0.361328,0.4297 -0.361328,1.2354 l 0,2.5195 -1.748047,0 0,-5.4687 1.748047,0 0,0.8984 q 0.336914,-0.5371 0.771484,-0.7813 0.439453,-0.249 1.049805,-0.249 0.08789,0 0.19043,0.01 0.102539,0 0.297851,0.029 l 0.0049,1.582 z" - style="font-size:10px;fill:#ffffff;fill-opacity:1" - id="path5374" /> - <path - d="m 20.332031,1035.9926 1.748047,0 0,7.5976 -1.748047,0 0,-7.5976 z" - style="font-size:10px;fill:#ffffff;fill-opacity:1" - id="path5376" /> - </g> - </g> -</svg> diff --git a/base/app/novnc/app/images/ctrlaltdel.svg b/base/app/novnc/app/images/ctrlaltdel.svg deleted file mode 100644 index d7744ea..0000000 --- a/base/app/novnc/app/images/ctrlaltdel.svg +++ /dev/null @@ -1,100 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="25" - height="25" - viewBox="0 0 25 25" - id="svg2" - version="1.1" - inkscape:version="0.91 r13725" - sodipodi:docname="ctrlaltdel.svg" - inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> - <defs - id="defs4" /> - <sodipodi:namedview - id="base" - pagecolor="#959595" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:zoom="8" - inkscape:cx="11.135667" - inkscape:cy="16.407428" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="false" - units="px" - inkscape:snap-bbox="true" - inkscape:bbox-paths="true" - inkscape:bbox-nodes="true" - inkscape:snap-bbox-edge-midpoints="true" - inkscape:object-paths="true" - showguides="true" - inkscape:window-width="1920" - inkscape:window-height="1136" - inkscape:window-x="1920" - inkscape:window-y="27" - inkscape:window-maximized="1" - inkscape:snap-smooth-nodes="true" - inkscape:object-nodes="true" - inkscape:snap-intersection-paths="true" - inkscape:snap-nodes="true" - inkscape:snap-global="true"> - <inkscape:grid - type="xygrid" - id="grid4136" /> - </sodipodi:namedview> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(0,-1027.3622)"> - <rect - style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect5253" - width="5" - height="5.0000172" - x="16" - y="1031.3622" - ry="1.0000174" /> - <rect - y="1043.3622" - x="4" - height="5.0000172" - width="5" - id="rect5255" - style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - ry="1.0000174" /> - <rect - style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect5257" - width="5" - height="5.0000172" - x="13" - y="1043.3622" - ry="1.0000174" /> - </g> -</svg> diff --git a/base/app/novnc/app/images/disconnect.svg b/base/app/novnc/app/images/disconnect.svg deleted file mode 100644 index 6be7d18..0000000 --- a/base/app/novnc/app/images/disconnect.svg +++ /dev/null @@ -1,94 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="25" - height="25" - viewBox="0 0 25 25" - id="svg2" - version="1.1" - inkscape:version="0.91 r13725" - sodipodi:docname="disconnect.svg" - inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> - <defs - id="defs4" /> - <sodipodi:namedview - id="base" - pagecolor="#959595" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:zoom="16" - inkscape:cx="25.05707" - inkscape:cy="11.594858" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="false" - units="px" - inkscape:snap-bbox="true" - inkscape:bbox-paths="true" - inkscape:bbox-nodes="true" - inkscape:snap-bbox-edge-midpoints="true" - inkscape:object-paths="true" - showguides="true" - inkscape:window-width="1920" - inkscape:window-height="1136" - inkscape:window-x="1920" - inkscape:window-y="27" - inkscape:window-maximized="1" - inkscape:snap-smooth-nodes="true" - inkscape:object-nodes="true" - inkscape:snap-intersection-paths="true" - inkscape:snap-nodes="true" - inkscape:snap-global="false"> - <inkscape:grid - type="xygrid" - id="grid4136" /> - </sodipodi:namedview> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(0,-1027.3622)"> - <g - id="g5171" - transform="translate(-24.062499,-6.15775e-4)"> - <path - id="path5110" - transform="translate(0,1027.3622)" - d="m 39.744141,3.4960938 c -0.769923,0 -1.539607,0.2915468 -2.121094,0.8730468 l -2.566406,2.5664063 1.414062,1.4140625 2.566406,-2.5664063 c 0.403974,-0.404 1.010089,-0.404 1.414063,0 l 2.828125,2.828125 c 0.40398,0.4039 0.403907,1.0101621 0,1.4140629 l -2.566406,2.566406 1.414062,1.414062 2.566406,-2.566406 c 1.163041,-1.1629 1.162968,-3.0791874 0,-4.2421874 L 41.865234,4.3691406 C 41.283747,3.7876406 40.514063,3.4960937 39.744141,3.4960938 Z M 39.017578,9.015625 a 1.0001,1.0001 0 0 0 -0.6875,0.3027344 l -0.445312,0.4453125 1.414062,1.4140621 0.445313,-0.445312 A 1.0001,1.0001 0 0 0 39.017578,9.015625 Z m -6.363281,0.7070312 a 1.0001,1.0001 0 0 0 -0.6875,0.3027348 L 28.431641,13.5625 c -1.163042,1.163 -1.16297,3.079187 0,4.242188 l 2.828125,2.828124 c 1.162974,1.163101 3.079213,1.163101 4.242187,0 l 3.535156,-3.535156 a 1.0001,1.0001 0 1 0 -1.414062,-1.414062 l -3.535156,3.535156 c -0.403974,0.404 -1.010089,0.404 -1.414063,0 l -2.828125,-2.828125 c -0.403981,-0.404 -0.403908,-1.010162 0,-1.414063 l 3.535156,-3.537109 A 1.0001,1.0001 0 0 0 32.654297,9.7226562 Z m 3.109375,2.1621098 -2.382813,2.384765 a 1.0001,1.0001 0 1 0 1.414063,1.414063 l 2.382812,-2.384766 -1.414062,-1.414062 z" - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" - inkscape:connector-curvature="0" /> - <rect - transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,0,0)" - y="752.29541" - x="-712.31262" - height="18.000017" - width="3" - id="rect5116" - style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> - </g> - </g> -</svg> diff --git a/base/app/novnc/app/images/drag.svg b/base/app/novnc/app/images/drag.svg deleted file mode 100644 index 139caf9..0000000 --- a/base/app/novnc/app/images/drag.svg +++ /dev/null @@ -1,76 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="25" - height="25" - viewBox="0 0 25 25" - id="svg2" - version="1.1" - inkscape:version="0.91 r13725" - sodipodi:docname="drag.svg" - inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> - <defs - id="defs4" /> - <sodipodi:namedview - id="base" - pagecolor="#959595" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:zoom="22.627417" - inkscape:cx="9.8789407" - inkscape:cy="9.5008608" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="true" - units="px" - inkscape:snap-bbox="true" - inkscape:bbox-paths="true" - inkscape:bbox-nodes="true" - inkscape:snap-bbox-edge-midpoints="true" - inkscape:object-paths="true" - showguides="false" - inkscape:window-width="1920" - inkscape:window-height="1136" - inkscape:window-x="1920" - inkscape:window-y="27" - inkscape:window-maximized="1"> - <inkscape:grid - type="xygrid" - id="grid4136" /> - </sodipodi:namedview> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(0,-1027.3622)"> - <path - style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 7.039733,1049.3037 c -0.4309106,-0.1233 -0.7932634,-0.4631 -0.9705434,-0.9103 -0.04922,-0.1241 -0.057118,-0.2988 -0.071321,-1.5771 l -0.015972,-1.4375 -0.328125,-0.082 c -0.7668138,-0.1927 -1.1897046,-0.4275 -1.7031253,-0.9457 -0.4586773,-0.4629 -0.6804297,-0.8433 -0.867034,-1.4875 -0.067215,-0.232 -0.068001,-0.2642 -0.078682,-3.2188 -0.012078,-3.341 -0.020337,-3.2012 0.2099452,-3.5555 0.2246623,-0.3458 0.5798271,-0.5892 0.9667343,-0.6626 0.092506,-0.017 0.531898,-0.032 0.9764271,-0.032 l 0.8082347,0 1.157e-4,1.336 c 1.125e-4,1.2779 0.00281,1.3403 0.062214,1.4378 0.091785,0.1505 0.2357707,0.226 0.4314082,0.2261 0.285389,2e-4 0.454884,-0.1352 0.5058962,-0.4042 0.019355,-0.102 0.031616,-0.982 0.031616,-2.269 0,-1.9756 0.00357,-2.1138 0.059205,-2.2926 0.1645475,-0.5287 0.6307616,-0.9246 1.19078,-1.0113 0.8000572,-0.1238 1.5711277,0.4446 1.6860387,1.2429 0.01732,0.1203 0.03177,0.8248 0.03211,1.5657 6.19e-4,1.3449 7.22e-4,1.347 0.07093,1.4499 0.108355,0.1587 0.255268,0.2248 0.46917,0.2108 0.204069,-0.013 0.316116,-0.08 0.413642,-0.2453 0.06028,-0.1024 0.06307,-0.1778 0.07862,-2.1218 0.01462,-1.8283 0.02124,-2.0285 0.07121,-2.1549 0.260673,-0.659 0.934894,-1.0527 1.621129,-0.9465 0.640523,0.099 1.152269,0.6104 1.243187,1.2421 0.01827,0.1269 0.03175,0.9943 0.03211,2.0657 l 6.19e-4,1.8469 0.07031,0.103 c 0.108355,0.1587 0.255267,0.2248 0.46917,0.2108 0.204069,-0.013 0.316115,-0.08 0.413642,-0.2453 0.05951,-0.1011 0.06329,-0.1786 0.07907,-1.6218 0.01469,-1.3438 0.02277,-1.5314 0.07121,-1.6549 0.257975,-0.6576 0.934425,-1.0527 1.620676,-0.9465 0.640522,0.099 1.152269,0.6104 1.243186,1.2421 0.0186,0.1292 0.03179,1.0759 0.03222,2.3125 7.15e-4,2.0335 0.0025,2.0966 0.06283,2.1956 0.09178,0.1505 0.235771,0.226 0.431409,0.2261 0.285388,2e-4 0.454884,-0.1352 0.505897,-0.4042 0.01874,-0.099 0.03161,-0.8192 0.03161,-1.769 0,-1.4848 0.0043,-1.6163 0.0592,-1.7926 0.164548,-0.5287 0.630762,-0.9246 1.19078,-1.0113 0.800057,-0.1238 1.571128,0.4446 1.686039,1.2429 0.04318,0.2999 0.04372,9.1764 5.78e-4,9.4531 -0.04431,0.2841 -0.217814,0.6241 -0.420069,0.8232 -0.320102,0.315 -0.63307,0.4268 -1.194973,0.4268 l -0.35281,0 -2.51e-4,1.2734 c -1.25e-4,0.7046 -0.01439,1.3642 -0.03191,1.4766 -0.06665,0.4274 -0.372966,0.8704 -0.740031,1.0702 -0.349999,0.1905 0.01748,0.18 -6.242199,0.1776 -5.3622439,0 -5.7320152,-0.01 -5.9121592,-0.057 l 1.4e-5,0 z" - id="path4379" - inkscape:connector-curvature="0" /> - </g> -</svg> diff --git a/base/app/novnc/app/images/error.svg b/base/app/novnc/app/images/error.svg deleted file mode 100644 index 8356d3f..0000000 --- a/base/app/novnc/app/images/error.svg +++ /dev/null @@ -1,81 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="25" - height="25" - viewBox="0 0 25 25" - id="svg2" - version="1.1" - inkscape:version="0.91 r13725" - sodipodi:docname="error.svg" - inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> - <defs - id="defs4" /> - <sodipodi:namedview - id="base" - pagecolor="#959595" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:zoom="1" - inkscape:cx="14.00357" - inkscape:cy="12.443398" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="false" - units="px" - inkscape:snap-bbox="true" - inkscape:bbox-paths="true" - inkscape:bbox-nodes="true" - inkscape:snap-bbox-edge-midpoints="true" - inkscape:object-paths="true" - showguides="true" - inkscape:window-width="1920" - inkscape:window-height="1136" - inkscape:window-x="1920" - inkscape:window-y="27" - inkscape:window-maximized="1" - inkscape:snap-smooth-nodes="true" - inkscape:object-nodes="true" - inkscape:snap-intersection-paths="true" - inkscape:snap-nodes="true" - inkscape:snap-global="true"> - <inkscape:grid - type="xygrid" - id="grid4136" /> - </sodipodi:namedview> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title /> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(0,-1027.3622)"> - <path - style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="M 7 3 C 4.7839905 3 3 4.7839905 3 7 L 3 18 C 3 20.21601 4.7839905 22 7 22 L 18 22 C 20.21601 22 22 20.21601 22 18 L 22 7 C 22 4.7839905 20.21601 3 18 3 L 7 3 z M 7.6992188 6 A 1.6916875 1.6924297 0 0 1 8.9121094 6.5117188 L 12.5 10.101562 L 16.087891 6.5117188 A 1.6916875 1.6924297 0 0 1 17.251953 6 A 1.6916875 1.6924297 0 0 1 18.480469 8.90625 L 14.892578 12.496094 L 18.480469 16.085938 A 1.6916875 1.6924297 0 1 1 16.087891 18.478516 L 12.5 14.888672 L 8.9121094 18.478516 A 1.6916875 1.6924297 0 1 1 6.5214844 16.085938 L 10.109375 12.496094 L 6.5214844 8.90625 A 1.6916875 1.6924297 0 0 1 7.6992188 6 z " - transform="translate(0,1027.3622)" - id="rect4135" /> - </g> -</svg> diff --git a/base/app/novnc/app/images/esc.svg b/base/app/novnc/app/images/esc.svg deleted file mode 100644 index 830152b..0000000 --- a/base/app/novnc/app/images/esc.svg +++ /dev/null @@ -1,92 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="25" - height="25" - viewBox="0 0 25 25" - id="svg2" - version="1.1" - inkscape:version="0.91 r13725" - sodipodi:docname="esc.svg" - inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> - <defs - id="defs4" /> - <sodipodi:namedview - id="base" - pagecolor="#959595" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:zoom="16" - inkscape:cx="18.205425" - inkscape:cy="17.531398" - inkscape:document-units="px" - inkscape:current-layer="text5290" - showgrid="false" - units="px" - inkscape:snap-bbox="true" - inkscape:bbox-paths="true" - inkscape:bbox-nodes="true" - inkscape:snap-bbox-edge-midpoints="true" - inkscape:object-paths="true" - showguides="true" - inkscape:window-width="1920" - inkscape:window-height="1136" - inkscape:window-x="1920" - inkscape:window-y="27" - inkscape:window-maximized="1" - inkscape:snap-smooth-nodes="true" - inkscape:object-nodes="true" - inkscape:snap-intersection-paths="true" - inkscape:snap-nodes="true" - inkscape:snap-global="true"> - <inkscape:grid - type="xygrid" - id="grid4136" /> - </sodipodi:namedview> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(0,-1027.3622)"> - <g - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:48px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'Sans Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="text5290"> - <path - d="m 3.9331055,1036.1464 5.0732422,0 0,1.4209 -3.1933594,0 0,1.3574 3.0029297,0 0,1.4209 -3.0029297,0 0,1.6699 3.3007812,0 0,1.4209 -5.180664,0 0,-7.29 z" - style="font-size:10px;fill:#ffffff;fill-opacity:1" - id="path5314" /> - <path - d="m 14.963379,1038.1385 0,1.3282 q -0.561524,-0.2344 -1.083984,-0.3516 -0.522461,-0.1172 -0.986329,-0.1172 -0.498046,0 -0.742187,0.127 -0.239258,0.122 -0.239258,0.3808 0,0.21 0.180664,0.3223 0.185547,0.1123 0.65918,0.166 l 0.307617,0.044 q 1.342773,0.1709 1.806641,0.5615 0.463867,0.3906 0.463867,1.2256 0,0.874 -0.644531,1.3134 -0.644532,0.4395 -1.923829,0.4395 -0.541992,0 -1.123046,-0.088 -0.576172,-0.083 -1.186524,-0.2539 l 0,-1.3281 q 0.522461,0.2539 1.069336,0.3808 0.551758,0.127 1.118164,0.127 0.512695,0 0.771485,-0.1416 0.258789,-0.1416 0.258789,-0.4199 0,-0.2344 -0.180664,-0.3467 -0.175782,-0.1172 -0.708008,-0.1807 l -0.307617,-0.039 q -1.166993,-0.1465 -1.635743,-0.542 -0.46875,-0.3955 -0.46875,-1.2012 0,-0.8691 0.595703,-1.2891 0.595704,-0.4199 1.826172,-0.4199 0.483399,0 1.015625,0.073 0.532227,0.073 1.157227,0.2294 z" - style="font-size:10px;fill:#ffffff;fill-opacity:1" - id="path5316" /> - <path - d="m 21.066895,1038.1385 0,1.4258 q -0.356446,-0.2441 -0.717774,-0.3613 -0.356445,-0.1172 -0.742187,-0.1172 -0.732422,0 -1.142579,0.4297 -0.405273,0.4248 -0.405273,1.1914 0,0.7666 0.405273,1.1963 0.410157,0.4248 1.142579,0.4248 0.410156,0 0.776367,-0.1221 0.371094,-0.122 0.683594,-0.3613 l 0,1.4307 q -0.410157,0.1513 -0.834961,0.2246 -0.419922,0.078 -0.844727,0.078 -1.479492,0 -2.314453,-0.7568 -0.834961,-0.7618 -0.834961,-2.1143 0,-1.3525 0.834961,-2.1094 0.834961,-0.7617 2.314453,-0.7617 0.429688,0 0.844727,0.078 0.419921,0.073 0.834961,0.2246 z" - style="font-size:10px;fill:#ffffff;fill-opacity:1" - id="path5318" /> - </g> - </g> -</svg> diff --git a/base/app/novnc/app/images/expander.svg b/base/app/novnc/app/images/expander.svg deleted file mode 100644 index e163535..0000000 --- a/base/app/novnc/app/images/expander.svg +++ /dev/null @@ -1,69 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="9" - height="10" - viewBox="0 0 9 10" - id="svg2" - version="1.1" - inkscape:version="0.91 r13725" - sodipodi:docname="expander.svg"> - <defs - id="defs4" /> - <sodipodi:namedview - id="base" - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0.0" - inkscape:pageshadow="2" - inkscape:zoom="45.254834" - inkscape:cx="9.8737281" - inkscape:cy="6.4583132" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="true" - units="px" - inkscape:snap-object-midpoints="false" - inkscape:object-nodes="true" - inkscape:window-width="1920" - inkscape:window-height="1136" - inkscape:window-x="0" - inkscape:window-y="27" - inkscape:window-maximized="1"> - <inkscape:grid - type="xygrid" - id="grid4136" /> - </sodipodi:namedview> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(0,-1042.3622)"> - <path - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" - d="M 2.0800781,1042.3633 A 2.0002,2.0002 0 0 0 0,1044.3613 l 0,6 a 2.0002,2.0002 0 0 0 3.0292969,1.7168 l 5,-3 a 2.0002,2.0002 0 0 0 0,-3.4316 l -5,-3 a 2.0002,2.0002 0 0 0 -0.9492188,-0.2832 z" - id="path4138" - inkscape:connector-curvature="0" /> - </g> -</svg> diff --git a/base/app/novnc/app/images/fullscreen.svg b/base/app/novnc/app/images/fullscreen.svg deleted file mode 100644 index 29bd05d..0000000 --- a/base/app/novnc/app/images/fullscreen.svg +++ /dev/null @@ -1,93 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="25" - height="25" - viewBox="0 0 25 25" - id="svg2" - version="1.1" - inkscape:version="0.91 r13725" - sodipodi:docname="fullscreen.svg" - inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> - <defs - id="defs4" /> - <sodipodi:namedview - id="base" - pagecolor="#959595" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:zoom="1" - inkscape:cx="16.400723" - inkscape:cy="15.083758" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="false" - units="px" - inkscape:snap-bbox="true" - inkscape:bbox-paths="true" - inkscape:bbox-nodes="true" - inkscape:snap-bbox-edge-midpoints="true" - inkscape:object-paths="true" - showguides="false" - inkscape:window-width="1920" - inkscape:window-height="1136" - inkscape:window-x="1920" - inkscape:window-y="27" - inkscape:window-maximized="1" - inkscape:snap-smooth-nodes="true" - inkscape:object-nodes="true" - inkscape:snap-intersection-paths="true" - inkscape:snap-nodes="false"> - <inkscape:grid - type="xygrid" - id="grid4136" /> - </sodipodi:namedview> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(0,-1027.3622)"> - <rect - style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect5006" - width="17" - height="17.000017" - x="4" - y="1031.3622" - ry="3.0000174" /> - <path - style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" - d="m 7.5,1044.8622 4,0 -1.5,-1.5 1.5,-1.5 -1,-1 -1.5,1.5 -1.5,-1.5 0,4 z" - id="path5017" - inkscape:connector-curvature="0" /> - <path - inkscape:connector-curvature="0" - id="path5025" - d="m 17.5,1034.8622 -4,0 1.5,1.5 -1.5,1.5 1,1 1.5,-1.5 1.5,1.5 0,-4 z" - style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /> - </g> -</svg> diff --git a/base/app/novnc/app/images/handle.svg b/base/app/novnc/app/images/handle.svg deleted file mode 100644 index 4a7a126..0000000 --- a/base/app/novnc/app/images/handle.svg +++ /dev/null @@ -1,82 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="5" - height="6" - viewBox="0 0 5 6" - id="svg2" - version="1.1" - inkscape:version="0.91 r13725" - sodipodi:docname="handle.svg" - inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> - <defs - id="defs4" /> - <sodipodi:namedview - id="base" - pagecolor="#959595" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:zoom="32" - inkscape:cx="1.3551778" - inkscape:cy="8.7800329" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="true" - units="px" - inkscape:snap-bbox="true" - inkscape:bbox-paths="true" - inkscape:bbox-nodes="true" - inkscape:snap-bbox-edge-midpoints="true" - inkscape:object-paths="true" - showguides="false" - inkscape:window-width="1920" - inkscape:window-height="1136" - inkscape:window-x="1920" - inkscape:window-y="27" - inkscape:window-maximized="1" - inkscape:snap-smooth-nodes="true" - inkscape:object-nodes="true" - inkscape:snap-intersection-paths="true" - inkscape:snap-nodes="true" - inkscape:snap-global="true"> - <inkscape:grid - type="xygrid" - id="grid4136" /> - </sodipodi:namedview> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(0,-1046.3622)"> - <path - style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="m 4.0000803,1049.3622 -3,-2 0,4 z" - id="path4247" - inkscape:connector-curvature="0" - sodipodi:nodetypes="cccc" /> - </g> -</svg> diff --git a/base/app/novnc/app/images/handle_bg.svg b/base/app/novnc/app/images/handle_bg.svg deleted file mode 100644 index 7579c42..0000000 --- a/base/app/novnc/app/images/handle_bg.svg +++ /dev/null @@ -1,172 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="15" - height="50" - viewBox="0 0 15 50" - id="svg2" - version="1.1" - inkscape:version="0.91 r13725" - sodipodi:docname="handle_bg.svg" - inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> - <defs - id="defs4" /> - <sodipodi:namedview - id="base" - pagecolor="#959595" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:zoom="16" - inkscape:cx="-10.001409" - inkscape:cy="24.512566" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="true" - units="px" - inkscape:snap-bbox="true" - inkscape:bbox-paths="true" - inkscape:bbox-nodes="true" - inkscape:snap-bbox-edge-midpoints="true" - inkscape:object-paths="true" - showguides="false" - inkscape:window-width="1920" - inkscape:window-height="1136" - inkscape:window-x="1920" - inkscape:window-y="27" - inkscape:window-maximized="1" - inkscape:snap-smooth-nodes="true" - inkscape:object-nodes="true" - inkscape:snap-intersection-paths="true" - inkscape:snap-nodes="true" - inkscape:snap-global="true"> - <inkscape:grid - type="xygrid" - id="grid4136" /> - </sodipodi:namedview> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(0,-1002.3622)"> - <rect - style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect4249" - width="1" - height="1.0000174" - x="9.5" - y="1008.8622" - ry="1.7382812e-05" /> - <rect - ry="1.7382812e-05" - y="1013.8622" - x="9.5" - height="1.0000174" - width="1" - id="rect4255" - style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> - <rect - ry="1.7382812e-05" - y="1008.8622" - x="4.5" - height="1.0000174" - width="1" - id="rect4261" - style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> - <rect - style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect4263" - width="1" - height="1.0000174" - x="4.5" - y="1013.8622" - ry="1.7382812e-05" /> - <rect - ry="1.7382812e-05" - y="1039.8622" - x="9.5" - height="1.0000174" - width="1" - id="rect4265" - style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> - <rect - style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect4267" - width="1" - height="1.0000174" - x="9.5" - y="1044.8622" - ry="1.7382812e-05" /> - <rect - style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect4269" - width="1" - height="1.0000174" - x="4.5" - y="1039.8622" - ry="1.7382812e-05" /> - <rect - ry="1.7382812e-05" - y="1044.8622" - x="4.5" - height="1.0000174" - width="1" - id="rect4271" - style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> - <rect - style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect4273" - width="1" - height="1.0000174" - x="9.5" - y="1018.8622" - ry="1.7382812e-05" /> - <rect - ry="1.7382812e-05" - y="1018.8622" - x="4.5" - height="1.0000174" - width="1" - id="rect4275" - style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> - <rect - style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect4277" - width="1" - height="1.0000174" - x="9.5" - y="1034.8622" - ry="1.7382812e-05" /> - <rect - ry="1.7382812e-05" - y="1034.8622" - x="4.5" - height="1.0000174" - width="1" - id="rect4279" - style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> - </g> -</svg> diff --git a/base/app/novnc/app/images/icons/Makefile b/base/app/novnc/app/images/icons/Makefile deleted file mode 100644 index 03eaed0..0000000 --- a/base/app/novnc/app/images/icons/Makefile +++ /dev/null @@ -1,42 +0,0 @@ -BROWSER_SIZES := 16 24 32 48 64 -#ANDROID_SIZES := 72 96 144 192 -# FIXME: The ICO is limited to 8 icons due to a Chrome bug: -# https://bugs.chromium.org/p/chromium/issues/detail?id=1381393 -ANDROID_SIZES := 96 144 192 -WEB_ICON_SIZES := $(BROWSER_SIZES) $(ANDROID_SIZES) - -#IOS_1X_SIZES := 20 29 40 76 # No such devices exist anymore -IOS_2X_SIZES := 40 58 80 120 152 167 -IOS_3X_SIZES := 60 87 120 180 -ALL_IOS_SIZES := $(IOS_1X_SIZES) $(IOS_2X_SIZES) $(IOS_3X_SIZES) - -ALL_ICONS := \ - $(ALL_IOS_SIZES:%=novnc-ios-%.png) \ - novnc.ico - -all: $(ALL_ICONS) - -# Our testing shows that the ICO file need to be sorted in largest to -# smallest to get the apporpriate behviour -WEB_ICON_SIZES_REVERSE := $(shell echo $(WEB_ICON_SIZES) | tr ' ' '\n' | sort -nr | tr '\n' ' ') -WEB_BASE_ICONS := $(WEB_ICON_SIZES_REVERSE:%=novnc-%.png) -.INTERMEDIATE: $(WEB_BASE_ICONS) - -novnc.ico: $(WEB_BASE_ICONS) - convert $(WEB_BASE_ICONS) "$@" - -# General conversion -novnc-%.png: novnc-icon.svg - convert -depth 8 -background transparent \ - -size $*x$* "$(lastword $^)" "$@" - -# iOS icons use their own SVG -novnc-ios-%.png: novnc-ios-icon.svg - convert -depth 8 -background transparent \ - -size $*x$* "$(lastword $^)" "$@" - -# The smallest sizes are generated using a different SVG -novnc-16.png novnc-24.png novnc-32.png: novnc-icon-sm.svg - -clean: - rm -f *.png diff --git a/base/app/novnc/app/images/icons/novnc-icon-sm.svg b/base/app/novnc/app/images/icons/novnc-icon-sm.svg deleted file mode 100644 index aa1c6f1..0000000 --- a/base/app/novnc/app/images/icons/novnc-icon-sm.svg +++ /dev/null @@ -1,163 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="16" - height="16" - viewBox="0 0 16 16" - id="svg2" - version="1.1" - inkscape:version="0.91 r13725" - sodipodi:docname="novnc-icon-sm.svg"> - <defs - id="defs4" /> - <sodipodi:namedview - id="base" - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0.0" - inkscape:pageshadow="2" - inkscape:zoom="45.254834" - inkscape:cx="9.722703" - inkscape:cy="5.5311896" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="false" - units="px" - inkscape:object-nodes="true" - inkscape:snap-smooth-nodes="true" - inkscape:snap-midpoints="true" - inkscape:window-width="1920" - inkscape:window-height="1136" - inkscape:window-x="1920" - inkscape:window-y="27" - inkscape:window-maximized="1"> - <inkscape:grid - type="xygrid" - id="grid4169" /> - </sodipodi:namedview> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(0,-1036.3621)"> - <rect - style="opacity:1;fill:#494949;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect4167" - width="16" - height="15.999992" - x="0" - y="1036.3622" - ry="2.6666584" /> - <path - style="opacity:1;fill:#313131;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="M 2.6666667,1036.3621 C 1.1893373,1036.3621 0,1037.5515 0,1039.0288 l 0,10.6666 c 0,1.4774 1.1893373,2.6667 2.6666667,2.6667 l 4,0 C 11.837333,1052.3621 16,1046.7128 16,1039.6955 l 0,-0.6667 c 0,-1.4773 -1.189337,-2.6667 -2.666667,-2.6667 l -10.6666663,0 z" - id="rect4173" - inkscape:connector-curvature="0" /> - <g - id="g4381"> - <g - transform="translate(0.25,0.25)" - style="fill:#000000;fill-opacity:1" - id="g4365"> - <g - style="fill:#000000;fill-opacity:1" - id="g4367"> - <path - inkscape:connector-curvature="0" - id="path4369" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 4.3289754,1039.3621 c 0.1846149,0 0.3419956,0.071 0.4716623,0.2121 C 4.933546,1039.7121 5,1039.8793 5,1040.0759 l 0,3.2862 -1,0 0,-2.964 c 0,-0.024 -0.011592,-0.036 -0.034038,-0.036 l -1.931924,0 C 2.011349,1040.3621 2,1040.3741 2,1040.3981 l 0,2.964 -1,0 0,-4 z" - sodipodi:nodetypes="scsccsssscccs" /> - <path - inkscape:connector-curvature="0" - id="path4371" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 6.6710244,1039.3621 2.6579513,0 c 0.184775,0 0.3419957,0.071 0.471662,0.2121 C 9.933546,1039.7121 10,1039.8793 10,1040.0759 l 0,2.5724 c 0,0.1966 -0.066454,0.3655 -0.1993623,0.5069 -0.1296663,0.1379 -0.286887,0.2069 -0.471662,0.2069 l -2.6579513,0 c -0.184775,0 -0.3436164,-0.069 -0.4765247,-0.2069 C 6.0648334,1043.0138 6,1042.8449 6,1042.6483 l 0,-2.5724 c 0,-0.1966 0.064833,-0.3638 0.1944997,-0.5017 0.1329083,-0.1414 0.2917497,-0.2121 0.4765247,-0.2121 z m 2.2949386,1 -1.931926,0 C 7.011344,1040.3621 7,1040.3741 7,1040.3981 l 0,1.928 c 0,0.024 0.011347,0.036 0.034037,0.036 l 1.931926,0 c 0.02269,0 0.034037,-0.012 0.034037,-0.036 l 0,-1.928 c 0,-0.024 -0.011347,-0.036 -0.034037,-0.036 z" - sodipodi:nodetypes="sscsscsscsscssssssssss" /> - </g> - <g - style="fill:#000000;fill-opacity:1" - id="g4373"> - <path - inkscape:connector-curvature="0" - id="path4375" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 3,1047.1121 1,-2.75 1,0 -1.5,4 -1,0 -1.5,-4 1,0 z" - sodipodi:nodetypes="cccccccc" /> - <path - inkscape:connector-curvature="0" - id="path4377" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 9,1046.8621 0,-2.5 1,0 0,4 -1,0 -2,-2.5 0,2.5 -1,0 0,-4 1,0 z" - sodipodi:nodetypes="ccccccccccc" /> - <path - inkscape:connector-curvature="0" - id="path4379" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 15,1045.3621 -2.96596,0 c -0.02269,0 -0.03404,0.012 -0.03404,0.036 l 0,1.928 c 0,0.024 0.01135,0.036 0.03404,0.036 l 2.96596,0 0,1 -3.324113,0 c -0.188017,0 -0.348479,-0.068 -0.481388,-0.2037 C 11.064833,1048.0192 11,1047.8511 11,1047.6542 l 0,-2.5842 c 0,-0.1969 0.06483,-0.3633 0.194499,-0.4991 0.132909,-0.1392 0.293371,-0.2088 0.481388,-0.2088 l 3.324113,0 z" - sodipodi:nodetypes="cssssccscsscscc" /> - </g> - </g> - <g - id="g4356"> - <g - id="g4347"> - <path - sodipodi:nodetypes="scsccsssscccs" - d="m 4.3289754,1039.3621 c 0.1846149,0 0.3419956,0.071 0.4716623,0.2121 C 4.933546,1039.7121 5,1039.8793 5,1040.0759 l 0,3.2862 -1,0 0,-2.964 c 0,-0.024 -0.011592,-0.036 -0.034038,-0.036 l -1.931924,0 c -0.022689,0 -0.034038,0.012 -0.034038,0.036 l 0,2.964 -1,0 0,-4 z" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#008000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="path4143" - inkscape:connector-curvature="0" /> - <path - sodipodi:nodetypes="sscsscsscsscssssssssss" - d="m 6.6710244,1039.3621 2.6579513,0 c 0.184775,0 0.3419957,0.071 0.471662,0.2121 C 9.933546,1039.7121 10,1039.8793 10,1040.0759 l 0,2.5724 c 0,0.1966 -0.066454,0.3655 -0.1993623,0.5069 -0.1296663,0.1379 -0.286887,0.2069 -0.471662,0.2069 l -2.6579513,0 c -0.184775,0 -0.3436164,-0.069 -0.4765247,-0.2069 C 6.0648334,1043.0138 6,1042.8449 6,1042.6483 l 0,-2.5724 c 0,-0.1966 0.064833,-0.3638 0.1944997,-0.5017 0.1329083,-0.1414 0.2917497,-0.2121 0.4765247,-0.2121 z m 2.2949386,1 -1.931926,0 C 7.011344,1040.3621 7,1040.3741 7,1040.3981 l 0,1.928 c 0,0.024 0.011347,0.036 0.034037,0.036 l 1.931926,0 c 0.02269,0 0.034037,-0.012 0.034037,-0.036 l 0,-1.928 c 0,-0.024 -0.011347,-0.036 -0.034037,-0.036 z" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#008000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="path4145" - inkscape:connector-curvature="0" /> - </g> - <g - id="g4351"> - <path - sodipodi:nodetypes="cccccccc" - d="m 3,1047.1121 1,-2.75 1,0 -1.5,4 -1,0 -1.5,-4 1,0 z" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffff00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="path4147" - inkscape:connector-curvature="0" /> - <path - sodipodi:nodetypes="ccccccccccc" - d="m 9,1046.8621 0,-2.5 1,0 0,4 -1,0 -2,-2.5 0,2.5 -1,0 0,-4 1,0 z" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffff00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="path4149" - inkscape:connector-curvature="0" /> - <path - sodipodi:nodetypes="cssssccscsscscc" - d="m 15,1045.3621 -2.96596,0 c -0.02269,0 -0.03404,0.012 -0.03404,0.036 l 0,1.928 c 0,0.024 0.01135,0.036 0.03404,0.036 l 2.96596,0 0,1 -3.324113,0 c -0.188017,0 -0.348479,-0.068 -0.481388,-0.2037 C 11.064833,1048.0192 11,1047.8511 11,1047.6542 l 0,-2.5842 c 0,-0.1969 0.06483,-0.3633 0.194499,-0.4991 0.132909,-0.1392 0.293371,-0.2088 0.481388,-0.2088 l 3.324113,0 z" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffff00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="path4151" - inkscape:connector-curvature="0" /> - </g> - </g> - </g> - </g> -</svg> diff --git a/base/app/novnc/app/images/icons/novnc-icon.svg b/base/app/novnc/app/images/icons/novnc-icon.svg deleted file mode 100644 index 1efff91..0000000 --- a/base/app/novnc/app/images/icons/novnc-icon.svg +++ /dev/null @@ -1,163 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="48" - height="48" - viewBox="0 0 48 48.000001" - id="svg2" - version="1.1" - inkscape:version="0.91 r13725" - sodipodi:docname="novnc-icon.svg"> - <defs - id="defs4" /> - <sodipodi:namedview - id="base" - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0.0" - inkscape:pageshadow="2" - inkscape:zoom="11.313708" - inkscape:cx="27.187245" - inkscape:cy="17.700974" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="false" - units="px" - inkscape:object-nodes="true" - inkscape:snap-smooth-nodes="true" - inkscape:snap-midpoints="true" - inkscape:window-width="1920" - inkscape:window-height="1136" - inkscape:window-x="1920" - inkscape:window-y="27" - inkscape:window-maximized="1"> - <inkscape:grid - type="xygrid" - id="grid4169" /> - </sodipodi:namedview> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title /> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(0,-1004.3621)"> - <rect - style="opacity:1;fill:#494949;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect4167" - width="48" - height="48" - x="0" - y="1004.3621" - ry="7.9999785" /> - <path - style="opacity:1;fill:#313131;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 8,1004.3621 c -4.4319881,0 -8,3.568 -8,8 l 0,32 c 0,4.432 3.5680119,8 8,8 l 12,0 c 15.512,0 28,-16.948 28,-38 l 0,-2 c 0,-4.432 -3.568012,-8 -8,-8 l -32,0 z" - id="rect4173" - inkscape:connector-curvature="0" /> - <g - id="g4300" - style="fill:#000000;fill-opacity:1;stroke:none" - transform="translate(0.5,0.5)"> - <g - id="g4302" - style="fill:#000000;fill-opacity:1;stroke:none"> - <path - sodipodi:nodetypes="scsccsssscccs" - d="m 11.986926,1016.3621 c 0.554325,0 1.025987,0.2121 1.414987,0.6362 0.398725,0.4138 0.600909,0.9155 0.598087,1.5052 l 0,6.8586 -2,0 0,-6.8914 c 0,-0.072 -0.03404,-0.1086 -0.102113,-0.1086 l -4.7957745,0 C 7.0340375,1018.3621 7,1018.3983 7,1018.4707 l 0,6.8914 -2,0 0,-9 z" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="path4304" - inkscape:connector-curvature="0" /> - <path - sodipodi:nodetypes="sscsscsscsscssssssssss" - d="m 17.013073,1016.3621 4.973854,0 c 0.554325,0 1.025987,0.2121 1.414986,0.6362 0.398725,0.4138 0.598087,0.9155 0.598087,1.5052 l 0,4.7172 c 0,0.5897 -0.199362,1.0966 -0.598087,1.5207 -0.388999,0.4138 -0.860661,0.6207 -1.414986,0.6207 l -4.973854,0 c -0.554325,0 -1.030849,-0.2069 -1.429574,-0.6207 C 15.1945,1024.3173 15,1023.8104 15,1023.2207 l 0,-4.7172 c 0,-0.5897 0.1945,-1.0914 0.583499,-1.5052 0.398725,-0.4241 0.875249,-0.6362 1.429574,-0.6362 z m 4.884815,2 -4.795776,0 c -0.06808,0 -0.102112,0.036 -0.102112,0.1086 l 0,4.7828 c 0,0.072 0.03404,0.1086 0.102112,0.1086 l 4.795776,0 c 0.06807,0 0.102112,-0.036 0.102112,-0.1086 l 0,-4.7828 c 0,-0.072 -0.03404,-0.1086 -0.102112,-0.1086 z" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="path4306" - inkscape:connector-curvature="0" /> - </g> - <g - id="g4308" - style="fill:#000000;fill-opacity:1;stroke:none"> - <path - sodipodi:nodetypes="cccccccc" - d="m 12,1036.9177 4.768114,-8.5556 2.231886,0 -6,11 -2,0 -6,-11 2.2318854,0 z" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="path4310" - inkscape:connector-curvature="0" /> - <path - sodipodi:nodetypes="ccccccccccc" - d="m 29,1036.3621 0,-8 2,0 0,11 -2,0 -7,-8 0,8 -2,0 0,-11 2,0 z" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="path4312" - inkscape:connector-curvature="0" /> - <path - sodipodi:nodetypes="cssssccscsscscc" - d="m 43,1030.3621 -8.897887,0 c -0.06808,0 -0.102113,0.036 -0.102113,0.1069 l 0,6.7862 c 0,0.071 0.03404,0.1069 0.102113,0.1069 l 8.897887,0 0,2 -8.972339,0 c -0.56405,0 -1.045437,-0.2037 -1.444162,-0.6111 C 32.1945,1038.3334 32,1037.8292 32,1037.2385 l 0,-6.7528 c 0,-0.5907 0.1945,-1.0898 0.583499,-1.4972 0.398725,-0.4176 0.880112,-0.6264 1.444162,-0.6264 l 8.972339,0 z" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="path4314" - inkscape:connector-curvature="0" /> - </g> - </g> - <g - id="g4291" - style="stroke:none"> - <g - id="g4282" - style="stroke:none"> - <path - inkscape:connector-curvature="0" - id="path4143" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#008000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 11.986926,1016.3621 c 0.554325,0 1.025987,0.2121 1.414987,0.6362 0.398725,0.4138 0.600909,0.9155 0.598087,1.5052 l 0,6.8586 -2,0 0,-6.8914 c 0,-0.072 -0.03404,-0.1086 -0.102113,-0.1086 l -4.7957745,0 C 7.0340375,1018.3621 7,1018.3983 7,1018.4707 l 0,6.8914 -2,0 0,-9 z" - sodipodi:nodetypes="scsccsssscccs" /> - <path - inkscape:connector-curvature="0" - id="path4145" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#008000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 17.013073,1016.3621 4.973854,0 c 0.554325,0 1.025987,0.2121 1.414986,0.6362 0.398725,0.4138 0.598087,0.9155 0.598087,1.5052 l 0,4.7172 c 0,0.5897 -0.199362,1.0966 -0.598087,1.5207 -0.388999,0.4138 -0.860661,0.6207 -1.414986,0.6207 l -4.973854,0 c -0.554325,0 -1.030849,-0.2069 -1.429574,-0.6207 C 15.1945,1024.3173 15,1023.8104 15,1023.2207 l 0,-4.7172 c 0,-0.5897 0.1945,-1.0914 0.583499,-1.5052 0.398725,-0.4241 0.875249,-0.6362 1.429574,-0.6362 z m 4.884815,2 -4.795776,0 c -0.06808,0 -0.102112,0.036 -0.102112,0.1086 l 0,4.7828 c 0,0.072 0.03404,0.1086 0.102112,0.1086 l 4.795776,0 c 0.06807,0 0.102112,-0.036 0.102112,-0.1086 l 0,-4.7828 c 0,-0.072 -0.03404,-0.1086 -0.102112,-0.1086 z" - sodipodi:nodetypes="sscsscsscsscssssssssss" /> - </g> - <g - id="g4286" - style="stroke:none"> - <path - inkscape:connector-curvature="0" - id="path4147" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffff00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 12,1036.9177 4.768114,-8.5556 2.231886,0 -6,11 -2,0 -6,-11 2.2318854,0 z" - sodipodi:nodetypes="cccccccc" /> - <path - inkscape:connector-curvature="0" - id="path4149" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffff00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 29,1036.3621 0,-8 2,0 0,11 -2,0 -7,-8 0,8 -2,0 0,-11 2,0 z" - sodipodi:nodetypes="ccccccccccc" /> - <path - inkscape:connector-curvature="0" - id="path4151" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffff00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 43,1030.3621 -8.897887,0 c -0.06808,0 -0.102113,0.036 -0.102113,0.1069 l 0,6.7862 c 0,0.071 0.03404,0.1069 0.102113,0.1069 l 8.897887,0 0,2 -8.972339,0 c -0.56405,0 -1.045437,-0.2037 -1.444162,-0.6111 C 32.1945,1038.3334 32,1037.8292 32,1037.2385 l 0,-6.7528 c 0,-0.5907 0.1945,-1.0898 0.583499,-1.4972 0.398725,-0.4176 0.880112,-0.6264 1.444162,-0.6264 l 8.972339,0 z" - sodipodi:nodetypes="cssssccscsscscc" /> - </g> - </g> - </g> -</svg> diff --git a/base/app/novnc/app/images/icons/novnc-ios-120.png b/base/app/novnc/app/images/icons/novnc-ios-120.png deleted file mode 100644 index 8da7bab..0000000 Binary files a/base/app/novnc/app/images/icons/novnc-ios-120.png and /dev/null differ diff --git a/base/app/novnc/app/images/icons/novnc-ios-152.png b/base/app/novnc/app/images/icons/novnc-ios-152.png deleted file mode 100644 index 60b2bce..0000000 Binary files a/base/app/novnc/app/images/icons/novnc-ios-152.png and /dev/null differ diff --git a/base/app/novnc/app/images/icons/novnc-ios-167.png b/base/app/novnc/app/images/icons/novnc-ios-167.png deleted file mode 100644 index 98fade2..0000000 Binary files a/base/app/novnc/app/images/icons/novnc-ios-167.png and /dev/null differ diff --git a/base/app/novnc/app/images/icons/novnc-ios-180.png b/base/app/novnc/app/images/icons/novnc-ios-180.png deleted file mode 100644 index 5d24df7..0000000 Binary files a/base/app/novnc/app/images/icons/novnc-ios-180.png and /dev/null differ diff --git a/base/app/novnc/app/images/icons/novnc-ios-40.png b/base/app/novnc/app/images/icons/novnc-ios-40.png deleted file mode 100644 index cf14894..0000000 Binary files a/base/app/novnc/app/images/icons/novnc-ios-40.png and /dev/null differ diff --git a/base/app/novnc/app/images/icons/novnc-ios-58.png b/base/app/novnc/app/images/icons/novnc-ios-58.png deleted file mode 100644 index f6dfbeb..0000000 Binary files a/base/app/novnc/app/images/icons/novnc-ios-58.png and /dev/null differ diff --git a/base/app/novnc/app/images/icons/novnc-ios-60.png b/base/app/novnc/app/images/icons/novnc-ios-60.png deleted file mode 100644 index 8cda295..0000000 Binary files a/base/app/novnc/app/images/icons/novnc-ios-60.png and /dev/null differ diff --git a/base/app/novnc/app/images/icons/novnc-ios-80.png b/base/app/novnc/app/images/icons/novnc-ios-80.png deleted file mode 100644 index 6c417c4..0000000 Binary files a/base/app/novnc/app/images/icons/novnc-ios-80.png and /dev/null differ diff --git a/base/app/novnc/app/images/icons/novnc-ios-87.png b/base/app/novnc/app/images/icons/novnc-ios-87.png deleted file mode 100644 index 4377d87..0000000 Binary files a/base/app/novnc/app/images/icons/novnc-ios-87.png and /dev/null differ diff --git a/base/app/novnc/app/images/icons/novnc-ios-icon.svg b/base/app/novnc/app/images/icons/novnc-ios-icon.svg deleted file mode 100644 index 009452a..0000000 --- a/base/app/novnc/app/images/icons/novnc-ios-icon.svg +++ /dev/null @@ -1,183 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - width="48" - height="48" - viewBox="0 0 48 48.000001" - id="svg2" - version="1.1" - inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" - sodipodi:docname="novnc-ios-icon.svg" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns="http://www.w3.org/2000/svg" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:dc="http://purl.org/dc/elements/1.1/"> - <defs - id="defs4" /> - <sodipodi:namedview - id="base" - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0.0" - inkscape:pageshadow="2" - inkscape:zoom="11.313708" - inkscape:cx="27.356195" - inkscape:cy="17.810253" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="false" - units="px" - inkscape:object-nodes="true" - inkscape:snap-smooth-nodes="true" - inkscape:snap-midpoints="true" - inkscape:window-width="2560" - inkscape:window-height="1371" - inkscape:window-x="0" - inkscape:window-y="0" - inkscape:window-maximized="1" - inkscape:showpageshadow="2" - inkscape:pagecheckerboard="0" - inkscape:deskcolor="#d1d1d1"> - <inkscape:grid - type="xygrid" - id="grid4169" /> - </sodipodi:namedview> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(0,-1004.3621)"> - <rect - style="opacity:1;fill:#494949;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect4167" - width="48" - height="48" - x="0" - y="1004.3621" - inkscape:label="background" /> - <path - style="opacity:1;fill:#313131;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 0,1004.3621 v 48 h 20 c 15.512,0 28,-16.948 28,-38 v -10 z" - id="rect4173" - inkscape:connector-curvature="0" - sodipodi:nodetypes="cccccc" - inkscape:label="darker_grey_plate" /> - <g - id="g4300" - style="display:inline;fill:#000000;fill-opacity:1;stroke:none" - transform="translate(0.5,0.5)" - inkscape:label="shadows"> - <g - id="g4302" - style="fill:#000000;fill-opacity:1;stroke:none" - inkscape:label="no"> - <path - sodipodi:nodetypes="scsccsssscccs" - d="m 11.986926,1016.3621 c 0.554325,0 1.025987,0.2121 1.414987,0.6362 0.398725,0.4138 0.600909,0.9155 0.598087,1.5052 v 6.8586 h -2 v -6.8914 c 0,-0.072 -0.03404,-0.1086 -0.102113,-0.1086 H 7.1021125 C 7.0340375,1018.3621 7,1018.3983 7,1018.4707 v 6.8914 H 5 v -9 z" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="path4304" - inkscape:connector-curvature="0" - inkscape:label="n" /> - <path - sodipodi:nodetypes="sscsscsscsscssssssssss" - d="m 17.013073,1016.3621 h 4.973854 c 0.554325,0 1.025987,0.2121 1.414986,0.6362 0.398725,0.4138 0.598087,0.9155 0.598087,1.5052 v 4.7172 c 0,0.5897 -0.199362,1.0966 -0.598087,1.5207 -0.388999,0.4138 -0.860661,0.6207 -1.414986,0.6207 h -4.973854 c -0.554325,0 -1.030849,-0.2069 -1.429574,-0.6207 C 15.1945,1024.3173 15,1023.8104 15,1023.2207 v -4.7172 c 0,-0.5897 0.1945,-1.0914 0.583499,-1.5052 0.398725,-0.4241 0.875249,-0.6362 1.429574,-0.6362 z m 4.884815,2 h -4.795776 c -0.06808,0 -0.102112,0.036 -0.102112,0.1086 v 4.7828 c 0,0.072 0.03404,0.1086 0.102112,0.1086 h 4.795776 c 0.06807,0 0.102112,-0.036 0.102112,-0.1086 v -4.7828 c 0,-0.072 -0.03404,-0.1086 -0.102112,-0.1086 z" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="path4306" - inkscape:connector-curvature="0" - inkscape:label="o" /> - </g> - <g - id="g4308" - style="fill:#000000;fill-opacity:1;stroke:none" - inkscape:label="VNC"> - <path - sodipodi:nodetypes="cccccccc" - d="m 12,1036.9177 4.768114,-8.5556 H 19 l -6,11 h -2 l -6,-11 h 2.2318854 z" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="path4310" - inkscape:connector-curvature="0" - inkscape:label="V" /> - <path - sodipodi:nodetypes="ccccccccccc" - d="m 29,1036.3621 v -8 h 2 v 11 h -2 l -7,-8 v 8 h -2 v -11 h 2 z" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="path4312" - inkscape:connector-curvature="0" - inkscape:label="N" /> - <path - sodipodi:nodetypes="cssssccscsscscc" - d="m 43,1030.3621 h -8.897887 c -0.06808,0 -0.102113,0.036 -0.102113,0.1069 v 6.7862 c 0,0.071 0.03404,0.1069 0.102113,0.1069 H 43 v 2 h -8.972339 c -0.56405,0 -1.045437,-0.2037 -1.444162,-0.6111 C 32.1945,1038.3334 32,1037.8292 32,1037.2385 v -6.7528 c 0,-0.5907 0.1945,-1.0898 0.583499,-1.4972 0.398725,-0.4176 0.880112,-0.6264 1.444162,-0.6264 H 43 Z" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="path4314" - inkscape:connector-curvature="0" - inkscape:label="C" /> - </g> - </g> - <g - id="g4291" - style="stroke:none" - inkscape:label="noVNC"> - <g - id="g4282" - style="stroke:none" - inkscape:label="no"> - <path - inkscape:connector-curvature="0" - id="path4143" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#008000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 11.986926,1016.3621 c 0.554325,0 1.025987,0.2121 1.414987,0.6362 0.398725,0.4138 0.600909,0.9155 0.598087,1.5052 l 0,6.8586 -2,0 0,-6.8914 c 0,-0.072 -0.03404,-0.1086 -0.102113,-0.1086 l -4.7957745,0 C 7.0340375,1018.3621 7,1018.3983 7,1018.4707 l 0,6.8914 -2,0 0,-9 z" - sodipodi:nodetypes="scsccsssscccs" - inkscape:label="n" /> - <path - inkscape:connector-curvature="0" - id="path4145" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#008000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 17.013073,1016.3621 4.973854,0 c 0.554325,0 1.025987,0.2121 1.414986,0.6362 0.398725,0.4138 0.598087,0.9155 0.598087,1.5052 l 0,4.7172 c 0,0.5897 -0.199362,1.0966 -0.598087,1.5207 -0.388999,0.4138 -0.860661,0.6207 -1.414986,0.6207 l -4.973854,0 c -0.554325,0 -1.030849,-0.2069 -1.429574,-0.6207 C 15.1945,1024.3173 15,1023.8104 15,1023.2207 l 0,-4.7172 c 0,-0.5897 0.1945,-1.0914 0.583499,-1.5052 0.398725,-0.4241 0.875249,-0.6362 1.429574,-0.6362 z m 4.884815,2 -4.795776,0 c -0.06808,0 -0.102112,0.036 -0.102112,0.1086 l 0,4.7828 c 0,0.072 0.03404,0.1086 0.102112,0.1086 l 4.795776,0 c 0.06807,0 0.102112,-0.036 0.102112,-0.1086 l 0,-4.7828 c 0,-0.072 -0.03404,-0.1086 -0.102112,-0.1086 z" - sodipodi:nodetypes="sscsscsscsscssssssssss" - inkscape:label="o" /> - </g> - <g - id="g4286" - style="stroke:none" - inkscape:label="VNC"> - <path - inkscape:connector-curvature="0" - id="path4147" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffff00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 12,1036.9177 4.768114,-8.5556 2.231886,0 -6,11 -2,0 -6,-11 2.2318854,0 z" - sodipodi:nodetypes="cccccccc" - inkscape:label="V" /> - <path - inkscape:connector-curvature="0" - id="path4149" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffff00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 29,1036.3621 0,-8 2,0 0,11 -2,0 -7,-8 0,8 -2,0 0,-11 2,0 z" - sodipodi:nodetypes="ccccccccccc" - inkscape:label="N" /> - <path - inkscape:connector-curvature="0" - id="path4151" - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffff00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 43,1030.3621 -8.897887,0 c -0.06808,0 -0.102113,0.036 -0.102113,0.1069 l 0,6.7862 c 0,0.071 0.03404,0.1069 0.102113,0.1069 l 8.897887,0 0,2 -8.972339,0 c -0.56405,0 -1.045437,-0.2037 -1.444162,-0.6111 C 32.1945,1038.3334 32,1037.8292 32,1037.2385 l 0,-6.7528 c 0,-0.5907 0.1945,-1.0898 0.583499,-1.4972 0.398725,-0.4176 0.880112,-0.6264 1.444162,-0.6264 l 8.972339,0 z" - sodipodi:nodetypes="cssssccscsscscc" - inkscape:label="C" /> - </g> - </g> - </g> -</svg> diff --git a/base/app/novnc/app/images/icons/novnc.ico b/base/app/novnc/app/images/icons/novnc.ico deleted file mode 100644 index c3bc58e..0000000 Binary files a/base/app/novnc/app/images/icons/novnc.ico and /dev/null differ diff --git a/base/app/novnc/app/images/info.svg b/base/app/novnc/app/images/info.svg deleted file mode 100644 index 557b772..0000000 --- a/base/app/novnc/app/images/info.svg +++ /dev/null @@ -1,81 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="25" - height="25" - viewBox="0 0 25 25" - id="svg2" - version="1.1" - inkscape:version="0.91 r13725" - sodipodi:docname="info.svg" - inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> - <defs - id="defs4" /> - <sodipodi:namedview - id="base" - pagecolor="#959595" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:zoom="1" - inkscape:cx="15.720838" - inkscape:cy="8.9111233" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="false" - units="px" - inkscape:snap-bbox="true" - inkscape:bbox-paths="true" - inkscape:bbox-nodes="true" - inkscape:snap-bbox-edge-midpoints="true" - inkscape:object-paths="true" - showguides="false" - inkscape:window-width="1920" - inkscape:window-height="1136" - inkscape:window-x="1920" - inkscape:window-y="27" - inkscape:window-maximized="1" - inkscape:snap-smooth-nodes="true" - inkscape:object-nodes="true" - inkscape:snap-intersection-paths="true" - inkscape:snap-nodes="true" - inkscape:snap-global="true"> - <inkscape:grid - type="xygrid" - id="grid4136" /> - </sodipodi:namedview> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title /> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(0,-1027.3622)"> - <path - style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="M 12.5 3 A 9.5 9.4999914 0 0 0 3 12.5 A 9.5 9.4999914 0 0 0 12.5 22 A 9.5 9.4999914 0 0 0 22 12.5 A 9.5 9.4999914 0 0 0 12.5 3 z M 12.5 5 A 1.5 1.5000087 0 0 1 14 6.5 A 1.5 1.5000087 0 0 1 12.5 8 A 1.5 1.5000087 0 0 1 11 6.5 A 1.5 1.5000087 0 0 1 12.5 5 z M 10.521484 8.9785156 L 12.521484 8.9785156 A 1.50015 1.50015 0 0 1 14.021484 10.478516 L 14.021484 15.972656 A 1.50015 1.50015 0 0 1 14.498047 18.894531 C 14.498047 18.894531 13.74301 19.228309 12.789062 18.912109 C 12.312092 18.754109 11.776235 18.366625 11.458984 17.828125 C 11.141734 17.289525 11.021484 16.668469 11.021484 15.980469 L 11.021484 11.980469 L 10.521484 11.980469 A 1.50015 1.50015 0 1 1 10.521484 8.9804688 L 10.521484 8.9785156 z " - transform="translate(0,1027.3622)" - id="path4136" /> - </g> -</svg> diff --git a/base/app/novnc/app/images/keyboard.svg b/base/app/novnc/app/images/keyboard.svg deleted file mode 100644 index 137b350..0000000 --- a/base/app/novnc/app/images/keyboard.svg +++ /dev/null @@ -1,88 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="25" - height="25" - viewBox="0 0 25 25" - id="svg2" - version="1.1" - inkscape:version="0.91 r13725" - sodipodi:docname="keyboard.svg" - inkscape:export-filename="/home/ossman/devel/noVNC/images/keyboard.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> - <defs - id="defs4" /> - <sodipodi:namedview - id="base" - pagecolor="#717171" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:zoom="1" - inkscape:cx="31.285341" - inkscape:cy="8.8028469" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="false" - units="px" - inkscape:snap-bbox="true" - inkscape:bbox-paths="true" - inkscape:bbox-nodes="true" - inkscape:snap-bbox-edge-midpoints="true" - inkscape:snap-bbox-midpoints="false" - inkscape:window-width="1920" - inkscape:window-height="1136" - inkscape:window-x="1920" - inkscape:window-y="27" - inkscape:window-maximized="1" - inkscape:object-paths="true" - inkscape:snap-intersection-paths="true" - inkscape:object-nodes="true" - inkscape:snap-midpoints="true" - inkscape:snap-smooth-nodes="true"> - <inkscape:grid - type="xygrid" - id="grid4136" /> - </sodipodi:namedview> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title /> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(0,-1027.3622)"> - <path - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" - d="M 7,3 C 4.8012876,3 3,4.8013 3,7 3,11.166667 3,15.333333 3,19.5 3,20.8764 4.1236413,22 5.5,22 l 14,0 C 20.876358,22 22,20.8764 22,19.5 22,15.333333 22,11.166667 22,7 22,4.8013 20.198712,3 18,3 Z m 0,2 11,0 c 1.125307,0 2,0.8747 2,2 L 20,12 5,12 5,7 C 5,5.8747 5.8746931,5 7,5 Z M 6.5,14 C 6.777,14 7,14.223 7,14.5 7,14.777 6.777,15 6.5,15 6.223,15 6,14.777 6,14.5 6,14.223 6.223,14 6.5,14 Z m 2,0 C 8.777,14 9,14.223 9,14.5 9,14.777 8.777,15 8.5,15 8.223,15 8,14.777 8,14.5 8,14.223 8.223,14 8.5,14 Z m 2,0 C 10.777,14 11,14.223 11,14.5 11,14.777 10.777,15 10.5,15 10.223,15 10,14.777 10,14.5 10,14.223 10.223,14 10.5,14 Z m 2,0 C 12.777,14 13,14.223 13,14.5 13,14.777 12.777,15 12.5,15 12.223,15 12,14.777 12,14.5 12,14.223 12.223,14 12.5,14 Z m 2,0 C 14.777,14 15,14.223 15,14.5 15,14.777 14.777,15 14.5,15 14.223,15 14,14.777 14,14.5 14,14.223 14.223,14 14.5,14 Z m 2,0 C 16.777,14 17,14.223 17,14.5 17,14.777 16.777,15 16.5,15 16.223,15 16,14.777 16,14.5 16,14.223 16.223,14 16.5,14 Z m 2,0 C 18.777,14 19,14.223 19,14.5 19,14.777 18.777,15 18.5,15 18.223,15 18,14.777 18,14.5 18,14.223 18.223,14 18.5,14 Z m -13,2 C 5.777,16 6,16.223 6,16.5 6,16.777 5.777,17 5.5,17 5.223,17 5,16.777 5,16.5 5,16.223 5.223,16 5.5,16 Z m 2,0 C 7.777,16 8,16.223 8,16.5 8,16.777 7.777,17 7.5,17 7.223,17 7,16.777 7,16.5 7,16.223 7.223,16 7.5,16 Z m 2,0 C 9.777,16 10,16.223 10,16.5 10,16.777 9.777,17 9.5,17 9.223,17 9,16.777 9,16.5 9,16.223 9.223,16 9.5,16 Z m 2,0 C 11.777,16 12,16.223 12,16.5 12,16.777 11.777,17 11.5,17 11.223,17 11,16.777 11,16.5 11,16.223 11.223,16 11.5,16 Z m 2,0 C 13.777,16 14,16.223 14,16.5 14,16.777 13.777,17 13.5,17 13.223,17 13,16.777 13,16.5 13,16.223 13.223,16 13.5,16 Z m 2,0 C 15.777,16 16,16.223 16,16.5 16,16.777 15.777,17 15.5,17 15.223,17 15,16.777 15,16.5 15,16.223 15.223,16 15.5,16 Z m 2,0 C 17.777,16 18,16.223 18,16.5 18,16.777 17.777,17 17.5,17 17.223,17 17,16.777 17,16.5 17,16.223 17.223,16 17.5,16 Z m 2,0 C 19.777,16 20,16.223 20,16.5 20,16.777 19.777,17 19.5,17 19.223,17 19,16.777 19,16.5 19,16.223 19.223,16 19.5,16 Z M 6,18 c 0.554,0 1,0.446 1,1 0,0.554 -0.446,1 -1,1 -0.554,0 -1,-0.446 -1,-1 0,-0.554 0.446,-1 1,-1 z m 2.8261719,0 7.3476561,0 C 16.631643,18 17,18.368372 17,18.826172 l 0,0.347656 C 17,19.631628 16.631643,20 16.173828,20 L 8.8261719,20 C 8.3683573,20 8,19.631628 8,19.173828 L 8,18.826172 C 8,18.368372 8.3683573,18 8.8261719,18 Z m 10.1113281,0 0.125,0 C 19.581551,18 20,18.4184 20,18.9375 l 0,0.125 C 20,19.5816 19.581551,20 19.0625,20 l -0.125,0 C 18.418449,20 18,19.5816 18,19.0625 l 0,-0.125 C 18,18.4184 18.418449,18 18.9375,18 Z" - transform="translate(0,1027.3622)" - id="rect4160" - inkscape:connector-curvature="0" - sodipodi:nodetypes="sccssccsssssccssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss" /> - <path - style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" - d="m 12.499929,1033.8622 -2,2 1.500071,0 0,2 1,0 0,-2 1.499929,0 z" - id="path4150" - inkscape:connector-curvature="0" - sodipodi:nodetypes="cccccccc" /> - </g> -</svg> diff --git a/base/app/novnc/app/images/power.svg b/base/app/novnc/app/images/power.svg deleted file mode 100644 index 4925d3e..0000000 --- a/base/app/novnc/app/images/power.svg +++ /dev/null @@ -1,87 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="25" - height="25" - viewBox="0 0 25 25" - id="svg2" - version="1.1" - inkscape:version="0.91 r13725" - sodipodi:docname="power.svg" - inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> - <defs - id="defs4" /> - <sodipodi:namedview - id="base" - pagecolor="#959595" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:zoom="1" - inkscape:cx="9.3159849" - inkscape:cy="13.436208" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="false" - units="px" - inkscape:snap-bbox="true" - inkscape:bbox-paths="true" - inkscape:bbox-nodes="true" - inkscape:snap-bbox-edge-midpoints="true" - inkscape:object-paths="true" - showguides="true" - inkscape:window-width="1920" - inkscape:window-height="1136" - inkscape:window-x="1920" - inkscape:window-y="27" - inkscape:window-maximized="1" - inkscape:snap-smooth-nodes="true" - inkscape:object-nodes="true" - inkscape:snap-intersection-paths="true" - inkscape:snap-nodes="true" - inkscape:snap-global="true"> - <inkscape:grid - type="xygrid" - id="grid4136" /> - </sodipodi:namedview> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(0,-1027.3622)"> - <path - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" - d="M 9 6.8183594 C 6.3418164 8.1213032 4.5 10.849161 4.5 14 C 4.5 18.4065 8.0935666 22 12.5 22 C 16.906433 22 20.5 18.4065 20.5 14 C 20.5 10.849161 18.658184 8.1213032 16 6.8183594 L 16 9.125 C 17.514327 10.211757 18.5 11.984508 18.5 14 C 18.5 17.3256 15.825553 20 12.5 20 C 9.1744469 20 6.5 17.3256 6.5 14 C 6.5 11.984508 7.4856727 10.211757 9 9.125 L 9 6.8183594 z " - transform="translate(0,1027.3622)" - id="path6140" /> - <path - style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="m 12.5,1031.8836 0,6.4786" - id="path6142" - inkscape:connector-curvature="0" - sodipodi:nodetypes="cc" /> - </g> -</svg> diff --git a/base/app/novnc/app/images/settings.svg b/base/app/novnc/app/images/settings.svg deleted file mode 100644 index dbb2e80..0000000 --- a/base/app/novnc/app/images/settings.svg +++ /dev/null @@ -1,76 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="25" - height="25" - viewBox="0 0 25 25" - id="svg2" - version="1.1" - inkscape:version="0.91 r13725" - sodipodi:docname="settings.svg" - inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> - <defs - id="defs4" /> - <sodipodi:namedview - id="base" - pagecolor="#959595" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:zoom="22.627417" - inkscape:cx="14.69683" - inkscape:cy="8.8039511" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="true" - units="px" - inkscape:snap-bbox="true" - inkscape:bbox-paths="true" - inkscape:bbox-nodes="true" - inkscape:snap-bbox-edge-midpoints="true" - inkscape:object-paths="true" - showguides="false" - inkscape:window-width="1920" - inkscape:window-height="1136" - inkscape:window-x="1920" - inkscape:window-y="27" - inkscape:window-maximized="1"> - <inkscape:grid - type="xygrid" - id="grid4136" /> - </sodipodi:namedview> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(0,-1027.3622)"> - <path - style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="M 11 3 L 11 5.1601562 A 7.5 7.5 0 0 0 8.3671875 6.2460938 L 6.84375 4.7226562 L 4.7226562 6.84375 L 6.2480469 8.3691406 A 7.5 7.5 0 0 0 5.1523438 11 L 3 11 L 3 14 L 5.1601562 14 A 7.5 7.5 0 0 0 6.2460938 16.632812 L 4.7226562 18.15625 L 6.84375 20.277344 L 8.3691406 18.751953 A 7.5 7.5 0 0 0 11 19.847656 L 11 22 L 14 22 L 14 19.839844 A 7.5 7.5 0 0 0 16.632812 18.753906 L 18.15625 20.277344 L 20.277344 18.15625 L 18.751953 16.630859 A 7.5 7.5 0 0 0 19.847656 14 L 22 14 L 22 11 L 19.839844 11 A 7.5 7.5 0 0 0 18.753906 8.3671875 L 20.277344 6.84375 L 18.15625 4.7226562 L 16.630859 6.2480469 A 7.5 7.5 0 0 0 14 5.1523438 L 14 3 L 11 3 z M 12.5 10 A 2.5 2.5 0 0 1 15 12.5 A 2.5 2.5 0 0 1 12.5 15 A 2.5 2.5 0 0 1 10 12.5 A 2.5 2.5 0 0 1 12.5 10 z " - transform="translate(0,1027.3622)" - id="rect4967" /> - </g> -</svg> diff --git a/base/app/novnc/app/images/tab.svg b/base/app/novnc/app/images/tab.svg deleted file mode 100644 index 1ccb322..0000000 --- a/base/app/novnc/app/images/tab.svg +++ /dev/null @@ -1,86 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="25" - height="25" - viewBox="0 0 25 25" - id="svg2" - version="1.1" - inkscape:version="0.91 r13725" - sodipodi:docname="tab.svg" - inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> - <defs - id="defs4" /> - <sodipodi:namedview - id="base" - pagecolor="#959595" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:zoom="16" - inkscape:cx="11.67335" - inkscape:cy="17.881696" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="false" - units="px" - inkscape:snap-bbox="true" - inkscape:bbox-paths="true" - inkscape:bbox-nodes="true" - inkscape:snap-bbox-edge-midpoints="true" - inkscape:object-paths="true" - showguides="true" - inkscape:window-width="1920" - inkscape:window-height="1136" - inkscape:window-x="1920" - inkscape:window-y="27" - inkscape:window-maximized="1" - inkscape:snap-smooth-nodes="true" - inkscape:object-nodes="true" - inkscape:snap-intersection-paths="true" - inkscape:snap-nodes="true" - inkscape:snap-global="true"> - <inkscape:grid - type="xygrid" - id="grid4136" /> - </sodipodi:namedview> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(0,-1027.3622)"> - <path - style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - d="m 3,1031.3622 0,8 2,0 0,-4 0,-4 -2,0 z m 2,4 4,4 0,-3 13,0 0,-2 -13,0 0,-3 -4,4 z" - id="rect5194" - inkscape:connector-curvature="0" /> - <path - id="path5211" - d="m 22,1048.3622 0,-8 -2,0 0,4 0,4 2,0 z m -2,-4 -4,-4 0,3 -13,0 0,2 13,0 0,3 4,-4 z" - style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - inkscape:connector-curvature="0" /> - </g> -</svg> diff --git a/base/app/novnc/app/images/toggleextrakeys.svg b/base/app/novnc/app/images/toggleextrakeys.svg deleted file mode 100644 index b578c0d..0000000 --- a/base/app/novnc/app/images/toggleextrakeys.svg +++ /dev/null @@ -1,90 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="25" - height="25" - viewBox="0 0 25 25" - id="svg2" - version="1.1" - inkscape:version="0.91 r13725" - sodipodi:docname="extrakeys.svg" - inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> - <defs - id="defs4" /> - <sodipodi:namedview - id="base" - pagecolor="#959595" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:zoom="1" - inkscape:cx="15.234555" - inkscape:cy="9.9710826" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="false" - units="px" - inkscape:snap-bbox="true" - inkscape:bbox-paths="true" - inkscape:bbox-nodes="true" - inkscape:snap-bbox-edge-midpoints="true" - inkscape:object-paths="true" - showguides="false" - inkscape:window-width="1920" - inkscape:window-height="1136" - inkscape:window-x="1920" - inkscape:window-y="27" - inkscape:window-maximized="1" - inkscape:snap-smooth-nodes="true" - inkscape:object-nodes="true" - inkscape:snap-intersection-paths="true" - inkscape:snap-nodes="false"> - <inkscape:grid - type="xygrid" - id="grid4136" /> - </sodipodi:namedview> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(0,-1027.3622)"> - <path - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" - d="m 8,1031.3622 c -2.1987124,0 -4,1.8013 -4,4 l 0,8.9996 c 0,2.1987 1.8012876,4 4,4 l 9,0 c 2.198712,0 4,-1.8013 4,-4 l 0,-8.9996 c 0,-2.1987 -1.801288,-4 -4,-4 z m 0,2 9,0 c 1.125307,0 2,0.8747 2,2 l 0,7.0005 c 0,1.1253 -0.874693,2 -2,2 l -9,0 c -1.1253069,0 -2,-0.8747 -2,-2 l 0,-7.0005 c 0,-1.1253 0.8746931,-2 2,-2 z" - id="rect5006" - inkscape:connector-curvature="0" - sodipodi:nodetypes="ssssssssssssssssss" /> - <g - style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:10px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'Sans Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="text4167" - transform="matrix(0.96021948,0,0,0.96021948,0.18921715,41.80659)"> - <path - d="m 14.292969,1040.6791 -2.939453,0 -0.463868,1.3281 -1.889648,0 2.700195,-7.29 2.241211,0 2.700196,7.29 -1.889649,0 -0.458984,-1.3281 z m -2.470703,-1.3526 1.99707,0 -0.996094,-2.9004 -1.000976,2.9004 z" - id="path4172" - inkscape:connector-curvature="0" /> - </g> - </g> -</svg> diff --git a/base/app/novnc/app/images/warning.svg b/base/app/novnc/app/images/warning.svg deleted file mode 100644 index 7114f9b..0000000 --- a/base/app/novnc/app/images/warning.svg +++ /dev/null @@ -1,81 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="25" - height="25" - viewBox="0 0 25 25" - id="svg2" - version="1.1" - inkscape:version="0.91 r13725" - sodipodi:docname="warning.svg" - inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> - <defs - id="defs4" /> - <sodipodi:namedview - id="base" - pagecolor="#959595" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:zoom="1" - inkscape:cx="16.457343" - inkscape:cy="12.179552" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="false" - units="px" - inkscape:snap-bbox="true" - inkscape:bbox-paths="true" - inkscape:bbox-nodes="true" - inkscape:snap-bbox-edge-midpoints="true" - inkscape:object-paths="true" - showguides="false" - inkscape:window-width="1920" - inkscape:window-height="1136" - inkscape:window-x="1920" - inkscape:window-y="27" - inkscape:window-maximized="1" - inkscape:snap-smooth-nodes="true" - inkscape:object-nodes="true" - inkscape:snap-intersection-paths="true" - inkscape:snap-nodes="true" - inkscape:snap-global="true"> - <inkscape:grid - type="xygrid" - id="grid4136" /> - </sodipodi:namedview> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1" - transform="translate(0,-1027.3622)"> - <path - style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" - d="M 12.513672 3.0019531 C 11.751609 2.9919531 11.052563 3.4242687 10.710938 4.1054688 L 3.2109375 19.105469 C 2.5461937 20.435369 3.5132277 21.9999 5 22 L 20 22 C 21.486772 21.9999 22.453806 20.435369 21.789062 19.105469 L 14.289062 4.1054688 C 13.951849 3.4330688 13.265888 3.0066531 12.513672 3.0019531 z M 12.478516 6.9804688 A 1.50015 1.50015 0 0 1 14 8.5 L 14 14.5 A 1.50015 1.50015 0 1 1 11 14.5 L 11 8.5 A 1.50015 1.50015 0 0 1 12.478516 6.9804688 z M 12.5 17 A 1.5 1.5 0 0 1 14 18.5 A 1.5 1.5 0 0 1 12.5 20 A 1.5 1.5 0 0 1 11 18.5 A 1.5 1.5 0 0 1 12.5 17 z " - transform="translate(0,1027.3622)" - id="path4208" /> - </g> -</svg> diff --git a/base/app/novnc/app/images/windows.svg b/base/app/novnc/app/images/windows.svg deleted file mode 100644 index ad5eec3..0000000 --- a/base/app/novnc/app/images/windows.svg +++ /dev/null @@ -1,65 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - version="1.1" - id="svg2" - inkscape:export-ydpi="90" - inkscape:export-xdpi="90" - sodipodi:docname="windows.svg" - inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png" - inkscape:version="0.92.4 (unknown)" - x="0px" - y="0px" - viewBox="-293 384 25 25" - xml:space="preserve" - width="25" - height="25"><metadata - id="metadata21"><rdf:RDF><cc:Work - rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs - id="defs19" /><sodipodi:namedview - pagecolor="#959595" - bordercolor="#666666" - borderopacity="1" - objecttolerance="10" - gridtolerance="10" - guidetolerance="10" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:window-width="1920" - inkscape:window-height="1136" - id="namedview17" - showgrid="true" - inkscape:pagecheckerboard="false" - inkscape:zoom="32" - inkscape:cx="3.926913" - inkscape:cy="13.255959" - inkscape:window-x="1920" - inkscape:window-y="27" - inkscape:window-maximized="1" - inkscape:current-layer="svg2"><inkscape:grid - type="xygrid" - id="grid818" /></sodipodi:namedview> -<style - type="text/css" - id="style2"> - .st0{fill:#FFFFFF;} -</style> - -<path - style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1" - d="M 21 4 L 11 5.1757812 L 11 12 L 21 12 L 21 4 z M 10 5.2949219 L 4 6 L 4 12 L 10 12 L 10 5.2949219 z " - transform="translate(-293,384)" - id="path853" /><path - id="path858" - d="m -272,405 -10,-1.17578 V 397 h 10 z M -283,403.70508 -289,403 v -6 h 6 z" - style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - inkscape:connector-curvature="0" /></svg> \ No newline at end of file diff --git a/base/app/novnc/app/locale/README b/base/app/novnc/app/locale/README deleted file mode 100644 index ca4f548..0000000 --- a/base/app/novnc/app/locale/README +++ /dev/null @@ -1 +0,0 @@ -DO NOT MODIFY THE FILES IN THIS FOLDER, THEY ARE AUTOMATICALLY GENERATED FROM THE PO-FILES. diff --git a/base/app/novnc/app/locale/cs.json b/base/app/novnc/app/locale/cs.json deleted file mode 100644 index 589145e..0000000 --- a/base/app/novnc/app/locale/cs.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "Connecting...": "Připojení...", - "Disconnecting...": "Odpojení...", - "Reconnecting...": "Obnova připojení...", - "Internal error": "Vnitřní chyba", - "Must set host": "Hostitel musí být nastavení", - "Connected (encrypted) to ": "Připojení (šifrované) k ", - "Connected (unencrypted) to ": "Připojení (nešifrované) k ", - "Something went wrong, connection is closed": "Něco se pokazilo, odpojeno", - "Failed to connect to server": "Chyba připojení k serveru", - "Disconnected": "Odpojeno", - "New connection has been rejected with reason: ": "Nové připojení bylo odmítnuto s odůvodněním: ", - "New connection has been rejected": "Nové připojení bylo odmítnuto", - "Password is required": "Je vyžadováno heslo", - "noVNC encountered an error:": "noVNC narazilo na chybu:", - "Hide/Show the control bar": "Skrýt/zobrazit ovládací panel", - "Move/Drag Viewport": "Přesunout/přetáhnout výřez", - "viewport drag": "přesun výřezu", - "Active Mouse Button": "Aktivní tlačítka myši", - "No mousebutton": "Žádné", - "Left mousebutton": "Levé tlačítko myši", - "Middle mousebutton": "Prostřední tlačítko myši", - "Right mousebutton": "Pravé tlačítko myši", - "Keyboard": "Klávesnice", - "Show Keyboard": "Zobrazit klávesnici", - "Extra keys": "Extra klávesy", - "Show Extra Keys": "Zobrazit extra klávesy", - "Ctrl": "Ctrl", - "Toggle Ctrl": "Přepnout Ctrl", - "Alt": "Alt", - "Toggle Alt": "Přepnout Alt", - "Send Tab": "Odeslat tabulátor", - "Tab": "Tab", - "Esc": "Esc", - "Send Escape": "Odeslat Esc", - "Ctrl+Alt+Del": "Ctrl+Alt+Del", - "Send Ctrl-Alt-Del": "Poslat Ctrl-Alt-Del", - "Shutdown/Reboot": "Vypnutí/Restart", - "Shutdown/Reboot...": "Vypnutí/Restart...", - "Power": "Napájení", - "Shutdown": "Vypnout", - "Reboot": "Restart", - "Reset": "Reset", - "Clipboard": "Schránka", - "Clear": "Vymazat", - "Fullscreen": "Celá obrazovka", - "Settings": "Nastavení", - "Shared Mode": "Sdílený režim", - "View Only": "Pouze prohlížení", - "Clip to Window": "Přizpůsobit oknu", - "Scaling Mode:": "Přizpůsobení velikosti", - "None": "Žádné", - "Local Scaling": "Místní", - "Remote Resizing": "Vzdálené", - "Advanced": "Pokročilé", - "Repeater ID:": "ID opakovače", - "WebSocket": "WebSocket", - "Encrypt": "Šifrování:", - "Host:": "Hostitel:", - "Port:": "Port:", - "Path:": "Cesta", - "Automatic Reconnect": "Automatická obnova připojení", - "Reconnect Delay (ms):": "Zpoždění připojení (ms)", - "Show Dot when No Cursor": "Tečka místo chybějícího kurzoru myši", - "Logging:": "Logování:", - "Disconnect": "Odpojit", - "Connect": "Připojit", - "Password:": "Heslo", - "Send Password": "Odeslat heslo", - "Cancel": "Zrušit" -} \ No newline at end of file diff --git a/base/app/novnc/app/locale/de.json b/base/app/novnc/app/locale/de.json deleted file mode 100644 index 62e7336..0000000 --- a/base/app/novnc/app/locale/de.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "Connecting...": "Verbinden...", - "Disconnecting...": "Verbindung trennen...", - "Reconnecting...": "Verbindung wiederherstellen...", - "Internal error": "Interner Fehler", - "Must set host": "Richten Sie den Server ein", - "Connected (encrypted) to ": "Verbunden mit (verschlüsselt) ", - "Connected (unencrypted) to ": "Verbunden mit (unverschlüsselt) ", - "Something went wrong, connection is closed": "Etwas lief schief, Verbindung wurde getrennt", - "Disconnected": "Verbindung zum Server getrennt", - "New connection has been rejected with reason: ": "Verbindung wurde aus folgendem Grund abgelehnt: ", - "New connection has been rejected": "Verbindung wurde abgelehnt", - "Password is required": "Passwort ist erforderlich", - "noVNC encountered an error:": "Ein Fehler ist aufgetreten:", - "Hide/Show the control bar": "Kontrollleiste verstecken/anzeigen", - "Move/Drag Viewport": "Ansichtsfenster verschieben/ziehen", - "viewport drag": "Ansichtsfenster ziehen", - "Active Mouse Button": "Aktive Maustaste", - "No mousebutton": "Keine Maustaste", - "Left mousebutton": "Linke Maustaste", - "Middle mousebutton": "Mittlere Maustaste", - "Right mousebutton": "Rechte Maustaste", - "Keyboard": "Tastatur", - "Show Keyboard": "Tastatur anzeigen", - "Extra keys": "Zusatztasten", - "Show Extra Keys": "Zusatztasten anzeigen", - "Ctrl": "Strg", - "Toggle Ctrl": "Strg umschalten", - "Alt": "Alt", - "Toggle Alt": "Alt umschalten", - "Send Tab": "Tab senden", - "Tab": "Tab", - "Esc": "Esc", - "Send Escape": "Escape senden", - "Ctrl+Alt+Del": "Strg+Alt+Entf", - "Send Ctrl-Alt-Del": "Strg+Alt+Entf senden", - "Shutdown/Reboot": "Herunterfahren/Neustarten", - "Shutdown/Reboot...": "Herunterfahren/Neustarten...", - "Power": "Energie", - "Shutdown": "Herunterfahren", - "Reboot": "Neustarten", - "Reset": "Zurücksetzen", - "Clipboard": "Zwischenablage", - "Clear": "Löschen", - "Fullscreen": "Vollbild", - "Settings": "Einstellungen", - "Shared Mode": "Geteilter Modus", - "View Only": "Nur betrachten", - "Clip to Window": "Auf Fenster begrenzen", - "Scaling Mode:": "Skalierungsmodus:", - "None": "Keiner", - "Local Scaling": "Lokales skalieren", - "Remote Resizing": "Serverseitiges skalieren", - "Advanced": "Erweitert", - "Repeater ID:": "Repeater ID:", - "WebSocket": "WebSocket", - "Encrypt": "Verschlüsselt", - "Host:": "Server:", - "Port:": "Port:", - "Path:": "Pfad:", - "Automatic Reconnect": "Automatisch wiederverbinden", - "Reconnect Delay (ms):": "Wiederverbindungsverzögerung (ms):", - "Logging:": "Protokollierung:", - "Disconnect": "Verbindung trennen", - "Connect": "Verbinden", - "Password:": "Passwort:", - "Cancel": "Abbrechen", - "Canvas not supported.": "Canvas nicht unterstützt." -} \ No newline at end of file diff --git a/base/app/novnc/app/locale/el.json b/base/app/novnc/app/locale/el.json deleted file mode 100644 index 4df3e03..0000000 --- a/base/app/novnc/app/locale/el.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "HTTPS is required for full functionality": "Το HTTPS είναι απαιτούμενο για πλήρη λειτουργικότητα", - "Connecting...": "Συνδέεται...", - "Disconnecting...": "Aποσυνδέεται...", - "Reconnecting...": "Επανασυνδέεται...", - "Internal error": "Εσωτερικό σφάλμα", - "Must set host": "Πρέπει να οριστεί ο διακομιστής", - "Connected (encrypted) to ": "Συνδέθηκε (κρυπτογραφημένα) με το ", - "Connected (unencrypted) to ": "Συνδέθηκε (μη κρυπτογραφημένα) με το ", - "Something went wrong, connection is closed": "Κάτι πήγε στραβά, η σύνδεση διακόπηκε", - "Failed to connect to server": "Αποτυχία στη σύνδεση με το διακομιστή", - "Disconnected": "Αποσυνδέθηκε", - "New connection has been rejected with reason: ": "Η νέα σύνδεση απορρίφθηκε διότι: ", - "New connection has been rejected": "Η νέα σύνδεση απορρίφθηκε ", - "Credentials are required": "Απαιτούνται διαπιστευτήρια", - "noVNC encountered an error:": "το noVNC αντιμετώπισε ένα σφάλμα:", - "Hide/Show the control bar": "Απόκρυψη/Εμφάνιση γραμμής ελέγχου", - "Drag": "Σύρσιμο", - "Move/Drag Viewport": "Μετακίνηση/Σύρσιμο Θεατού πεδίου", - "Keyboard": "Πληκτρολόγιο", - "Show Keyboard": "Εμφάνιση Πληκτρολογίου", - "Extra keys": "Επιπλέον πλήκτρα", - "Show Extra Keys": "Εμφάνιση Επιπλέον Πλήκτρων", - "Ctrl": "Ctrl", - "Toggle Ctrl": "Εναλλαγή Ctrl", - "Alt": "Alt", - "Toggle Alt": "Εναλλαγή Alt", - "Toggle Windows": "Εναλλαγή Παράθυρων", - "Windows": "Παράθυρα", - "Send Tab": "Αποστολή Tab", - "Tab": "Tab", - "Esc": "Esc", - "Send Escape": "Αποστολή Escape", - "Ctrl+Alt+Del": "Ctrl+Alt+Del", - "Send Ctrl-Alt-Del": "Αποστολή Ctrl-Alt-Del", - "Shutdown/Reboot": "Κλείσιμο/Επανεκκίνηση", - "Shutdown/Reboot...": "Κλείσιμο/Επανεκκίνηση...", - "Power": "Απενεργοποίηση", - "Shutdown": "Κλείσιμο", - "Reboot": "Επανεκκίνηση", - "Reset": "Επαναφορά", - "Clipboard": "Πρόχειρο", - "Edit clipboard content in the textarea below.": "Επεξεργαστείτε το περιεχόμενο του πρόχειρου στην περιοχή κειμένου παρακάτω.", - "Settings": "Ρυθμίσεις", - "Shared Mode": "Κοινόχρηστη Λειτουργία", - "View Only": "Μόνο Θέαση", - "Clip to Window": "Αποκοπή στο όριο του Παράθυρου", - "Scaling Mode:": "Λειτουργία Κλιμάκωσης:", - "None": "Καμία", - "Local Scaling": "Τοπική Κλιμάκωση", - "Remote Resizing": "Απομακρυσμένη Αλλαγή μεγέθους", - "Advanced": "Για προχωρημένους", - "Quality:": "Ποιότητα:", - "Compression level:": "Επίπεδο συμπίεσης:", - "Repeater ID:": "Repeater ID:", - "WebSocket": "WebSocket", - "Encrypt": "Κρυπτογράφηση", - "Host:": "Όνομα διακομιστή:", - "Port:": "Πόρτα διακομιστή:", - "Path:": "Διαδρομή:", - "Automatic Reconnect": "Αυτόματη επανασύνδεση", - "Reconnect Delay (ms):": "Καθυστέρηση επανασύνδεσης (ms):", - "Show Dot when No Cursor": "Εμφάνιση Τελείας όταν δεν υπάρχει Δρομέας", - "Logging:": "Καταγραφή:", - "Version:": "Έκδοση:", - "Disconnect": "Αποσύνδεση", - "Connect": "Σύνδεση", - "Server identity": "Ταυτότητα Διακομιστή", - "The server has provided the following identifying information:": "Ο διακομιστής παρείχε την ακόλουθη πληροφορία ταυτοποίησης:", - "Fingerprint:": "Δακτυλικό αποτύπωμα:", - "Please verify that the information is correct and press \"Approve\". Otherwise press \"Reject\".": "Παρακαλώ επαληθεύσετε ότι η πληροφορία είναι σωστή και πιέστε \"Αποδοχή\". Αλλιώς πιέστε \"Απόρριψη\".", - "Approve": "Αποδοχή", - "Reject": "Απόρριψη", - "Credentials": "Διαπιστευτήρια", - "Username:": "Κωδικός Χρήστη:", - "Password:": "Κωδικός Πρόσβασης:", - "Send Credentials": "Αποστολή Διαπιστευτηρίων", - "Cancel": "Ακύρωση" -} \ No newline at end of file diff --git a/base/app/novnc/app/locale/es.json b/base/app/novnc/app/locale/es.json deleted file mode 100644 index b9e663a..0000000 --- a/base/app/novnc/app/locale/es.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "Connecting...": "Conectando...", - "Connected (encrypted) to ": "Conectado (con encriptación) a", - "Connected (unencrypted) to ": "Conectado (sin encriptación) a", - "Disconnecting...": "Desconectando...", - "Disconnected": "Desconectado", - "Must set host": "Se debe configurar el host", - "Reconnecting...": "Reconectando...", - "Password is required": "La contraseña es obligatoria", - "Disconnect timeout": "Tiempo de desconexión agotado", - "noVNC encountered an error:": "noVNC ha encontrado un error:", - "Hide/Show the control bar": "Ocultar/Mostrar la barra de control", - "Move/Drag Viewport": "Mover/Arrastrar la ventana", - "viewport drag": "Arrastrar la ventana", - "Active Mouse Button": "Botón activo del ratón", - "No mousebutton": "Ningún botón del ratón", - "Left mousebutton": "Botón izquierdo del ratón", - "Middle mousebutton": "Botón central del ratón", - "Right mousebutton": "Botón derecho del ratón", - "Keyboard": "Teclado", - "Show Keyboard": "Mostrar teclado", - "Extra keys": "Teclas adicionales", - "Show Extra Keys": "Mostrar Teclas Adicionales", - "Ctrl": "Ctrl", - "Toggle Ctrl": "Pulsar/Soltar Ctrl", - "Alt": "Alt", - "Toggle Alt": "Pulsar/Soltar Alt", - "Send Tab": "Enviar Tabulación", - "Tab": "Tabulación", - "Esc": "Esc", - "Send Escape": "Enviar Escape", - "Ctrl+Alt+Del": "Ctrl+Alt+Del", - "Send Ctrl-Alt-Del": "Enviar Ctrl+Alt+Del", - "Shutdown/Reboot": "Apagar/Reiniciar", - "Shutdown/Reboot...": "Apagar/Reiniciar...", - "Power": "Encender", - "Shutdown": "Apagar", - "Reboot": "Reiniciar", - "Reset": "Restablecer", - "Clipboard": "Portapapeles", - "Clear": "Vaciar", - "Fullscreen": "Pantalla Completa", - "Settings": "Configuraciones", - "Encrypt": "Encriptar", - "Shared Mode": "Modo Compartido", - "View Only": "Solo visualización", - "Clip to Window": "Recortar al tamaño de la ventana", - "Scaling Mode:": "Modo de escalado:", - "None": "Ninguno", - "Local Scaling": "Escalado Local", - "Local Downscaling": "Reducción de escala local", - "Remote Resizing": "Cambio de tamaño remoto", - "Advanced": "Avanzado", - "Local Cursor": "Cursor Local", - "Repeater ID:": "ID del Repetidor:", - "WebSocket": "WebSocket", - "Host:": "Host:", - "Port:": "Puerto:", - "Path:": "Ruta:", - "Automatic Reconnect": "Reconexión automática", - "Reconnect Delay (ms):": "Retraso en la reconexión (ms):", - "Logging:": "Registrando:", - "Disconnect": "Desconectar", - "Connect": "Conectar", - "Password:": "Contraseña:", - "Cancel": "Cancelar", - "Canvas not supported.": "Canvas no soportado." -} \ No newline at end of file diff --git a/base/app/novnc/app/locale/fr.json b/base/app/novnc/app/locale/fr.json deleted file mode 100644 index c0eeec7..0000000 --- a/base/app/novnc/app/locale/fr.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "Connecting...": "En cours de connexion...", - "Disconnecting...": "Déconnexion en cours...", - "Reconnecting...": "Reconnexion en cours...", - "Internal error": "Erreur interne", - "Must set host": "Doit définir l'hôte", - "Connected (encrypted) to ": "Connecté (chiffré) à ", - "Connected (unencrypted) to ": "Connecté (non chiffré) à ", - "Something went wrong, connection is closed": "Quelque chose s'est mal passé, la connexion a été fermée", - "Failed to connect to server": "Échec de connexion au serveur", - "Disconnected": "Déconnecté", - "New connection has been rejected with reason: ": "Une nouvelle connexion a été rejetée avec motif : ", - "New connection has been rejected": "Une nouvelle connexion a été rejetée", - "Credentials are required": "Les identifiants sont requis", - "noVNC encountered an error:": "noVNC a rencontré une erreur :", - "Hide/Show the control bar": "Masquer/Afficher la barre de contrôle", - "Drag": "Faire glisser", - "Move/Drag Viewport": "Déplacer/faire glisser le Viewport", - "Keyboard": "Clavier", - "Show Keyboard": "Afficher le clavier", - "Extra keys": "Touches supplémentaires", - "Show Extra Keys": "Afficher les touches supplémentaires", - "Ctrl": "Ctrl", - "Toggle Ctrl": "Basculer Ctrl", - "Alt": "Alt", - "Toggle Alt": "Basculer Alt", - "Toggle Windows": "Basculer Windows", - "Windows": "Windows", - "Send Tab": "Envoyer l'onglet", - "Tab": "l'onglet", - "Esc": "Esc", - "Send Escape": "Envoyer Escape", - "Ctrl+Alt+Del": "Ctrl+Alt+Del", - "Send Ctrl-Alt-Del": "Envoyer Ctrl-Alt-Del", - "Shutdown/Reboot": "Arrêter/Redémarrer", - "Shutdown/Reboot...": "Arrêter/Redémarrer...", - "Power": "Alimentation", - "Shutdown": "Arrêter", - "Reboot": "Redémarrer", - "Reset": "Réinitialiser", - "Clipboard": "Presse-papiers", - "Clear": "Effacer", - "Fullscreen": "Plein écran", - "Settings": "Paramètres", - "Shared Mode": "Mode partagé", - "View Only": "Afficher uniquement", - "Clip to Window": "Clip à fenêtre", - "Scaling Mode:": "Mode mise à l'échelle :", - "None": "Aucun", - "Local Scaling": "Mise à l'échelle locale", - "Remote Resizing": "Redimensionnement à distance", - "Advanced": "Avancé", - "Quality:": "Qualité :", - "Compression level:": "Niveau de compression :", - "Repeater ID:": "ID Répéteur :", - "WebSocket": "WebSocket", - "Encrypt": "Chiffrer", - "Host:": "Hôte :", - "Port:": "Port :", - "Path:": "Chemin :", - "Automatic Reconnect": "Reconnecter automatiquemen", - "Reconnect Delay (ms):": "Délai de reconnexion (ms) :", - "Show Dot when No Cursor": "Afficher le point lorsqu'il n'y a pas de curseur", - "Logging:": "Se connecter :", - "Version:": "Version :", - "Disconnect": "Déconnecter", - "Connect": "Connecter", - "Username:": "Nom d'utilisateur :", - "Password:": "Mot de passe :", - "Send Credentials": "Envoyer les identifiants", - "Cancel": "Annuler" -} \ No newline at end of file diff --git a/base/app/novnc/app/locale/it.json b/base/app/novnc/app/locale/it.json deleted file mode 100644 index 18a7f74..0000000 --- a/base/app/novnc/app/locale/it.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "Connecting...": "Connessione in corso...", - "Disconnecting...": "Disconnessione...", - "Reconnecting...": "Riconnessione...", - "Internal error": "Errore interno", - "Must set host": "Devi impostare l'host", - "Connected (encrypted) to ": "Connesso (crittografato) a ", - "Connected (unencrypted) to ": "Connesso (non crittografato) a", - "Something went wrong, connection is closed": "Qualcosa è andato storto, la connessione è stata chiusa", - "Failed to connect to server": "Impossibile connettersi al server", - "Disconnected": "Disconnesso", - "New connection has been rejected with reason: ": "La nuova connessione è stata rifiutata con motivo: ", - "New connection has been rejected": "La nuova connessione è stata rifiutata", - "Credentials are required": "Le credenziali sono obbligatorie", - "noVNC encountered an error:": "noVNC ha riscontrato un errore:", - "Hide/Show the control bar": "Nascondi/Mostra la barra di controllo", - "Keyboard": "Tastiera", - "Show Keyboard": "Mostra tastiera", - "Extra keys": "Tasti Aggiuntivi", - "Show Extra Keys": "Mostra Tasti Aggiuntivi", - "Ctrl": "Ctrl", - "Toggle Ctrl": "Tieni premuto Ctrl", - "Alt": "Alt", - "Toggle Alt": "Tieni premuto Alt", - "Toggle Windows": "Tieni premuto Windows", - "Windows": "Windows", - "Send Tab": "Invia Tab", - "Tab": "Tab", - "Esc": "Esc", - "Send Escape": "Invia Esc", - "Ctrl+Alt+Del": "Ctrl+Alt+Canc", - "Send Ctrl-Alt-Del": "Invia Ctrl-Alt-Canc", - "Shutdown/Reboot": "Spegnimento/Riavvio", - "Shutdown/Reboot...": "Spegnimento/Riavvio...", - "Power": "Alimentazione", - "Shutdown": "Spegnimento", - "Reboot": "Riavvio", - "Reset": "Reset", - "Clipboard": "Clipboard", - "Clear": "Pulisci", - "Fullscreen": "Schermo intero", - "Settings": "Impostazioni", - "Shared Mode": "Modalità condivisa", - "View Only": "Sola Visualizzazione", - "Scaling Mode:": "Modalità di ridimensionamento:", - "None": "Nessuna", - "Local Scaling": "Ridimensionamento Locale", - "Remote Resizing": "Ridimensionamento Remoto", - "Advanced": "Avanzate", - "Quality:": "Qualità:", - "Compression level:": "Livello Compressione:", - "Repeater ID:": "ID Ripetitore:", - "WebSocket": "WebSocket", - "Encrypt": "Crittografa", - "Host:": "Host:", - "Port:": "Porta:", - "Path:": "Percorso:", - "Automatic Reconnect": "Riconnessione Automatica", - "Reconnect Delay (ms):": "Ritardo Riconnessione (ms):", - "Show Dot when No Cursor": "Mostra Punto quando Nessun Cursore", - "Version:": "Versione:", - "Disconnect": "Disconnetti", - "Connect": "Connetti", - "Username:": "Utente:", - "Password:": "Password:", - "Send Credentials": "Invia Credenziale", - "Cancel": "Annulla" -} \ No newline at end of file diff --git a/base/app/novnc/app/locale/ja.json b/base/app/novnc/app/locale/ja.json deleted file mode 100644 index 70fd7a5..0000000 --- a/base/app/novnc/app/locale/ja.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "HTTPS is required for full functionality": "すべての機能を使用するにはHTTPS接続が必要です", - "Connecting...": "接続しています...", - "Disconnecting...": "切断しています...", - "Reconnecting...": "再接続しています...", - "Internal error": "内部エラー", - "Must set host": "ホストを設定する必要があります", - "Connected (encrypted) to ": "接続しました (暗号化済み): ", - "Connected (unencrypted) to ": "接続しました (暗号化されていません): ", - "Something went wrong, connection is closed": "何らかの問題で、接続が閉じられました", - "Failed to connect to server": "サーバーへの接続に失敗しました", - "Disconnected": "切断しました", - "New connection has been rejected with reason: ": "新規接続は次の理由で拒否されました: ", - "New connection has been rejected": "新規接続は拒否されました", - "Credentials are required": "資格情報が必要です", - "noVNC encountered an error:": "noVNC でエラーが発生しました:", - "Hide/Show the control bar": "コントロールバーを隠す/表示する", - "Drag": "ドラッグ", - "Move/Drag Viewport": "ビューポートを移動/ドラッグ", - "Keyboard": "キーボード", - "Show Keyboard": "キーボードを表示", - "Extra keys": "追加キー", - "Show Extra Keys": "追加キーを表示", - "Ctrl": "Ctrl", - "Toggle Ctrl": "Ctrl キーをトグル", - "Alt": "Alt", - "Toggle Alt": "Alt キーをトグル", - "Toggle Windows": "Windows キーをトグル", - "Windows": "Windows", - "Send Tab": "Tab キーを送信", - "Tab": "Tab", - "Esc": "Esc", - "Send Escape": "Escape キーを送信", - "Ctrl+Alt+Del": "Ctrl+Alt+Del", - "Send Ctrl-Alt-Del": "Ctrl-Alt-Del を送信", - "Shutdown/Reboot": "シャットダウン/再起動", - "Shutdown/Reboot...": "シャットダウン/再起動...", - "Power": "電源", - "Shutdown": "シャットダウン", - "Reboot": "再起動", - "Reset": "リセット", - "Clipboard": "クリップボード", - "Edit clipboard content in the textarea below.": "以下の入力欄からクリップボードの内容を編集できます。", - "Full Screen": "全画面表示", - "Settings": "設定", - "Shared Mode": "共有モード", - "View Only": "表示専用", - "Clip to Window": "ウィンドウにクリップ", - "Scaling Mode:": "スケーリングモード:", - "None": "なし", - "Local Scaling": "ローカルスケーリング", - "Remote Resizing": "リモートでリサイズ", - "Advanced": "高度", - "Quality:": "品質:", - "Compression level:": "圧縮レベル:", - "Repeater ID:": "リピーター ID:", - "WebSocket": "WebSocket", - "Encrypt": "暗号化", - "Host:": "ホスト:", - "Port:": "ポート:", - "Path:": "パス:", - "Automatic Reconnect": "自動再接続", - "Reconnect Delay (ms):": "再接続する遅延 (ミリ秒):", - "Show Dot when No Cursor": "カーソルがないときにドットを表示する", - "Logging:": "ロギング:", - "Version:": "バージョン:", - "Disconnect": "切断", - "Connect": "接続", - "Server identity": "サーバーの識別情報", - "The server has provided the following identifying information:": "サーバーは以下の識別情報を提供しています:", - "Fingerprint:": "フィンガープリント:", - "Please verify that the information is correct and press \"Approve\". Otherwise press \"Reject\".": "この情報が正しい場合は「承認」を、そうでない場合は「拒否」を押してください。", - "Approve": "承認", - "Reject": "拒否", - "Credentials": "資格情報", - "Username:": "ユーザー名:", - "Password:": "パスワード:", - "Send Credentials": "資格情報を送信", - "Cancel": "キャンセル" -} \ No newline at end of file diff --git a/base/app/novnc/app/locale/ko.json b/base/app/novnc/app/locale/ko.json deleted file mode 100644 index e4ecddc..0000000 --- a/base/app/novnc/app/locale/ko.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "Connecting...": "연결중...", - "Disconnecting...": "연결 해제중...", - "Reconnecting...": "재연결중...", - "Internal error": "내부 오류", - "Must set host": "호스트는 설정되어야 합니다.", - "Connected (encrypted) to ": "다음과 (암호화되어) 연결되었습니다:", - "Connected (unencrypted) to ": "다음과 (암호화 없이) 연결되었습니다:", - "Something went wrong, connection is closed": "무언가 잘못되었습니다, 연결이 닫혔습니다.", - "Failed to connect to server": "서버에 연결하지 못했습니다.", - "Disconnected": "연결이 해제되었습니다.", - "New connection has been rejected with reason: ": "새 연결이 다음 이유로 거부되었습니다:", - "New connection has been rejected": "새 연결이 거부되었습니다.", - "Password is required": "비밀번호가 필요합니다.", - "noVNC encountered an error:": "noVNC에 오류가 발생했습니다:", - "Hide/Show the control bar": "컨트롤 바 숨기기/보이기", - "Move/Drag Viewport": "움직이기/드래그 뷰포트", - "viewport drag": "뷰포트 드래그", - "Active Mouse Button": "마우스 버튼 활성화", - "No mousebutton": "마우스 버튼 없음", - "Left mousebutton": "왼쪽 마우스 버튼", - "Middle mousebutton": "중간 마우스 버튼", - "Right mousebutton": "오른쪽 마우스 버튼", - "Keyboard": "키보드", - "Show Keyboard": "키보드 보이기", - "Extra keys": "기타 키들", - "Show Extra Keys": "기타 키들 보이기", - "Ctrl": "Ctrl", - "Toggle Ctrl": "Ctrl 켜기/끄기", - "Alt": "Alt", - "Toggle Alt": "Alt 켜기/끄기", - "Send Tab": "Tab 보내기", - "Tab": "Tab", - "Esc": "Esc", - "Send Escape": "Esc 보내기", - "Ctrl+Alt+Del": "Ctrl+Alt+Del", - "Send Ctrl-Alt-Del": "Ctrl+Alt+Del 보내기", - "Shutdown/Reboot": "셧다운/리붓", - "Shutdown/Reboot...": "셧다운/리붓...", - "Power": "전원", - "Shutdown": "셧다운", - "Reboot": "리붓", - "Reset": "리셋", - "Clipboard": "클립보드", - "Clear": "지우기", - "Fullscreen": "전체화면", - "Settings": "설정", - "Shared Mode": "공유 모드", - "View Only": "보기 전용", - "Clip to Window": "창에 클립", - "Scaling Mode:": "스케일링 모드:", - "None": "없음", - "Local Scaling": "로컬 스케일링", - "Remote Resizing": "원격 크기 조절", - "Advanced": "고급", - "Repeater ID:": "중계 ID", - "WebSocket": "웹소켓", - "Encrypt": "암호화", - "Host:": "호스트:", - "Port:": "포트:", - "Path:": "위치:", - "Automatic Reconnect": "자동 재연결", - "Reconnect Delay (ms):": "재연결 지연 시간 (ms)", - "Logging:": "로깅", - "Disconnect": "연결 해제", - "Connect": "연결", - "Password:": "비밀번호:", - "Send Password": "비밀번호 전송", - "Cancel": "취소" -} \ No newline at end of file diff --git a/base/app/novnc/app/locale/nl.json b/base/app/novnc/app/locale/nl.json deleted file mode 100644 index 0cdcc92..0000000 --- a/base/app/novnc/app/locale/nl.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "Connecting...": "Verbinden...", - "Disconnecting...": "Verbinding verbreken...", - "Reconnecting...": "Opnieuw verbinding maken...", - "Internal error": "Interne fout", - "Must set host": "Host moeten worden ingesteld", - "Connected (encrypted) to ": "Verbonden (versleuteld) met ", - "Connected (unencrypted) to ": "Verbonden (onversleuteld) met ", - "Something went wrong, connection is closed": "Er iets fout gelopen, verbinding werd verbroken", - "Failed to connect to server": "Verbinding maken met server is mislukt", - "Disconnected": "Verbinding verbroken", - "New connection has been rejected with reason: ": "Nieuwe verbinding is geweigerd omwille van de volgende reden: ", - "New connection has been rejected": "Nieuwe verbinding is geweigerd", - "Password is required": "Wachtwoord is vereist", - "noVNC encountered an error:": "noVNC heeft een fout bemerkt:", - "Hide/Show the control bar": "Verberg/Toon de bedieningsbalk", - "Move/Drag Viewport": "Verplaats/Versleep Kijkvenster", - "viewport drag": "kijkvenster slepen", - "Active Mouse Button": "Actieve Muisknop", - "No mousebutton": "Geen muisknop", - "Left mousebutton": "Linker muisknop", - "Middle mousebutton": "Middelste muisknop", - "Right mousebutton": "Rechter muisknop", - "Keyboard": "Toetsenbord", - "Show Keyboard": "Toon Toetsenbord", - "Extra keys": "Extra toetsen", - "Show Extra Keys": "Toon Extra Toetsen", - "Ctrl": "Ctrl", - "Toggle Ctrl": "Ctrl omschakelen", - "Alt": "Alt", - "Toggle Alt": "Alt omschakelen", - "Toggle Windows": "Windows omschakelen", - "Windows": "Windows", - "Send Tab": "Tab Sturen", - "Tab": "Tab", - "Esc": "Esc", - "Send Escape": "Escape Sturen", - "Ctrl+Alt+Del": "Ctrl-Alt-Del", - "Send Ctrl-Alt-Del": "Ctrl-Alt-Del Sturen", - "Shutdown/Reboot": "Uitschakelen/Herstarten", - "Shutdown/Reboot...": "Uitschakelen/Herstarten...", - "Power": "Systeem", - "Shutdown": "Uitschakelen", - "Reboot": "Herstarten", - "Reset": "Resetten", - "Clipboard": "Klembord", - "Clear": "Wissen", - "Fullscreen": "Volledig Scherm", - "Settings": "Instellingen", - "Shared Mode": "Gedeelde Modus", - "View Only": "Alleen Kijken", - "Clip to Window": "Randen buiten venster afsnijden", - "Scaling Mode:": "Schaalmodus:", - "None": "Geen", - "Local Scaling": "Lokaal Schalen", - "Remote Resizing": "Op Afstand Formaat Wijzigen", - "Advanced": "Geavanceerd", - "Repeater ID:": "Repeater ID:", - "WebSocket": "WebSocket", - "Encrypt": "Versleutelen", - "Host:": "Host:", - "Port:": "Poort:", - "Path:": "Pad:", - "Automatic Reconnect": "Automatisch Opnieuw Verbinden", - "Reconnect Delay (ms):": "Vertraging voor Opnieuw Verbinden (ms):", - "Show Dot when No Cursor": "Geef stip weer indien geen cursor", - "Logging:": "Logmeldingen:", - "Disconnect": "Verbinding verbreken", - "Connect": "Verbinden", - "Password:": "Wachtwoord:", - "Send Password": "Verzend Wachtwoord:", - "Cancel": "Annuleren" -} \ No newline at end of file diff --git a/base/app/novnc/app/locale/pl.json b/base/app/novnc/app/locale/pl.json deleted file mode 100644 index 006ac7a..0000000 --- a/base/app/novnc/app/locale/pl.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "Connecting...": "Łączenie...", - "Disconnecting...": "Rozłączanie...", - "Reconnecting...": "Łączenie...", - "Internal error": "Błąd wewnętrzny", - "Must set host": "Host i port są wymagane", - "Connected (encrypted) to ": "Połączenie (szyfrowane) z ", - "Connected (unencrypted) to ": "Połączenie (nieszyfrowane) z ", - "Something went wrong, connection is closed": "Coś poszło źle, połączenie zostało zamknięte", - "Disconnected": "Rozłączony", - "New connection has been rejected with reason: ": "Nowe połączenie zostało odrzucone z powodu: ", - "New connection has been rejected": "Nowe połączenie zostało odrzucone", - "Password is required": "Hasło jest wymagane", - "noVNC encountered an error:": "noVNC napotkało błąd:", - "Hide/Show the control bar": "Pokaż/Ukryj pasek ustawień", - "Move/Drag Viewport": "Ruszaj/Przeciągaj Viewport", - "viewport drag": "przeciągnij viewport", - "Active Mouse Button": "Aktywny Przycisk Myszy", - "No mousebutton": "Brak przycisku myszy", - "Left mousebutton": "Lewy przycisk myszy", - "Middle mousebutton": "Środkowy przycisk myszy", - "Right mousebutton": "Prawy przycisk myszy", - "Keyboard": "Klawiatura", - "Show Keyboard": "Pokaż klawiaturę", - "Extra keys": "Przyciski dodatkowe", - "Show Extra Keys": "Pokaż przyciski dodatkowe", - "Ctrl": "Ctrl", - "Toggle Ctrl": "Przełącz Ctrl", - "Alt": "Alt", - "Toggle Alt": "Przełącz Alt", - "Send Tab": "Wyślij Tab", - "Tab": "Tab", - "Esc": "Esc", - "Send Escape": "Wyślij Escape", - "Ctrl+Alt+Del": "Ctrl+Alt+Del", - "Send Ctrl-Alt-Del": "Wyślij Ctrl-Alt-Del", - "Shutdown/Reboot": "Wyłącz/Uruchom ponownie", - "Shutdown/Reboot...": "Wyłącz/Uruchom ponownie...", - "Power": "Włączony", - "Shutdown": "Wyłącz", - "Reboot": "Uruchom ponownie", - "Reset": "Resetuj", - "Clipboard": "Schowek", - "Clear": "Wyczyść", - "Fullscreen": "Pełny ekran", - "Settings": "Ustawienia", - "Shared Mode": "Tryb Współdzielenia", - "View Only": "Tylko Podgląd", - "Clip to Window": "Przytnij do Okna", - "Scaling Mode:": "Tryb Skalowania:", - "None": "Brak", - "Local Scaling": "Skalowanie lokalne", - "Remote Resizing": "Skalowanie zdalne", - "Advanced": "Zaawansowane", - "Repeater ID:": "ID Repeatera:", - "WebSocket": "WebSocket", - "Encrypt": "Szyfrowanie", - "Host:": "Host:", - "Port:": "Port:", - "Path:": "Ścieżka:", - "Automatic Reconnect": "Automatycznie wznawiaj połączenie", - "Reconnect Delay (ms):": "Opóźnienie wznawiania (ms):", - "Logging:": "Poziom logowania:", - "Disconnect": "Rozłącz", - "Connect": "Połącz", - "Password:": "Hasło:", - "Cancel": "Anuluj", - "Canvas not supported.": "Element Canvas nie jest wspierany." -} \ No newline at end of file diff --git a/base/app/novnc/app/locale/pt_BR.json b/base/app/novnc/app/locale/pt_BR.json deleted file mode 100644 index aa130f7..0000000 --- a/base/app/novnc/app/locale/pt_BR.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "Connecting...": "Conectando...", - "Disconnecting...": "Desconectando...", - "Reconnecting...": "Reconectando...", - "Internal error": "Erro interno", - "Must set host": "É necessário definir o host", - "Connected (encrypted) to ": "Conectado (com criptografia) a ", - "Connected (unencrypted) to ": "Conectado (sem criptografia) a ", - "Something went wrong, connection is closed": "Algo deu errado. A conexão foi encerrada.", - "Failed to connect to server": "Falha ao conectar-se ao servidor", - "Disconnected": "Desconectado", - "New connection has been rejected with reason: ": "A nova conexão foi rejeitada pelo motivo: ", - "New connection has been rejected": "A nova conexão foi rejeitada", - "Credentials are required": "Credenciais são obrigatórias", - "noVNC encountered an error:": "O noVNC encontrou um erro:", - "Hide/Show the control bar": "Esconder/mostrar a barra de controles", - "Drag": "Arrastar", - "Move/Drag Viewport": "Mover/arrastar a janela", - "Keyboard": "Teclado", - "Show Keyboard": "Mostrar teclado", - "Extra keys": "Teclas adicionais", - "Show Extra Keys": "Mostar teclas adicionais", - "Ctrl": "Ctrl", - "Toggle Ctrl": "Pressionar/soltar Ctrl", - "Alt": "Alt", - "Toggle Alt": "Pressionar/soltar Alt", - "Toggle Windows": "Pressionar/soltar Windows", - "Windows": "Windows", - "Send Tab": "Enviar Tab", - "Tab": "Tab", - "Esc": "Esc", - "Send Escape": "Enviar Esc", - "Ctrl+Alt+Del": "Ctrl+Alt+Del", - "Send Ctrl-Alt-Del": "Enviar Ctrl-Alt-Del", - "Shutdown/Reboot": "Desligar/reiniciar", - "Shutdown/Reboot...": "Desligar/reiniciar...", - "Power": "Ligar", - "Shutdown": "Desligar", - "Reboot": "Reiniciar", - "Reset": "Reiniciar (forçado)", - "Clipboard": "Área de transferência", - "Clear": "Limpar", - "Fullscreen": "Tela cheia", - "Settings": "Configurações", - "Shared Mode": "Modo compartilhado", - "View Only": "Apenas visualizar", - "Clip to Window": "Recortar à janela", - "Scaling Mode:": "Modo de dimensionamento:", - "None": "Nenhum", - "Local Scaling": "Local", - "Remote Resizing": "Remoto", - "Advanced": "Avançado", - "Quality:": "Qualidade:", - "Compression level:": "Nível de compressão:", - "Repeater ID:": "ID do repetidor:", - "WebSocket": "WebSocket", - "Encrypt": "Criptografar", - "Host:": "Host:", - "Port:": "Porta:", - "Path:": "Caminho:", - "Automatic Reconnect": "Reconexão automática", - "Reconnect Delay (ms):": "Atraso da reconexão (ms)", - "Show Dot when No Cursor": "Mostrar ponto quando não há cursor", - "Logging:": "Registros:", - "Version:": "Versão:", - "Disconnect": "Desconectar", - "Connect": "Conectar", - "Username:": "Nome de usuário:", - "Password:": "Senha:", - "Send Credentials": "Enviar credenciais", - "Cancel": "Cancelar" -} \ No newline at end of file diff --git a/base/app/novnc/app/locale/ru.json b/base/app/novnc/app/locale/ru.json deleted file mode 100644 index cab9739..0000000 --- a/base/app/novnc/app/locale/ru.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "Connecting...": "Подключение...", - "Disconnecting...": "Отключение...", - "Reconnecting...": "Переподключение...", - "Internal error": "Внутренняя ошибка", - "Must set host": "Задайте имя сервера или IP", - "Connected (encrypted) to ": "Подключено (с шифрованием) к ", - "Connected (unencrypted) to ": "Подключено (без шифрования) к ", - "Something went wrong, connection is closed": "Что-то пошло не так, подключение разорвано", - "Failed to connect to server": "Ошибка подключения к серверу", - "Disconnected": "Отключено", - "New connection has been rejected with reason: ": "Новое соединение отклонено по причине: ", - "New connection has been rejected": "Новое соединение отклонено", - "Credentials are required": "Требуются учетные данные", - "noVNC encountered an error:": "Ошибка noVNC: ", - "Hide/Show the control bar": "Скрыть/Показать контрольную панель", - "Drag": "Переместить", - "Move/Drag Viewport": "Переместить окно", - "Keyboard": "Клавиатура", - "Show Keyboard": "Показать клавиатуру", - "Extra keys": "Дополнительные Кнопки", - "Show Extra Keys": "Показать Дополнительные Кнопки", - "Ctrl": "Ctrl", - "Toggle Ctrl": "Переключение нажатия Ctrl", - "Alt": "Alt", - "Toggle Alt": "Переключение нажатия Alt", - "Toggle Windows": "Переключение вкладок", - "Windows": "Вкладка", - "Send Tab": "Передать нажатие Tab", - "Tab": "Tab", - "Esc": "Esc", - "Send Escape": "Передать нажатие Escape", - "Ctrl+Alt+Del": "Ctrl+Alt+Del", - "Send Ctrl-Alt-Del": "Передать нажатие Ctrl-Alt-Del", - "Shutdown/Reboot": "Выключить/Перезагрузить", - "Shutdown/Reboot...": "Выключить/Перезагрузить...", - "Power": "Питание", - "Shutdown": "Выключить", - "Reboot": "Перезагрузить", - "Reset": "Сброс", - "Clipboard": "Буфер обмена", - "Clear": "Очистить", - "Fullscreen": "Во весь экран", - "Settings": "Настройки", - "Shared Mode": "Общий режим", - "View Only": "Только Просмотр", - "Clip to Window": "В окно", - "Scaling Mode:": "Масштаб:", - "None": "Нет", - "Local Scaling": "Локльный масштаб", - "Remote Resizing": "Удаленная перенастройка размера", - "Advanced": "Дополнительно", - "Quality:": "Качество", - "Compression level:": "Уровень Сжатия", - "Repeater ID:": "Идентификатор ID:", - "WebSocket": "WebSocket", - "Encrypt": "Шифрование", - "Host:": "Сервер:", - "Port:": "Порт:", - "Path:": "Путь:", - "Automatic Reconnect": "Автоматическое переподключение", - "Reconnect Delay (ms):": "Задержка переподключения (мс):", - "Show Dot when No Cursor": "Показать точку вместо курсора", - "Logging:": "Лог:", - "Version:": "Версия", - "Disconnect": "Отключение", - "Connect": "Подключение", - "Username:": "Имя Пользователя", - "Password:": "Пароль:", - "Send Credentials": "Передача Учетных Данных", - "Cancel": "Выход" -} \ No newline at end of file diff --git a/base/app/novnc/app/locale/sv.json b/base/app/novnc/app/locale/sv.json deleted file mode 100644 index 80a400b..0000000 --- a/base/app/novnc/app/locale/sv.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "Running without HTTPS is not recommended, crashes or other issues are likely.": "Det är ej rekommenderat att köra utan HTTPS, krascher och andra problem är troliga.", - "Connecting...": "Ansluter...", - "Disconnecting...": "Kopplar ner...", - "Reconnecting...": "Återansluter...", - "Internal error": "Internt fel", - "Must set host": "Du måste specifiera en värd", - "Failed to connect to server: ": "Misslyckades att ansluta till servern: ", - "Connected (encrypted) to ": "Ansluten (krypterat) till ", - "Connected (unencrypted) to ": "Ansluten (okrypterat) till ", - "Something went wrong, connection is closed": "Något gick fel, anslutningen avslutades", - "Failed to connect to server": "Misslyckades att ansluta till servern", - "Disconnected": "Frånkopplad", - "New connection has been rejected with reason: ": "Ny anslutning har blivit nekad med följande skäl: ", - "New connection has been rejected": "Ny anslutning har blivit nekad", - "Credentials are required": "Användaruppgifter krävs", - "noVNC encountered an error:": "noVNC stötte på ett problem:", - "Hide/Show the control bar": "Göm/Visa kontrollbaren", - "Drag": "Dra", - "Move/Drag Viewport": "Flytta/Dra Vyn", - "Keyboard": "Tangentbord", - "Show Keyboard": "Visa Tangentbord", - "Extra keys": "Extraknappar", - "Show Extra Keys": "Visa Extraknappar", - "Ctrl": "Ctrl", - "Toggle Ctrl": "Växla Ctrl", - "Alt": "Alt", - "Toggle Alt": "Växla Alt", - "Toggle Windows": "Växla Windows", - "Windows": "Windows", - "Send Tab": "Skicka Tab", - "Tab": "Tab", - "Esc": "Esc", - "Send Escape": "Skicka Escape", - "Ctrl+Alt+Del": "Ctrl+Alt+Del", - "Send Ctrl-Alt-Del": "Skicka Ctrl-Alt-Del", - "Shutdown/Reboot": "Stäng av/Boota om", - "Shutdown/Reboot...": "Stäng av/Boota om...", - "Power": "Ström", - "Shutdown": "Stäng av", - "Reboot": "Boota om", - "Reset": "Återställ", - "Clipboard": "Urklipp", - "Edit clipboard content in the textarea below.": "Redigera urklippets innehåll i fältet nedan.", - "Full Screen": "Fullskärm", - "Settings": "Inställningar", - "Shared Mode": "Delat Läge", - "View Only": "Endast Visning", - "Clip to Window": "Begränsa till Fönster", - "Scaling Mode:": "Skalningsläge:", - "None": "Ingen", - "Local Scaling": "Lokal Skalning", - "Remote Resizing": "Ändra Storlek", - "Advanced": "Avancerat", - "Quality:": "Kvalitet:", - "Compression level:": "Kompressionsnivå:", - "Repeater ID:": "Repeater-ID:", - "WebSocket": "WebSocket", - "Encrypt": "Kryptera", - "Host:": "Värd:", - "Port:": "Port:", - "Path:": "Sökväg:", - "Automatic Reconnect": "Automatisk Återanslutning", - "Reconnect Delay (ms):": "Fördröjning (ms):", - "Show Dot when No Cursor": "Visa prick när ingen muspekare finns", - "Logging:": "Loggning:", - "Version:": "Version:", - "Disconnect": "Koppla från", - "Connect": "Anslut", - "Server identity": "Server-identitet", - "The server has provided the following identifying information:": "Servern har gett följande identifierande information:", - "Fingerprint:": "Fingeravtryck:", - "Please verify that the information is correct and press \"Approve\". Otherwise press \"Reject\".": "Kontrollera att informationen är korrekt och tryck sedan \"Godkänn\". Tryck annars \"Neka\".", - "Approve": "Godkänn", - "Reject": "Neka", - "Credentials": "Användaruppgifter", - "Username:": "Användarnamn:", - "Password:": "Lösenord:", - "Send Credentials": "Skicka Användaruppgifter", - "Cancel": "Avbryt" -} \ No newline at end of file diff --git a/base/app/novnc/app/locale/tr.json b/base/app/novnc/app/locale/tr.json deleted file mode 100644 index 451c1b8..0000000 --- a/base/app/novnc/app/locale/tr.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "Connecting...": "Bağlanıyor...", - "Disconnecting...": "Bağlantı kesiliyor...", - "Reconnecting...": "Yeniden bağlantı kuruluyor...", - "Internal error": "İç hata", - "Must set host": "Sunucuyu kur", - "Connected (encrypted) to ": "Bağlı (şifrelenmiş)", - "Connected (unencrypted) to ": "Bağlandı (şifrelenmemiş)", - "Something went wrong, connection is closed": "Bir şeyler ters gitti, bağlantı kesildi", - "Disconnected": "Bağlantı kesildi", - "New connection has been rejected with reason: ": "Bağlantı aşağıdaki nedenlerden dolayı reddedildi: ", - "New connection has been rejected": "Bağlantı reddedildi", - "Password is required": "Şifre gerekli", - "noVNC encountered an error:": "Bir hata oluştu:", - "Hide/Show the control bar": "Denetim masasını Gizle/Göster", - "Move/Drag Viewport": "Görünümü Taşı/Sürükle", - "viewport drag": "Görüntü penceresini sürükle", - "Active Mouse Button": "Aktif Fare Düğmesi", - "No mousebutton": "Fare düğmesi yok", - "Left mousebutton": "Farenin sol düğmesi", - "Middle mousebutton": "Farenin orta düğmesi", - "Right mousebutton": "Farenin sağ düğmesi", - "Keyboard": "Klavye", - "Show Keyboard": "Klavye Düzenini Göster", - "Extra keys": "Ekstra tuşlar", - "Show Extra Keys": "Ekstra tuşları göster", - "Ctrl": "Ctrl", - "Toggle Ctrl": "Ctrl Değiştir ", - "Alt": "Alt", - "Toggle Alt": "Alt Değiştir", - "Send Tab": "Sekme Gönder", - "Tab": "Sekme", - "Esc": "Esc", - "Send Escape": "Boşluk Gönder", - "Ctrl+Alt+Del": "Ctrl + Alt + Del", - "Send Ctrl-Alt-Del": "Ctrl-Alt-Del Gönder", - "Shutdown/Reboot": "Kapat/Yeniden Başlat", - "Shutdown/Reboot...": "Kapat/Yeniden Başlat...", - "Power": "Güç", - "Shutdown": "Kapat", - "Reboot": "Yeniden Başlat", - "Reset": "Sıfırla", - "Clipboard": "Pano", - "Clear": "Temizle", - "Fullscreen": "Tam Ekran", - "Settings": "Ayarlar", - "Shared Mode": "Paylaşım Modu", - "View Only": "Sadece Görüntüle", - "Clip to Window": "Pencereye Tıkla", - "Scaling Mode:": "Ölçekleme Modu:", - "None": "Bilinmeyen", - "Local Scaling": "Yerel Ölçeklendirme", - "Remote Resizing": "Uzaktan Yeniden Boyutlandırma", - "Advanced": "Gelişmiş", - "Repeater ID:": "Tekralayıcı ID:", - "WebSocket": "WebSocket", - "Encrypt": "Şifrele", - "Host:": "Ana makine:", - "Port:": "Port:", - "Path:": "Yol:", - "Automatic Reconnect": "Otomatik Yeniden Bağlan", - "Reconnect Delay (ms):": "Yeniden Bağlanma Süreci (ms):", - "Logging:": "Giriş yapılıyor:", - "Disconnect": "Bağlantıyı Kes", - "Connect": "Bağlan", - "Password:": "Parola:", - "Cancel": "Vazgeç", - "Canvas not supported.": "Tuval desteklenmiyor." -} \ No newline at end of file diff --git a/base/app/novnc/app/locale/zh_CN.json b/base/app/novnc/app/locale/zh_CN.json deleted file mode 100644 index 3679ead..0000000 --- a/base/app/novnc/app/locale/zh_CN.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "Connecting...": "连接中...", - "Connected (encrypted) to ": "已连接(已加密)到", - "Connected (unencrypted) to ": "已连接(未加密)到", - "Disconnecting...": "正在断开连接...", - "Disconnected": "已断开连接", - "Must set host": "必须设置主机", - "Reconnecting...": "重新连接中...", - "Password is required": "请提供密码", - "Disconnect timeout": "超时断开", - "noVNC encountered an error:": "noVNC 遇到一个错误:", - "Hide/Show the control bar": "显示/隐藏控制栏", - "Move/Drag Viewport": "移动/拖动窗口", - "viewport drag": "窗口拖动", - "Active Mouse Button": "启动鼠标按键", - "No mousebutton": "禁用鼠标按键", - "Left mousebutton": "鼠标左键", - "Middle mousebutton": "鼠标中键", - "Right mousebutton": "鼠标右键", - "Keyboard": "键盘", - "Show Keyboard": "显示键盘", - "Extra keys": "额外按键", - "Show Extra Keys": "显示额外按键", - "Ctrl": "Ctrl", - "Toggle Ctrl": "切换 Ctrl", - "Edit clipboard content in the textarea below.": "在下面的文本区域中编辑剪贴板内容。", - "Alt": "Alt", - "Toggle Alt": "切换 Alt", - "Send Tab": "发送 Tab 键", - "Tab": "Tab", - "Esc": "Esc", - "Send Escape": "发送 Escape 键", - "Ctrl+Alt+Del": "Ctrl+Alt+Del", - "Send Ctrl-Alt-Del": "发送 Ctrl+Alt+Del 键", - "Shutdown/Reboot": "关机/重启", - "Shutdown/Reboot...": "关机/重启...", - "Power": "电源", - "Shutdown": "关机", - "Reboot": "重启", - "Reset": "重置", - "Clipboard": "剪贴板", - "Clear": "清除", - "Fullscreen": "全屏", - "Settings": "设置", - "Encrypt": "加密", - "Shared Mode": "分享模式", - "View Only": "仅查看", - "Clip to Window": "限制/裁切窗口大小", - "Scaling Mode:": "缩放模式:", - "None": "无", - "Local Scaling": "本地缩放", - "Local Downscaling": "降低本地尺寸", - "Remote Resizing": "远程调整大小", - "Advanced": "高级", - "Local Cursor": "本地光标", - "Repeater ID:": "中继站 ID", - "WebSocket": "WebSocket", - "Host:": "主机:", - "Port:": "端口:", - "Path:": "路径:", - "Automatic Reconnect": "自动重新连接", - "Reconnect Delay (ms):": "重新连接间隔 (ms):", - "Logging:": "日志级别:", - "Disconnect": "断开连接", - "Connect": "连接", - "Password:": "密码:", - "Cancel": "取消", - "Canvas not supported.": "不支持 Canvas。" -} \ No newline at end of file diff --git a/base/app/novnc/app/locale/zh_TW.json b/base/app/novnc/app/locale/zh_TW.json deleted file mode 100644 index 8ddf813..0000000 --- a/base/app/novnc/app/locale/zh_TW.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "Connecting...": "連線中...", - "Disconnecting...": "正在中斷連線...", - "Reconnecting...": "重新連線中...", - "Internal error": "內部錯誤", - "Must set host": "請提供主機資訊", - "Connected (encrypted) to ": "已加密連線到", - "Connected (unencrypted) to ": "未加密連線到", - "Something went wrong, connection is closed": "發生錯誤,連線已關閉", - "Failed to connect to server": "無法連線到伺服器", - "Disconnected": "連線已中斷", - "New connection has been rejected with reason: ": "連線被拒絕,原因:", - "New connection has been rejected": "連線被拒絕", - "Password is required": "請提供密碼", - "noVNC encountered an error:": "noVNC 遇到一個錯誤:", - "Hide/Show the control bar": "顯示/隱藏控制列", - "Move/Drag Viewport": "拖放顯示範圍", - "viewport drag": "顯示範圍拖放", - "Active Mouse Button": "啟用滑鼠按鍵", - "No mousebutton": "無滑鼠按鍵", - "Left mousebutton": "滑鼠左鍵", - "Middle mousebutton": "滑鼠中鍵", - "Right mousebutton": "滑鼠右鍵", - "Keyboard": "鍵盤", - "Show Keyboard": "顯示鍵盤", - "Extra keys": "額外按鍵", - "Show Extra Keys": "顯示額外按鍵", - "Ctrl": "Ctrl", - "Toggle Ctrl": "切換 Ctrl", - "Alt": "Alt", - "Toggle Alt": "切換 Alt", - "Send Tab": "送出 Tab 鍵", - "Tab": "Tab", - "Esc": "Esc", - "Send Escape": "送出 Escape 鍵", - "Ctrl+Alt+Del": "Ctrl-Alt-Del", - "Send Ctrl-Alt-Del": "送出 Ctrl-Alt-Del 快捷鍵", - "Shutdown/Reboot": "關機/重新啟動", - "Shutdown/Reboot...": "關機/重新啟動...", - "Power": "電源", - "Shutdown": "關機", - "Reboot": "重新啟動", - "Reset": "重設", - "Clipboard": "剪貼簿", - "Clear": "清除", - "Fullscreen": "全螢幕", - "Settings": "設定", - "Shared Mode": "分享模式", - "View Only": "僅檢視", - "Clip to Window": "限制/裁切視窗大小", - "Scaling Mode:": "縮放模式:", - "None": "無", - "Local Scaling": "本機縮放", - "Remote Resizing": "遠端調整大小", - "Advanced": "進階", - "Repeater ID:": "中繼站 ID", - "WebSocket": "WebSocket", - "Encrypt": "加密", - "Host:": "主機:", - "Port:": "連接埠:", - "Path:": "路徑:", - "Automatic Reconnect": "自動重新連線", - "Reconnect Delay (ms):": "重新連線間隔 (ms):", - "Logging:": "日誌級別:", - "Disconnect": "中斷連線", - "Connect": "連線", - "Password:": "密碼:", - "Cancel": "取消" -} \ No newline at end of file diff --git a/base/app/novnc/app/localization.js b/base/app/novnc/app/localization.js deleted file mode 100644 index 7d7e6e6..0000000 --- a/base/app/novnc/app/localization.js +++ /dev/null @@ -1,206 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2018 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - */ - -/* - * Localization Utilities - */ - -export class Localizer { - constructor() { - // Currently configured language - this.language = 'en'; - - // Current dictionary of translations - this._dictionary = undefined; - } - - // Configure suitable language based on user preferences - async setup(supportedLanguages, baseURL) { - this.language = 'en'; // Default: US English - this._dictionary = undefined; - - this._setupLanguage(supportedLanguages); - await this._setupDictionary(baseURL); - } - - _setupLanguage(supportedLanguages) { - /* - * Navigator.languages only available in Chrome (32+) and FireFox (32+) - * Fall back to navigator.language for other browsers - */ - let userLanguages; - if (typeof window.navigator.languages == 'object') { - userLanguages = window.navigator.languages; - } else { - userLanguages = [navigator.language || navigator.userLanguage]; - } - - for (let i = 0;i < userLanguages.length;i++) { - const userLang = userLanguages[i] - .toLowerCase() - .replace("_", "-") - .split("-"); - - // First pass: perfect match - for (let j = 0; j < supportedLanguages.length; j++) { - const supLang = supportedLanguages[j] - .toLowerCase() - .replace("_", "-") - .split("-"); - - if (userLang[0] !== supLang[0]) { - continue; - } - if (userLang[1] !== supLang[1]) { - continue; - } - - this.language = supportedLanguages[j]; - return; - } - - // Second pass: English fallback - if (userLang[0] === 'en') { - return; - } - - // Third pass pass: other fallback - for (let j = 0;j < supportedLanguages.length;j++) { - const supLang = supportedLanguages[j] - .toLowerCase() - .replace("_", "-") - .split("-"); - - if (userLang[0] !== supLang[0]) { - continue; - } - if (supLang[1] !== undefined) { - continue; - } - - this.language = supportedLanguages[j]; - return; - } - } - } - - async _setupDictionary(baseURL) { - if (baseURL) { - if (!baseURL.endsWith("/")) { - baseURL = baseURL + "/"; - } - } else { - baseURL = ""; - } - - if (this.language === "en") { - return; - } - - let response = await fetch(baseURL + this.language + ".json"); - if (!response.ok) { - throw Error("" + response.status + " " + response.statusText); - } - - this._dictionary = await response.json(); - } - - // Retrieve localised text - get(id) { - if (typeof this._dictionary !== 'undefined' && - this._dictionary[id]) { - return this._dictionary[id]; - } else { - return id; - } - } - - // Traverses the DOM and translates relevant fields - // See https://html.spec.whatwg.org/multipage/dom.html#attr-translate - translateDOM() { - const self = this; - - function process(elem, enabled) { - function isAnyOf(searchElement, items) { - return items.indexOf(searchElement) !== -1; - } - - function translateString(str) { - // We assume surrounding whitespace, and whitespace around line - // breaks is just for source formatting - str = str.split("\n").map(s => s.trim()).join(" ").trim(); - return self.get(str); - } - - function translateAttribute(elem, attr) { - const str = translateString(elem.getAttribute(attr)); - elem.setAttribute(attr, str); - } - - function translateTextNode(node) { - const str = translateString(node.data); - node.data = str; - } - - if (elem.hasAttribute("translate")) { - if (isAnyOf(elem.getAttribute("translate"), ["", "yes"])) { - enabled = true; - } else if (isAnyOf(elem.getAttribute("translate"), ["no"])) { - enabled = false; - } - } - - if (enabled) { - if (elem.hasAttribute("abbr") && - elem.tagName === "TH") { - translateAttribute(elem, "abbr"); - } - if (elem.hasAttribute("alt") && - isAnyOf(elem.tagName, ["AREA", "IMG", "INPUT"])) { - translateAttribute(elem, "alt"); - } - if (elem.hasAttribute("download") && - isAnyOf(elem.tagName, ["A", "AREA"])) { - translateAttribute(elem, "download"); - } - if (elem.hasAttribute("label") && - isAnyOf(elem.tagName, ["MENUITEM", "MENU", "OPTGROUP", - "OPTION", "TRACK"])) { - translateAttribute(elem, "label"); - } - // FIXME: Should update "lang" - if (elem.hasAttribute("placeholder") && - isAnyOf(elem.tagName, ["INPUT", "TEXTAREA"])) { - translateAttribute(elem, "placeholder"); - } - if (elem.hasAttribute("title")) { - translateAttribute(elem, "title"); - } - if (elem.hasAttribute("value") && - elem.tagName === "INPUT" && - isAnyOf(elem.getAttribute("type"), ["reset", "button", "submit"])) { - translateAttribute(elem, "value"); - } - } - - for (let i = 0; i < elem.childNodes.length; i++) { - const node = elem.childNodes[i]; - if (node.nodeType === node.ELEMENT_NODE) { - process(node, enabled); - } else if (node.nodeType === node.TEXT_NODE && enabled) { - translateTextNode(node); - } - } - } - - process(document.body, true); - } -} - -export const l10n = new Localizer(); -export default l10n.get.bind(l10n); diff --git a/base/app/novnc/app/sounds/CREDITS b/base/app/novnc/app/sounds/CREDITS deleted file mode 100644 index ec1fb55..0000000 --- a/base/app/novnc/app/sounds/CREDITS +++ /dev/null @@ -1,4 +0,0 @@ -bell - Copyright: Dr. Richard Boulanger et al - URL: http://www.archive.org/details/Berklee44v12 - License: CC-BY Attribution 3.0 Unported diff --git a/base/app/novnc/app/sounds/bell.mp3 b/base/app/novnc/app/sounds/bell.mp3 deleted file mode 100644 index fdbf149..0000000 Binary files a/base/app/novnc/app/sounds/bell.mp3 and /dev/null differ diff --git a/base/app/novnc/app/sounds/bell.oga b/base/app/novnc/app/sounds/bell.oga deleted file mode 100644 index 144d2b3..0000000 Binary files a/base/app/novnc/app/sounds/bell.oga and /dev/null differ diff --git a/base/app/novnc/app/styles/Orbitron700.ttf b/base/app/novnc/app/styles/Orbitron700.ttf deleted file mode 100644 index e28729d..0000000 Binary files a/base/app/novnc/app/styles/Orbitron700.ttf and /dev/null differ diff --git a/base/app/novnc/app/styles/Orbitron700.woff b/base/app/novnc/app/styles/Orbitron700.woff deleted file mode 100644 index 61db630..0000000 Binary files a/base/app/novnc/app/styles/Orbitron700.woff and /dev/null differ diff --git a/base/app/novnc/app/styles/base.css b/base/app/novnc/app/styles/base.css deleted file mode 100644 index f83ad4b..0000000 --- a/base/app/novnc/app/styles/base.css +++ /dev/null @@ -1,922 +0,0 @@ -/* - * noVNC base CSS - * Copyright (C) 2019 The noVNC Authors - * noVNC is licensed under the MPL 2.0 (see LICENSE.txt) - * This file is licensed under the 2-Clause BSD license (see LICENSE.txt). - */ - -/* - * Z index layers: - * - * 0: Main screen - * 10: Control bar - * 50: Transition blocker - * 60: Connection popups - * 100: Status bar - * ... - * 1000: Javascript crash - * ... - * 10000: Max (used for polyfills) - */ - -/* - * State variables (set on :root): - * - * noVNC_loading: Page is still loading - * noVNC_connecting: Connecting to server - * noVNC_reconnecting: Re-establishing a connection - * noVNC_connected: Connected to server (most common state) - * noVNC_disconnecting: Disconnecting from server - */ - -:root { - font-family: sans-serif; -} - -body { - margin:0; - padding:0; - /*Background image with light grey curve.*/ - background-color:#494949; - background-repeat:no-repeat; - background-position:right bottom; - height:100%; - touch-action: none; -} - -html { - height:100%; -} - -.noVNC_only_touch.noVNC_hidden { - display: none; -} - -.noVNC_disabled { - color: rgb(128, 128, 128); -} - -/* ---------------------------------------- - * Spinner - * ---------------------------------------- - */ - -.noVNC_spinner { - position: relative; -} -.noVNC_spinner, .noVNC_spinner::before, .noVNC_spinner::after { - width: 10px; - height: 10px; - border-radius: 2px; - box-shadow: -60px 10px 0 rgba(255, 255, 255, 0); - animation: noVNC_spinner 1.0s linear infinite; -} -.noVNC_spinner::before { - content: ""; - position: absolute; - left: 0px; - top: 0px; - animation-delay: -0.1s; -} -.noVNC_spinner::after { - content: ""; - position: absolute; - top: 0px; - left: 0px; - animation-delay: 0.1s; -} -@keyframes noVNC_spinner { - 0% { box-shadow: -60px 10px 0 rgba(255, 255, 255, 0); width: 20px; } - 25% { box-shadow: 20px 10px 0 rgba(255, 255, 255, 1); width: 10px; } - 50% { box-shadow: 60px 10px 0 rgba(255, 255, 255, 0); width: 10px; } -} - -/* ---------------------------------------- - * WebKit centering hacks - * ---------------------------------------- - */ - -.noVNC_center { - /* - * This is a workaround because webkit misrenders transforms and - * uses non-integer coordinates, resulting in blurry content. - * Ideally we'd use "top: 50%; transform: translateY(-50%);" on - * the objects instead. - */ - display: flex; - align-items: center; - justify-content: center; - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - pointer-events: none; -} -.noVNC_center > * { - pointer-events: auto; -} -.noVNC_vcenter { - display: flex !important; - flex-direction: column; - justify-content: center; - position: fixed; - top: 0; - left: 0; - height: 100%; - margin: 0 !important; - padding: 0 !important; - pointer-events: none; -} -.noVNC_vcenter > * { - pointer-events: auto; -} - -/* ---------------------------------------- - * Layering - * ---------------------------------------- - */ - -.noVNC_connect_layer { - z-index: 60; -} - -/* ---------------------------------------- - * Fallback error - * ---------------------------------------- - */ - -#noVNC_fallback_error { - z-index: 1000; - visibility: hidden; - /* Put a dark background in front of everything but the error, - and don't let mouse events pass through */ - background: rgba(0, 0, 0, 0.8); - pointer-events: all; -} -#noVNC_fallback_error.noVNC_open { - visibility: visible; -} - -#noVNC_fallback_error > div { - max-width: calc(100vw - 30px - 30px); - max-height: calc(100vh - 30px - 30px); - overflow: auto; - - padding: 15px; - - transition: 0.5s ease-in-out; - - transform: translateY(-50px); - opacity: 0; - - text-align: center; - font-weight: bold; - color: #fff; - - border-radius: 10px; - box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5); - background: rgba(200,55,55,0.8); -} -#noVNC_fallback_error.noVNC_open > div { - transform: translateY(0); - opacity: 1; -} - -#noVNC_fallback_errormsg { - font-weight: normal; -} - -#noVNC_fallback_errormsg .noVNC_message { - display: inline-block; - text-align: left; - font-family: monospace; - white-space: pre-wrap; -} - -#noVNC_fallback_error .noVNC_location { - font-style: italic; - font-size: 0.8em; - color: rgba(255, 255, 255, 0.8); -} - -#noVNC_fallback_error .noVNC_stack { - padding: 10px; - margin: 10px; - font-size: 0.8em; - text-align: left; - font-family: monospace; - white-space: pre; - border: 1px solid rgba(0, 0, 0, 0.5); - background: rgba(0, 0, 0, 0.2); - overflow: auto; -} - -/* ---------------------------------------- - * Control Bar - * ---------------------------------------- - */ - -#noVNC_control_bar_anchor { - /* The anchor is needed to get z-stacking to work */ - position: fixed; - z-index: 10; - - transition: 0.5s ease-in-out; - - /* Edge misrenders animations wihthout this */ - transform: translateX(0); -} -:root.noVNC_connected #noVNC_control_bar_anchor.noVNC_idle { - opacity: 0.8; -} -#noVNC_control_bar_anchor.noVNC_right { - left: auto; - right: 0; -} - -#noVNC_control_bar { - position: relative; - left: -100%; - - transition: 0.5s ease-in-out; - - background-color: rgb(110, 132, 163); - border-radius: 0 10px 10px 0; - - user-select: none; - -webkit-user-select: none; - -webkit-touch-callout: none; /* Disable iOS image long-press popup */ -} -#noVNC_control_bar.noVNC_open { - box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5); - left: 0; -} -#noVNC_control_bar::before { - /* This extra element is to get a proper shadow */ - content: ""; - position: absolute; - z-index: -1; - height: 100%; - width: 30px; - left: -30px; - transition: box-shadow 0.5s ease-in-out; -} -#noVNC_control_bar.noVNC_open::before { - box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5); -} -.noVNC_right #noVNC_control_bar { - left: 100%; - border-radius: 10px 0 0 10px; -} -.noVNC_right #noVNC_control_bar.noVNC_open { - left: 0; -} -.noVNC_right #noVNC_control_bar::before { - visibility: hidden; -} - -#noVNC_control_bar_handle { - position: absolute; - left: -15px; - top: 0; - transform: translateY(35px); - width: calc(100% + 30px); - height: 50px; - z-index: -1; - cursor: pointer; - border-radius: 5px; - background-color: rgb(83, 99, 122); - background-image: url("../images/handle_bg.svg"); - background-repeat: no-repeat; - background-position: right; - box-shadow: 3px 3px 0px rgba(0, 0, 0, 0.5); -} -#noVNC_control_bar_handle:after { - content: ""; - transition: transform 0.5s ease-in-out; - background: url("../images/handle.svg"); - position: absolute; - top: 22px; /* (50px-6px)/2 */ - right: 5px; - width: 5px; - height: 6px; -} -#noVNC_control_bar.noVNC_open #noVNC_control_bar_handle:after { - transform: translateX(1px) rotate(180deg); -} -:root:not(.noVNC_connected) #noVNC_control_bar_handle { - display: none; -} -.noVNC_right #noVNC_control_bar_handle { - background-position: left; -} -.noVNC_right #noVNC_control_bar_handle:after { - left: 5px; - right: 0; - transform: translateX(1px) rotate(180deg); -} -.noVNC_right #noVNC_control_bar.noVNC_open #noVNC_control_bar_handle:after { - transform: none; -} -/* Larger touch area for the handle, used when a touch screen is available */ -#noVNC_control_bar_handle div { - position: absolute; - right: -35px; - top: 0; - width: 50px; - height: 100%; - display: none; -} -@media (any-pointer: coarse) { - #noVNC_control_bar_handle div { - display: initial; - } -} -.noVNC_right #noVNC_control_bar_handle div { - left: -35px; - right: auto; -} - -#noVNC_control_bar > .noVNC_scroll { - max-height: 100vh; /* Chrome is buggy with 100% */ - overflow-x: hidden; - overflow-y: auto; - padding: 0 10px; -} - -#noVNC_control_bar > .noVNC_scroll > * { - display: block; - margin: 10px auto; -} - -/* Control bar hint */ -#noVNC_hint_anchor { - position: fixed; - right: -50px; - left: auto; -} -#noVNC_control_bar_anchor.noVNC_right + #noVNC_hint_anchor { - left: -50px; - right: auto; -} -#noVNC_control_bar_hint { - position: relative; - transform: scale(0); - width: 100px; - height: 50%; - max-height: 600px; - - visibility: hidden; - opacity: 0; - transition: 0.2s ease-in-out; - background: transparent; - box-shadow: 0 0 10px black, inset 0 0 10px 10px rgba(110, 132, 163, 0.8); - border-radius: 10px; - transition-delay: 0s; -} -#noVNC_control_bar_hint.noVNC_active { - visibility: visible; - opacity: 1; - transition-delay: 0.2s; - transform: scale(1); -} -#noVNC_control_bar_hint.noVNC_notransition { - transition: none !important; -} - -/* Control bar buttons */ -#noVNC_control_bar .noVNC_button { - padding: 4px 4px; - vertical-align: middle; - border:1px solid rgba(255, 255, 255, 0.2); - border-radius: 6px; - background-color: transparent; - background-image: unset; /* we don't want the gradiant from input.css */ -} -#noVNC_control_bar .noVNC_button.noVNC_selected { - border-color: rgba(0, 0, 0, 0.8); - background-color: rgba(0, 0, 0, 0.5); -} -#noVNC_control_bar .noVNC_button.noVNC_selected:not(:disabled):hover { - border-color: rgba(0, 0, 0, 0.4); - background-color: rgba(0, 0, 0, 0.2); -} -#noVNC_control_bar .noVNC_button:not(:disabled):hover { - background-color: rgba(255, 255, 255, 0.2); -} -#noVNC_control_bar .noVNC_button:not(:disabled):active { - padding-top: 5px; - padding-bottom: 3px; -} -#noVNC_control_bar .noVNC_button.noVNC_hidden { - display: none !important; -} - -/* Android browsers don't properly update hover state if touch events are - * intercepted, like they are when clicking on the remote screen. */ -@media (any-pointer: coarse) { - #noVNC_control_bar .noVNC_button:not(:disabled):hover { - background-color: transparent; - } - #noVNC_control_bar .noVNC_button.noVNC_selected:not(:disabled):hover { - border-color: rgba(0, 0, 0, 0.8); - background-color: rgba(0, 0, 0, 0.5); - } -} - - -/* Panels */ -.noVNC_panel { - transform: translateX(25px); - - transition: 0.5s ease-in-out; - - box-sizing: border-box; /* so max-width don't have to care about padding */ - max-width: calc(100vw - 75px - 25px); /* minus left and right margins */ - max-height: 100vh; /* Chrome is buggy with 100% */ - overflow-x: hidden; - overflow-y: auto; - - visibility: hidden; - opacity: 0; - - padding: 15px; - - background: #fff; - border-radius: 10px; - color: #000; - border: 2px solid #E0E0E0; - box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5); -} -.noVNC_panel.noVNC_open { - visibility: visible; - opacity: 1; - transform: translateX(75px); -} -.noVNC_right .noVNC_vcenter { - left: auto; - right: 0; -} -.noVNC_right .noVNC_panel { - transform: translateX(-25px); -} -.noVNC_right .noVNC_panel.noVNC_open { - transform: translateX(-75px); -} - -.noVNC_panel > * { - display: block; - margin: 10px auto; -} -.noVNC_panel > *:first-child { - margin-top: 0 !important; -} -.noVNC_panel > *:last-child { - margin-bottom: 0 !important; -} - -.noVNC_panel hr { - border: none; - border-top: 1px solid rgb(192, 192, 192); -} - -.noVNC_panel label { - display: block; - white-space: nowrap; - margin: 5px; -} - -.noVNC_panel li { - margin: 5px; -} - -.noVNC_panel .noVNC_heading { - background-color: rgb(110, 132, 163); - border-radius: 5px; - padding: 5px; - /* Compensate for padding in image */ - padding-right: 8px; - color: white; - font-size: 20px; - white-space: nowrap; -} -.noVNC_panel .noVNC_heading img { - vertical-align: bottom; -} - -.noVNC_submit { - float: right; -} - -/* Expanders */ -.noVNC_expander { - cursor: pointer; -} -.noVNC_expander::before { - content: url("../images/expander.svg"); - display: inline-block; - margin-right: 5px; - transition: 0.2s ease-in-out; -} -.noVNC_expander.noVNC_open::before { - transform: rotateZ(90deg); -} -.noVNC_expander ~ * { - margin: 5px; - margin-left: 10px; - padding: 5px; - background: rgba(0, 0, 0, 0.05); - border-radius: 5px; -} -.noVNC_expander:not(.noVNC_open) ~ * { - display: none; -} - -/* Control bar content */ - -#noVNC_control_bar .noVNC_logo { - font-size: 13px; -} - -.noVNC_logo + hr { - /* Remove all but top border */ - border: none; - border-top: 1px solid rgba(255, 255, 255, 0.2); -} - -:root:not(.noVNC_connected) #noVNC_view_drag_button { - display: none; -} - -/* noVNC Touch Device only buttons */ -:root:not(.noVNC_connected) #noVNC_mobile_buttons { - display: none; -} -@media not all and (any-pointer: coarse) { - /* FIXME: The button for the virtual keyboard is the only button in this - group of "mobile buttons". It is bad to assume that no touch - devices have physical keyboards available. Hopefully we can get - a media query for this: - https://github.com/w3c/csswg-drafts/issues/3871 */ - :root.noVNC_connected #noVNC_mobile_buttons { - display: none; - } -} - -/* Extra manual keys */ -:root:not(.noVNC_connected) #noVNC_toggle_extra_keys_button { - display: none; -} - -#noVNC_modifiers { - background-color: rgb(92, 92, 92); - border: none; - padding: 10px; -} - -/* Shutdown/Reboot */ -:root:not(.noVNC_connected) #noVNC_power_button { - display: none; -} -#noVNC_power { -} -#noVNC_power_buttons { - display: none; -} - -#noVNC_power input[type=button] { - width: 100%; -} - -/* Clipboard */ -:root:not(.noVNC_connected) #noVNC_clipboard_button { - display: none; -} -#noVNC_clipboard_text { - width: 360px; - min-width: 150px; - height: 160px; - min-height: 70px; - - box-sizing: border-box; - max-width: 100%; - /* minus approximate height of title, height of subtitle, and margin */ - max-height: calc(100vh - 10em - 25px); -} - -/* Settings */ -#noVNC_settings { -} -#noVNC_settings ul { - list-style: none; - padding: 0px; -} -#noVNC_setting_port { - width: 80px; -} -#noVNC_setting_path { - width: 100px; -} - -/* Version */ - -.noVNC_version_wrapper { - font-size: small; -} - -.noVNC_version { - margin-left: 1rem; -} - -/* Connection Controls */ -:root:not(.noVNC_connected) #noVNC_disconnect_button { - display: none; -} - -/* ---------------------------------------- - * Status Dialog - * ---------------------------------------- - */ - -#noVNC_status { - position: fixed; - top: 0; - left: 0; - width: 100%; - z-index: 100; - transform: translateY(-100%); - - cursor: pointer; - - transition: 0.5s ease-in-out; - - visibility: hidden; - opacity: 0; - - padding: 5px; - - display: flex; - flex-direction: row; - justify-content: center; - align-content: center; - - line-height: 1.6; - word-wrap: break-word; - color: #fff; - - border-bottom: 1px solid rgba(0, 0, 0, 0.9); -} -#noVNC_status.noVNC_open { - transform: translateY(0); - visibility: visible; - opacity: 1; -} - -#noVNC_status::before { - content: ""; - display: inline-block; - width: 25px; - height: 25px; - margin-right: 5px; -} - -#noVNC_status.noVNC_status_normal { - background: rgba(128,128,128,0.9); -} -#noVNC_status.noVNC_status_normal::before { - content: url("../images/info.svg") " "; -} -#noVNC_status.noVNC_status_error { - background: rgba(200,55,55,0.9); -} -#noVNC_status.noVNC_status_error::before { - content: url("../images/error.svg") " "; -} -#noVNC_status.noVNC_status_warn { - background: rgba(180,180,30,0.9); -} -#noVNC_status.noVNC_status_warn::before { - content: url("../images/warning.svg") " "; -} - -/* ---------------------------------------- - * Connect Dialog - * ---------------------------------------- - */ - -#noVNC_connect_dlg { - transition: 0.5s ease-in-out; - - transform: scale(0, 0); - visibility: hidden; - opacity: 0; -} -#noVNC_connect_dlg.noVNC_open { - transform: scale(1, 1); - visibility: visible; - opacity: 1; -} -#noVNC_connect_dlg .noVNC_logo { - transition: 0.5s ease-in-out; - padding: 10px; - margin-bottom: 10px; - - font-size: 80px; - text-align: center; - - border-radius: 5px; -} -@media (max-width: 440px) { - #noVNC_connect_dlg { - max-width: calc(100vw - 100px); - } - #noVNC_connect_dlg .noVNC_logo { - font-size: calc(25vw - 30px); - } -} -#noVNC_connect_dlg div { - padding: 12px; - - background-color: rgb(110, 132, 163); - border-radius: 12px; - text-align: center; - font-size: 20px; - - box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5); -} -#noVNC_connect_button { - width: 100%; - padding: 5px 30px; - - cursor: pointer; - - border-color: rgb(83, 99, 122); - border-radius: 5px; - - background: linear-gradient(to top, rgb(110, 132, 163), rgb(99, 119, 147)); - color: white; - - /* This avoids it jumping around when :active */ - vertical-align: middle; -} -#noVNC_connect_button:hover { - background: linear-gradient(to top, rgb(110, 132, 163), rgb(105, 125, 155)); -} - -#noVNC_connect_button img { - vertical-align: bottom; - height: 1.3em; -} - -/* ---------------------------------------- - * Server verification Dialog - * ---------------------------------------- - */ - -#noVNC_verify_server_dlg { - position: relative; - - transform: translateY(-50px); -} -#noVNC_verify_server_dlg.noVNC_open { - transform: translateY(0); -} -#noVNC_fingerprint_block { - margin: 10px; -} - -/* ---------------------------------------- - * Password Dialog - * ---------------------------------------- - */ - -#noVNC_credentials_dlg { - position: relative; - - transform: translateY(-50px); -} -#noVNC_credentials_dlg.noVNC_open { - transform: translateY(0); -} -#noVNC_username_block.noVNC_hidden, -#noVNC_password_block.noVNC_hidden { - display: none; -} - - -/* ---------------------------------------- - * Main Area - * ---------------------------------------- - */ - -/* Transition screen */ -#noVNC_transition { - transition: 0.5s ease-in-out; - - display: flex; - opacity: 0; - visibility: hidden; - - position: fixed; - top: 0; - left: 0; - bottom: 0; - right: 0; - - color: white; - background: rgba(0, 0, 0, 0.5); - z-index: 50; - - /*display: flex;*/ - align-items: center; - justify-content: center; - flex-direction: column; -} -:root.noVNC_loading #noVNC_transition, -:root.noVNC_connecting #noVNC_transition, -:root.noVNC_disconnecting #noVNC_transition, -:root.noVNC_reconnecting #noVNC_transition { - opacity: 1; - visibility: visible; -} -:root:not(.noVNC_reconnecting) #noVNC_cancel_reconnect_button { - display: none; -} -#noVNC_transition_text { - font-size: 1.5em; -} - -/* Main container */ -#noVNC_container { - width: 100%; - height: 100%; - background-color: #313131; - border-bottom-right-radius: 800px 600px; - /*border-top-left-radius: 800px 600px;*/ - - /* If selection isn't disabled, long-pressing stuff in the sidebar - can accidentally select the container or the canvas. This can - happen when attempting to move the handle. */ - user-select: none; - -webkit-user-select: none; -} - -#noVNC_keyboardinput { - width: 1px; - height: 1px; - background-color: #fff; - color: #fff; - border: 0; - position: absolute; - left: -40px; - z-index: -1; - ime-mode: disabled; -} - -/*Default noVNC logo.*/ -/* From: http://fonts.googleapis.com/css?family=Orbitron:700 */ -@font-face { - font-family: 'Orbitron'; - font-style: normal; - font-weight: 700; - src: local('?'), url('Orbitron700.woff') format('woff'), - url('Orbitron700.ttf') format('truetype'); -} - -.noVNC_logo { - color:yellow; - font-family: 'Orbitron', 'OrbitronTTF', sans-serif; - line-height: 0.9; - text-shadow: 0.1em 0.1em 0 black; -} -.noVNC_logo span{ - color:green; -} - -#noVNC_bell { - display: none; -} - -/* ---------------------------------------- - * Media sizing - * ---------------------------------------- - */ - -@media screen and (max-width: 640px){ - #noVNC_logo { - font-size: 150px; - } -} - -@media screen and (min-width: 321px) and (max-width: 480px) { - #noVNC_logo { - font-size: 110px; - } -} - -@media screen and (max-width: 320px) { - #noVNC_logo { - font-size: 90px; - } -} diff --git a/base/app/novnc/app/styles/input.css b/base/app/novnc/app/styles/input.css deleted file mode 100644 index dc345aa..0000000 --- a/base/app/novnc/app/styles/input.css +++ /dev/null @@ -1,281 +0,0 @@ -/* - * noVNC general input element CSS - * Copyright (C) 2022 The noVNC Authors - * noVNC is licensed under the MPL 2.0 (see LICENSE.txt) - * This file is licensed under the 2-Clause BSD license (see LICENSE.txt). - */ - -/* - * Common for all inputs - */ -input, input::file-selector-button, button, select, textarea { - /* Respect standard font settings */ - font: inherit; - - /* Disable default rendering */ - appearance: none; - background: none; - - padding: 5px; - border: 1px solid rgb(192, 192, 192); - border-radius: 5px; - color: black; - --bg-gradient: linear-gradient(to top, rgb(255, 255, 255) 80%, rgb(240, 240, 240)); - background-image: var(--bg-gradient); -} - -/* - * Buttons - */ -input[type=button], -input[type=color], -input[type=image], -input[type=reset], -input[type=submit], -input::file-selector-button, -button, -select { - border-bottom-width: 2px; - - /* This avoids it jumping around when :active */ - vertical-align: middle; - margin-top: 0; - - padding-left: 20px; - padding-right: 20px; - - /* Disable Chrome's touch tap highlight */ - -webkit-tap-highlight-color: transparent; -} - -/* - * Select dropdowns - */ -select { - --select-arrow: url('data:image/svg+xml;utf8, \ - <svg width="8" height="6" version="1.1" viewBox="0 0 8 6" \ - xmlns="http://www.w3.org/2000/svg"> \ - <path d="m6.5 1.5 -2.5 3 -2.5 -3 5 0" stroke-width="3" \ - stroke="rgb(31,31,31)" fill="none" \ - stroke-linecap="round" stroke-linejoin="round" /> \ - </svg>'); - background-image: var(--select-arrow), var(--bg-gradient); - background-position: calc(100% - 7px), left top; - background-repeat: no-repeat; - padding-right: calc(2*7px + 8px); - padding-left: 7px; -} -/* FIXME: :active isn't set when the <select> is opened in Firefox: - https://bugzilla.mozilla.org/show_bug.cgi?id=1805406 */ -select:active { - /* Rotated arrow */ - background-image: url('data:image/svg+xml;utf8, \ - <svg width="8" height="6" version="1.1" viewBox="0 0 8 6" \ - xmlns="http://www.w3.org/2000/svg" transform="rotate(180)" > \ - <path d="m6.5 1.5 -2.5 3 -2.5 -3 5 0" stroke-width="3" \ - stroke="rgb(31,31,31)" fill="none" \ - stroke-linecap="round" stroke-linejoin="round" /> \ - </svg>'), var(--bg-gradient); -} -option { - color: black; - background: white; -} - -/* - * Checkboxes - */ -input[type=checkbox] { - display: inline-flex; - justify-content: center; - align-items: center; - background-color: white; - background-image: unset; - border: 1px solid dimgrey; - border-radius: 3px; - width: 13px; - height: 13px; - padding: 0; - margin-right: 6px; - vertical-align: bottom; - transition: 0.2s background-color linear; -} -input[type=checkbox]:checked { - background-color: rgb(110, 132, 163); - border-color: rgb(110, 132, 163); -} -input[type=checkbox]:checked::after { - content: ""; - display: block; /* width & height doesn't work on inline elements */ - width: 3px; - height: 7px; - border: 1px solid white; - border-width: 0 2px 2px 0; - transform: rotate(40deg) translateY(-1px); -} - -/* - * Radiobuttons - */ -input[type=radio] { - border-radius: 50%; - border: 1px solid dimgrey; - width: 12px; - height: 12px; - padding: 0; - margin-right: 6px; - transition: 0.2s border linear; -} -input[type=radio]:checked { - border: 6px solid rgb(110, 132, 163); -} - -/* - * Range sliders - */ -input[type=range] { - border: unset; - border-radius: 3px; - height: 20px; - padding: 0; - background: transparent; -} -/* -webkit-slider.. & -moz-range.. cant be in selector lists: - https://bugs.chromium.org/p/chromium/issues/detail?id=1154623 */ -input[type=range]::-webkit-slider-runnable-track { - background-color: rgb(110, 132, 163); - height: 6px; - border-radius: 3px; -} -input[type=range]::-moz-range-track { - background-color: rgb(110, 132, 163); - height: 6px; - border-radius: 3px; -} -input[type=range]::-webkit-slider-thumb { - appearance: none; - width: 18px; - height: 20px; - border-radius: 5px; - background-color: white; - border: 1px solid dimgray; - margin-top: -7px; -} -input[type=range]::-moz-range-thumb { - appearance: none; - width: 18px; - height: 20px; - border-radius: 5px; - background-color: white; - border: 1px solid dimgray; - margin-top: -7px; -} - -/* - * File choosers - */ -input[type=file] { - background-image: none; - border: none; -} -input::file-selector-button { - margin-right: 6px; -} - -/* - * Hover - */ -input[type=button]:hover, -input[type=color]:hover, -input[type=image]:hover, -input[type=reset]:hover, -input[type=submit]:hover, -input::file-selector-button:hover, -button:hover { - background-image: linear-gradient(to top, rgb(255, 255, 255), rgb(250, 250, 250)); -} -select:hover { - background-image: var(--select-arrow), - linear-gradient(to top, rgb(255, 255, 255), rgb(250, 250, 250)); - background-position: calc(100% - 7px), left top; - background-repeat: no-repeat; -} -@media (any-pointer: coarse) { - /* We don't want a hover style after touch input */ - input[type=button]:hover, - input[type=color]:hover, - input[type=image]:hover, - input[type=reset]:hover, - input[type=submit]:hover, - input::file-selector-button:hover, - button:hover { - background-image: var(--bg-gradient); - } - select:hover { - background-image: var(--select-arrow), var(--bg-gradient); - } -} - -/* - * Active (clicked) - */ -input[type=button]:active, -input[type=color]:active, -input[type=image]:active, -input[type=reset]:active, -input[type=submit]:active, -input::file-selector-button:active, -button:active, -select:active { - border-bottom-width: 1px; - margin-top: 1px; -} - -/* - * Focus (tab) - */ -input:focus-visible, -input:focus-visible::file-selector-button, -button:focus-visible, -select:focus-visible, -textarea:focus-visible { - outline: 2px solid rgb(74, 144, 217); - outline-offset: 1px; -} -input[type=file]:focus-visible { - outline: none; /* We outline the button instead of the entire element */ -} - -/* - * Disabled - */ -input:disabled, -input:disabled::file-selector-button, -button:disabled, -select:disabled, -textarea:disabled { - opacity: 0.4; -} -input[type=button]:disabled, -input[type=color]:disabled, -input[type=image]:disabled, -input[type=reset]:disabled, -input[type=submit]:disabled, -input:disabled::file-selector-button, -button:disabled, -select:disabled { - background-image: var(--bg-gradient); - border-bottom-width: 2px; - margin-top: 0; -} -input[type=file]:disabled { - background-image: none; -} -select:disabled { - background-image: var(--select-arrow), var(--bg-gradient); -} -input[type=image]:disabled { - /* See Firefox bug: - https://bugzilla.mozilla.org/show_bug.cgi?id=1798304 */ - cursor: default; -} diff --git a/base/app/novnc/app/ui.js b/base/app/novnc/app/ui.js deleted file mode 100644 index f27dfe2..0000000 --- a/base/app/novnc/app/ui.js +++ /dev/null @@ -1,1778 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2019 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - */ - -import * as Log from '../core/util/logging.js'; -import _, { l10n } from './localization.js'; -import { isTouchDevice, isMac, isIOS, isAndroid, isChromeOS, isSafari, - hasScrollbarGutter, dragThreshold } - from '../core/util/browser.js'; -import { setCapture, getPointerEvent } from '../core/util/events.js'; -import KeyTable from "../core/input/keysym.js"; -import keysyms from "../core/input/keysymdef.js"; -import Keyboard from "../core/input/keyboard.js"; -import RFB from "../core/rfb.js"; -import * as WebUtil from "./webutil.js"; - -const PAGE_TITLE = "noVNC"; - -const UI = { - - connected: false, - desktopName: "", - - statusTimeout: null, - hideKeyboardTimeout: null, - idleControlbarTimeout: null, - closeControlbarTimeout: null, - - controlbarGrabbed: false, - controlbarDrag: false, - controlbarMouseDownClientY: 0, - controlbarMouseDownOffsetY: 0, - - lastKeyboardinput: null, - defaultKeyboardinputLen: 100, - - inhibitReconnect: true, - reconnectCallback: null, - reconnectPassword: null, - - prime() { - return WebUtil.initSettings().then(() => { - if (document.readyState === "interactive" || document.readyState === "complete") { - return UI.start(); - } - - return new Promise((resolve, reject) => { - document.addEventListener('DOMContentLoaded', () => UI.start().then(resolve).catch(reject)); - }); - }); - }, - - // Render default UI and initialize settings menu - start() { - - UI.initSettings(); - - // Translate the DOM - l10n.translateDOM(); - - // We rely on modern APIs which might not be available in an - // insecure context - if (!window.isSecureContext) { - // FIXME: This gets hidden when connecting - UI.showStatus(_("Running without HTTPS is not recommended, crashes or other issues are likely."), 'error'); - } - - // Try to fetch version number - fetch('./package.json') - .then((response) => { - if (!response.ok) { - throw Error("" + response.status + " " + response.statusText); - } - return response.json(); - }) - .then((packageInfo) => { - Array.from(document.getElementsByClassName('noVNC_version')).forEach(el => el.innerText = packageInfo.version); - }) - .catch((err) => { - Log.Error("Couldn't fetch package.json: " + err); - Array.from(document.getElementsByClassName('noVNC_version_wrapper')) - .concat(Array.from(document.getElementsByClassName('noVNC_version_separator'))) - .forEach(el => el.style.display = 'none'); - }); - - // Adapt the interface for touch screen devices - if (isTouchDevice) { - // Remove the address bar - setTimeout(() => window.scrollTo(0, 1), 100); - } - - // Restore control bar position - if (WebUtil.readSetting('controlbar_pos') === 'right') { - UI.toggleControlbarSide(); - } - - UI.initFullscreen(); - - // Setup event handlers - UI.addControlbarHandlers(); - UI.addTouchSpecificHandlers(); - UI.addExtraKeysHandlers(); - UI.addMachineHandlers(); - UI.addConnectionControlHandlers(); - UI.addClipboardHandlers(); - UI.addSettingsHandlers(); - document.getElementById("noVNC_status") - .addEventListener('click', UI.hideStatus); - - // Bootstrap fallback input handler - UI.keyboardinputReset(); - - UI.openControlbar(); - - UI.updateVisualState('init'); - - document.documentElement.classList.remove("noVNC_loading"); - - let autoconnect = WebUtil.getConfigVar('autoconnect', false); - if (autoconnect === 'true' || autoconnect == '1') { - autoconnect = true; - UI.connect(); - } else { - autoconnect = false; - // Show the connect panel on first load unless autoconnecting - UI.openConnectPanel(); - } - - return Promise.resolve(UI.rfb); - }, - - initFullscreen() { - // Only show the button if fullscreen is properly supported - // * Safari doesn't support alphanumerical input while in fullscreen - if (!isSafari() && - (document.documentElement.requestFullscreen || - document.documentElement.mozRequestFullScreen || - document.documentElement.webkitRequestFullscreen || - document.body.msRequestFullscreen)) { - document.getElementById('noVNC_fullscreen_button') - .classList.remove("noVNC_hidden"); - UI.addFullscreenHandlers(); - } - }, - - initSettings() { - // Logging selection dropdown - const llevels = ['error', 'warn', 'info', 'debug']; - for (let i = 0; i < llevels.length; i += 1) { - UI.addOption(document.getElementById('noVNC_setting_logging'), llevels[i], llevels[i]); - } - - // Settings with immediate effects - UI.initSetting('logging', 'warn'); - UI.updateLogging(); - - // if port == 80 (or 443) then it won't be present and should be - // set manually - let port = window.location.port; - if (!port) { - if (window.location.protocol.substring(0, 5) == 'https') { - port = 443; - } else if (window.location.protocol.substring(0, 4) == 'http') { - port = 80; - } - } - - /* Populate the controls if defaults are provided in the URL */ - UI.initSetting('host', window.location.hostname); - UI.initSetting('port', port); - UI.initSetting('encrypt', (window.location.protocol === "https:")); - UI.initSetting('view_clip', false); - UI.initSetting('resize', 'off'); - UI.initSetting('quality', 6); - UI.initSetting('compression', 2); - UI.initSetting('shared', true); - UI.initSetting('view_only', false); - UI.initSetting('show_dot', false); - UI.initSetting('path', 'websockify'); - UI.initSetting('repeaterID', ''); - UI.initSetting('reconnect', false); - UI.initSetting('reconnect_delay', 5000); - - UI.setupSettingLabels(); - }, - // Adds a link to the label elements on the corresponding input elements - setupSettingLabels() { - const labels = document.getElementsByTagName('LABEL'); - for (let i = 0; i < labels.length; i++) { - const htmlFor = labels[i].htmlFor; - if (htmlFor != '') { - const elem = document.getElementById(htmlFor); - if (elem) elem.label = labels[i]; - } else { - // If 'for' isn't set, use the first input element child - const children = labels[i].children; - for (let j = 0; j < children.length; j++) { - if (children[j].form !== undefined) { - children[j].label = labels[i]; - break; - } - } - } - } - }, - -/* ------^------- -* /INIT -* ============== -* EVENT HANDLERS -* ------v------*/ - - addControlbarHandlers() { - document.getElementById("noVNC_control_bar") - .addEventListener('mousemove', UI.activateControlbar); - document.getElementById("noVNC_control_bar") - .addEventListener('mouseup', UI.activateControlbar); - document.getElementById("noVNC_control_bar") - .addEventListener('mousedown', UI.activateControlbar); - document.getElementById("noVNC_control_bar") - .addEventListener('keydown', UI.activateControlbar); - - document.getElementById("noVNC_control_bar") - .addEventListener('mousedown', UI.keepControlbar); - document.getElementById("noVNC_control_bar") - .addEventListener('keydown', UI.keepControlbar); - - document.getElementById("noVNC_view_drag_button") - .addEventListener('click', UI.toggleViewDrag); - - document.getElementById("noVNC_control_bar_handle") - .addEventListener('mousedown', UI.controlbarHandleMouseDown); - document.getElementById("noVNC_control_bar_handle") - .addEventListener('mouseup', UI.controlbarHandleMouseUp); - document.getElementById("noVNC_control_bar_handle") - .addEventListener('mousemove', UI.dragControlbarHandle); - // resize events aren't available for elements - window.addEventListener('resize', UI.updateControlbarHandle); - - const exps = document.getElementsByClassName("noVNC_expander"); - for (let i = 0;i < exps.length;i++) { - exps[i].addEventListener('click', UI.toggleExpander); - } - }, - - addTouchSpecificHandlers() { - document.getElementById("noVNC_keyboard_button") - .addEventListener('click', UI.toggleVirtualKeyboard); - - UI.touchKeyboard = new Keyboard(document.getElementById('noVNC_keyboardinput')); - UI.touchKeyboard.onkeyevent = UI.keyEvent; - UI.touchKeyboard.grab(); - document.getElementById("noVNC_keyboardinput") - .addEventListener('input', UI.keyInput); - document.getElementById("noVNC_keyboardinput") - .addEventListener('focus', UI.onfocusVirtualKeyboard); - document.getElementById("noVNC_keyboardinput") - .addEventListener('blur', UI.onblurVirtualKeyboard); - document.getElementById("noVNC_keyboardinput") - .addEventListener('submit', () => false); - - document.documentElement - .addEventListener('mousedown', UI.keepVirtualKeyboard, true); - - document.getElementById("noVNC_control_bar") - .addEventListener('touchstart', UI.activateControlbar); - document.getElementById("noVNC_control_bar") - .addEventListener('touchmove', UI.activateControlbar); - document.getElementById("noVNC_control_bar") - .addEventListener('touchend', UI.activateControlbar); - document.getElementById("noVNC_control_bar") - .addEventListener('input', UI.activateControlbar); - - document.getElementById("noVNC_control_bar") - .addEventListener('touchstart', UI.keepControlbar); - document.getElementById("noVNC_control_bar") - .addEventListener('input', UI.keepControlbar); - - document.getElementById("noVNC_control_bar_handle") - .addEventListener('touchstart', UI.controlbarHandleMouseDown); - document.getElementById("noVNC_control_bar_handle") - .addEventListener('touchend', UI.controlbarHandleMouseUp); - document.getElementById("noVNC_control_bar_handle") - .addEventListener('touchmove', UI.dragControlbarHandle); - }, - - addExtraKeysHandlers() { - document.getElementById("noVNC_toggle_extra_keys_button") - .addEventListener('click', UI.toggleExtraKeys); - document.getElementById("noVNC_toggle_ctrl_button") - .addEventListener('click', UI.toggleCtrl); - document.getElementById("noVNC_toggle_windows_button") - .addEventListener('click', UI.toggleWindows); - document.getElementById("noVNC_toggle_alt_button") - .addEventListener('click', UI.toggleAlt); - document.getElementById("noVNC_send_tab_button") - .addEventListener('click', UI.sendTab); - document.getElementById("noVNC_send_esc_button") - .addEventListener('click', UI.sendEsc); - document.getElementById("noVNC_send_ctrl_alt_del_button") - .addEventListener('click', UI.sendCtrlAltDel); - }, - - addMachineHandlers() { - document.getElementById("noVNC_shutdown_button") - .addEventListener('click', () => UI.rfb.machineShutdown()); - document.getElementById("noVNC_reboot_button") - .addEventListener('click', () => UI.rfb.machineReboot()); - document.getElementById("noVNC_reset_button") - .addEventListener('click', () => UI.rfb.machineReset()); - document.getElementById("noVNC_power_button") - .addEventListener('click', UI.togglePowerPanel); - }, - - addConnectionControlHandlers() { - document.getElementById("noVNC_disconnect_button") - .addEventListener('click', UI.disconnect); - document.getElementById("noVNC_connect_button") - .addEventListener('click', UI.connect); - document.getElementById("noVNC_cancel_reconnect_button") - .addEventListener('click', UI.cancelReconnect); - - document.getElementById("noVNC_approve_server_button") - .addEventListener('click', UI.approveServer); - document.getElementById("noVNC_reject_server_button") - .addEventListener('click', UI.rejectServer); - document.getElementById("noVNC_credentials_button") - .addEventListener('click', UI.setCredentials); - }, - - addClipboardHandlers() { - document.getElementById("noVNC_clipboard_button") - .addEventListener('click', UI.toggleClipboardPanel); - document.getElementById("noVNC_clipboard_text") - .addEventListener('change', UI.clipboardSend); - }, - - // Add a call to save settings when the element changes, - // unless the optional parameter changeFunc is used instead. - addSettingChangeHandler(name, changeFunc) { - const settingElem = document.getElementById("noVNC_setting_" + name); - if (changeFunc === undefined) { - changeFunc = () => UI.saveSetting(name); - } - settingElem.addEventListener('change', changeFunc); - }, - - addSettingsHandlers() { - document.getElementById("noVNC_settings_button") - .addEventListener('click', UI.toggleSettingsPanel); - - UI.addSettingChangeHandler('encrypt'); - UI.addSettingChangeHandler('resize'); - UI.addSettingChangeHandler('resize', UI.applyResizeMode); - UI.addSettingChangeHandler('resize', UI.updateViewClip); - UI.addSettingChangeHandler('quality'); - UI.addSettingChangeHandler('quality', UI.updateQuality); - UI.addSettingChangeHandler('compression'); - UI.addSettingChangeHandler('compression', UI.updateCompression); - UI.addSettingChangeHandler('view_clip'); - UI.addSettingChangeHandler('view_clip', UI.updateViewClip); - UI.addSettingChangeHandler('shared'); - UI.addSettingChangeHandler('view_only'); - UI.addSettingChangeHandler('view_only', UI.updateViewOnly); - UI.addSettingChangeHandler('show_dot'); - UI.addSettingChangeHandler('show_dot', UI.updateShowDotCursor); - UI.addSettingChangeHandler('host'); - UI.addSettingChangeHandler('port'); - UI.addSettingChangeHandler('path'); - UI.addSettingChangeHandler('repeaterID'); - UI.addSettingChangeHandler('logging'); - UI.addSettingChangeHandler('logging', UI.updateLogging); - UI.addSettingChangeHandler('reconnect'); - UI.addSettingChangeHandler('reconnect_delay'); - }, - - addFullscreenHandlers() { - document.getElementById("noVNC_fullscreen_button") - .addEventListener('click', UI.toggleFullscreen); - - window.addEventListener('fullscreenchange', UI.updateFullscreenButton); - window.addEventListener('mozfullscreenchange', UI.updateFullscreenButton); - window.addEventListener('webkitfullscreenchange', UI.updateFullscreenButton); - window.addEventListener('msfullscreenchange', UI.updateFullscreenButton); - }, - -/* ------^------- - * /EVENT HANDLERS - * ============== - * VISUAL - * ------v------*/ - - // Disable/enable controls depending on connection state - updateVisualState(state) { - - document.documentElement.classList.remove("noVNC_connecting"); - document.documentElement.classList.remove("noVNC_connected"); - document.documentElement.classList.remove("noVNC_disconnecting"); - document.documentElement.classList.remove("noVNC_reconnecting"); - - const transitionElem = document.getElementById("noVNC_transition_text"); - switch (state) { - case 'init': - break; - case 'connecting': - transitionElem.textContent = _("Connecting..."); - document.documentElement.classList.add("noVNC_connecting"); - break; - case 'connected': - document.documentElement.classList.add("noVNC_connected"); - break; - case 'disconnecting': - transitionElem.textContent = _("Disconnecting..."); - document.documentElement.classList.add("noVNC_disconnecting"); - break; - case 'disconnected': - break; - case 'reconnecting': - transitionElem.textContent = _("Reconnecting..."); - document.documentElement.classList.add("noVNC_reconnecting"); - break; - default: - Log.Error("Invalid visual state: " + state); - UI.showStatus(_("Internal error"), 'error'); - return; - } - - if (UI.connected) { - UI.updateViewClip(); - - UI.disableSetting('encrypt'); - UI.disableSetting('shared'); - UI.disableSetting('host'); - UI.disableSetting('port'); - UI.disableSetting('path'); - UI.disableSetting('repeaterID'); - - // Hide the controlbar after 2 seconds - UI.closeControlbarTimeout = setTimeout(UI.closeControlbar, 2000); - } else { - UI.enableSetting('encrypt'); - UI.enableSetting('shared'); - UI.enableSetting('host'); - UI.enableSetting('port'); - UI.enableSetting('path'); - UI.enableSetting('repeaterID'); - UI.updatePowerButton(); - UI.keepControlbar(); - } - - // State change closes dialogs as they may not be relevant - // anymore - UI.closeAllPanels(); - document.getElementById('noVNC_verify_server_dlg') - .classList.remove('noVNC_open'); - document.getElementById('noVNC_credentials_dlg') - .classList.remove('noVNC_open'); - }, - - showStatus(text, statusType, time) { - const statusElem = document.getElementById('noVNC_status'); - - if (typeof statusType === 'undefined') { - statusType = 'normal'; - } - - // Don't overwrite more severe visible statuses and never - // errors. Only shows the first error. - if (statusElem.classList.contains("noVNC_open")) { - if (statusElem.classList.contains("noVNC_status_error")) { - return; - } - if (statusElem.classList.contains("noVNC_status_warn") && - statusType === 'normal') { - return; - } - } - - clearTimeout(UI.statusTimeout); - - switch (statusType) { - case 'error': - statusElem.classList.remove("noVNC_status_warn"); - statusElem.classList.remove("noVNC_status_normal"); - statusElem.classList.add("noVNC_status_error"); - break; - case 'warning': - case 'warn': - statusElem.classList.remove("noVNC_status_error"); - statusElem.classList.remove("noVNC_status_normal"); - statusElem.classList.add("noVNC_status_warn"); - break; - case 'normal': - case 'info': - default: - statusElem.classList.remove("noVNC_status_error"); - statusElem.classList.remove("noVNC_status_warn"); - statusElem.classList.add("noVNC_status_normal"); - break; - } - - statusElem.textContent = text; - statusElem.classList.add("noVNC_open"); - - // If no time was specified, show the status for 1.5 seconds - if (typeof time === 'undefined') { - time = 1500; - } - - // Error messages do not timeout - if (statusType !== 'error') { - UI.statusTimeout = window.setTimeout(UI.hideStatus, time); - } - }, - - hideStatus() { - clearTimeout(UI.statusTimeout); - document.getElementById('noVNC_status').classList.remove("noVNC_open"); - }, - - activateControlbar(event) { - clearTimeout(UI.idleControlbarTimeout); - // We manipulate the anchor instead of the actual control - // bar in order to avoid creating new a stacking group - document.getElementById('noVNC_control_bar_anchor') - .classList.remove("noVNC_idle"); - UI.idleControlbarTimeout = window.setTimeout(UI.idleControlbar, 2000); - }, - - idleControlbar() { - // Don't fade if a child of the control bar has focus - if (document.getElementById('noVNC_control_bar') - .contains(document.activeElement) && document.hasFocus()) { - UI.activateControlbar(); - return; - } - - document.getElementById('noVNC_control_bar_anchor') - .classList.add("noVNC_idle"); - }, - - keepControlbar() { - clearTimeout(UI.closeControlbarTimeout); - }, - - openControlbar() { - document.getElementById('noVNC_control_bar') - .classList.add("noVNC_open"); - }, - - closeControlbar() { - UI.closeAllPanels(); - document.getElementById('noVNC_control_bar') - .classList.remove("noVNC_open"); - UI.rfb.focus(); - }, - - toggleControlbar() { - if (document.getElementById('noVNC_control_bar') - .classList.contains("noVNC_open")) { - UI.closeControlbar(); - } else { - UI.openControlbar(); - } - }, - - toggleControlbarSide() { - // Temporarily disable animation, if bar is displayed, to avoid weird - // movement. The transitionend-event will not fire when display=none. - const bar = document.getElementById('noVNC_control_bar'); - const barDisplayStyle = window.getComputedStyle(bar).display; - if (barDisplayStyle !== 'none') { - bar.style.transitionDuration = '0s'; - bar.addEventListener('transitionend', () => bar.style.transitionDuration = ''); - } - - const anchor = document.getElementById('noVNC_control_bar_anchor'); - if (anchor.classList.contains("noVNC_right")) { - WebUtil.writeSetting('controlbar_pos', 'left'); - anchor.classList.remove("noVNC_right"); - } else { - WebUtil.writeSetting('controlbar_pos', 'right'); - anchor.classList.add("noVNC_right"); - } - - // Consider this a movement of the handle - UI.controlbarDrag = true; - - // The user has "followed" hint, let's hide it until the next drag - UI.showControlbarHint(false, false); - }, - - showControlbarHint(show, animate=true) { - const hint = document.getElementById('noVNC_control_bar_hint'); - - if (animate) { - hint.classList.remove("noVNC_notransition"); - } else { - hint.classList.add("noVNC_notransition"); - } - - if (show) { - hint.classList.add("noVNC_active"); - } else { - hint.classList.remove("noVNC_active"); - } - }, - - dragControlbarHandle(e) { - if (!UI.controlbarGrabbed) return; - - const ptr = getPointerEvent(e); - - const anchor = document.getElementById('noVNC_control_bar_anchor'); - if (ptr.clientX < (window.innerWidth * 0.1)) { - if (anchor.classList.contains("noVNC_right")) { - UI.toggleControlbarSide(); - } - } else if (ptr.clientX > (window.innerWidth * 0.9)) { - if (!anchor.classList.contains("noVNC_right")) { - UI.toggleControlbarSide(); - } - } - - if (!UI.controlbarDrag) { - const dragDistance = Math.abs(ptr.clientY - UI.controlbarMouseDownClientY); - - if (dragDistance < dragThreshold) return; - - UI.controlbarDrag = true; - } - - const eventY = ptr.clientY - UI.controlbarMouseDownOffsetY; - - UI.moveControlbarHandle(eventY); - - e.preventDefault(); - e.stopPropagation(); - UI.keepControlbar(); - UI.activateControlbar(); - }, - - // Move the handle but don't allow any position outside the bounds - moveControlbarHandle(viewportRelativeY) { - const handle = document.getElementById("noVNC_control_bar_handle"); - const handleHeight = handle.getBoundingClientRect().height; - const controlbarBounds = document.getElementById("noVNC_control_bar") - .getBoundingClientRect(); - const margin = 10; - - // These heights need to be non-zero for the below logic to work - if (handleHeight === 0 || controlbarBounds.height === 0) { - return; - } - - let newY = viewportRelativeY; - - // Check if the coordinates are outside the control bar - if (newY < controlbarBounds.top + margin) { - // Force coordinates to be below the top of the control bar - newY = controlbarBounds.top + margin; - - } else if (newY > controlbarBounds.top + - controlbarBounds.height - handleHeight - margin) { - // Force coordinates to be above the bottom of the control bar - newY = controlbarBounds.top + - controlbarBounds.height - handleHeight - margin; - } - - // Corner case: control bar too small for stable position - if (controlbarBounds.height < (handleHeight + margin * 2)) { - newY = controlbarBounds.top + - (controlbarBounds.height - handleHeight) / 2; - } - - // The transform needs coordinates that are relative to the parent - const parentRelativeY = newY - controlbarBounds.top; - handle.style.transform = "translateY(" + parentRelativeY + "px)"; - }, - - updateControlbarHandle() { - // Since the control bar is fixed on the viewport and not the page, - // the move function expects coordinates relative the the viewport. - const handle = document.getElementById("noVNC_control_bar_handle"); - const handleBounds = handle.getBoundingClientRect(); - UI.moveControlbarHandle(handleBounds.top); - }, - - controlbarHandleMouseUp(e) { - if ((e.type == "mouseup") && (e.button != 0)) return; - - // mouseup and mousedown on the same place toggles the controlbar - if (UI.controlbarGrabbed && !UI.controlbarDrag) { - UI.toggleControlbar(); - e.preventDefault(); - e.stopPropagation(); - UI.keepControlbar(); - UI.activateControlbar(); - } - UI.controlbarGrabbed = false; - UI.showControlbarHint(false); - }, - - controlbarHandleMouseDown(e) { - if ((e.type == "mousedown") && (e.button != 0)) return; - - const ptr = getPointerEvent(e); - - const handle = document.getElementById("noVNC_control_bar_handle"); - const bounds = handle.getBoundingClientRect(); - - // Touch events have implicit capture - if (e.type === "mousedown") { - setCapture(handle); - } - - UI.controlbarGrabbed = true; - UI.controlbarDrag = false; - - UI.showControlbarHint(true); - - UI.controlbarMouseDownClientY = ptr.clientY; - UI.controlbarMouseDownOffsetY = ptr.clientY - bounds.top; - e.preventDefault(); - e.stopPropagation(); - UI.keepControlbar(); - UI.activateControlbar(); - }, - - toggleExpander(e) { - if (this.classList.contains("noVNC_open")) { - this.classList.remove("noVNC_open"); - } else { - this.classList.add("noVNC_open"); - } - }, - -/* ------^------- - * /VISUAL - * ============== - * SETTINGS - * ------v------*/ - - // Initial page load read/initialization of settings - initSetting(name, defVal) { - // Check Query string followed by cookie - let val = WebUtil.getConfigVar(name); - if (val === null) { - val = WebUtil.readSetting(name, defVal); - } - WebUtil.setSetting(name, val); - UI.updateSetting(name); - return val; - }, - - // Set the new value, update and disable form control setting - forceSetting(name, val) { - WebUtil.setSetting(name, val); - UI.updateSetting(name); - UI.disableSetting(name); - }, - - // Update cookie and form control setting. If value is not set, then - // updates from control to current cookie setting. - updateSetting(name) { - - // Update the settings control - let value = UI.getSetting(name); - - const ctrl = document.getElementById('noVNC_setting_' + name); - if (ctrl.type === 'checkbox') { - ctrl.checked = value; - - } else if (typeof ctrl.options !== 'undefined') { - for (let i = 0; i < ctrl.options.length; i += 1) { - if (ctrl.options[i].value === value) { - ctrl.selectedIndex = i; - break; - } - } - } else { - ctrl.value = value; - } - }, - - // Save control setting to cookie - saveSetting(name) { - const ctrl = document.getElementById('noVNC_setting_' + name); - let val; - if (ctrl.type === 'checkbox') { - val = ctrl.checked; - } else if (typeof ctrl.options !== 'undefined') { - val = ctrl.options[ctrl.selectedIndex].value; - } else { - val = ctrl.value; - } - WebUtil.writeSetting(name, val); - //Log.Debug("Setting saved '" + name + "=" + val + "'"); - return val; - }, - - // Read form control compatible setting from cookie - getSetting(name) { - const ctrl = document.getElementById('noVNC_setting_' + name); - let val = WebUtil.readSetting(name); - if (typeof val !== 'undefined' && val !== null && ctrl.type === 'checkbox') { - if (val.toString().toLowerCase() in {'0': 1, 'no': 1, 'false': 1}) { - val = false; - } else { - val = true; - } - } - return val; - }, - - // These helpers compensate for the lack of parent-selectors and - // previous-sibling-selectors in CSS which are needed when we want to - // disable the labels that belong to disabled input elements. - disableSetting(name) { - const ctrl = document.getElementById('noVNC_setting_' + name); - ctrl.disabled = true; - ctrl.label.classList.add('noVNC_disabled'); - }, - - enableSetting(name) { - const ctrl = document.getElementById('noVNC_setting_' + name); - ctrl.disabled = false; - ctrl.label.classList.remove('noVNC_disabled'); - }, - -/* ------^------- - * /SETTINGS - * ============== - * PANELS - * ------v------*/ - - closeAllPanels() { - UI.closeSettingsPanel(); - UI.closePowerPanel(); - UI.closeClipboardPanel(); - UI.closeExtraKeys(); - }, - -/* ------^------- - * /PANELS - * ============== - * SETTINGS (panel) - * ------v------*/ - - openSettingsPanel() { - UI.closeAllPanels(); - UI.openControlbar(); - - // Refresh UI elements from saved cookies - UI.updateSetting('encrypt'); - UI.updateSetting('view_clip'); - UI.updateSetting('resize'); - UI.updateSetting('quality'); - UI.updateSetting('compression'); - UI.updateSetting('shared'); - UI.updateSetting('view_only'); - UI.updateSetting('path'); - UI.updateSetting('repeaterID'); - UI.updateSetting('logging'); - UI.updateSetting('reconnect'); - UI.updateSetting('reconnect_delay'); - - document.getElementById('noVNC_settings') - .classList.add("noVNC_open"); - document.getElementById('noVNC_settings_button') - .classList.add("noVNC_selected"); - }, - - closeSettingsPanel() { - document.getElementById('noVNC_settings') - .classList.remove("noVNC_open"); - document.getElementById('noVNC_settings_button') - .classList.remove("noVNC_selected"); - }, - - toggleSettingsPanel() { - if (document.getElementById('noVNC_settings') - .classList.contains("noVNC_open")) { - UI.closeSettingsPanel(); - } else { - UI.openSettingsPanel(); - } - }, - -/* ------^------- - * /SETTINGS - * ============== - * POWER - * ------v------*/ - - openPowerPanel() { - UI.closeAllPanels(); - UI.openControlbar(); - - document.getElementById('noVNC_power') - .classList.add("noVNC_open"); - document.getElementById('noVNC_power_button') - .classList.add("noVNC_selected"); - }, - - closePowerPanel() { - document.getElementById('noVNC_power') - .classList.remove("noVNC_open"); - document.getElementById('noVNC_power_button') - .classList.remove("noVNC_selected"); - }, - - togglePowerPanel() { - if (document.getElementById('noVNC_power') - .classList.contains("noVNC_open")) { - UI.closePowerPanel(); - } else { - UI.openPowerPanel(); - } - }, - - // Disable/enable power button - updatePowerButton() { - if (UI.connected && - UI.rfb.capabilities.power && - !UI.rfb.viewOnly) { - document.getElementById('noVNC_power_button') - .classList.remove("noVNC_hidden"); - } else { - document.getElementById('noVNC_power_button') - .classList.add("noVNC_hidden"); - // Close power panel if open - UI.closePowerPanel(); - } - }, - -/* ------^------- - * /POWER - * ============== - * CLIPBOARD - * ------v------*/ - - openClipboardPanel() { - UI.closeAllPanels(); - UI.openControlbar(); - - document.getElementById('noVNC_clipboard') - .classList.add("noVNC_open"); - document.getElementById('noVNC_clipboard_button') - .classList.add("noVNC_selected"); - }, - - closeClipboardPanel() { - document.getElementById('noVNC_clipboard') - .classList.remove("noVNC_open"); - document.getElementById('noVNC_clipboard_button') - .classList.remove("noVNC_selected"); - }, - - toggleClipboardPanel() { - if (document.getElementById('noVNC_clipboard') - .classList.contains("noVNC_open")) { - UI.closeClipboardPanel(); - } else { - UI.openClipboardPanel(); - } - }, - - clipboardReceive(e) { - Log.Debug(">> UI.clipboardReceive: " + e.detail.text.substr(0, 40) + "..."); - document.getElementById('noVNC_clipboard_text').value = e.detail.text; - Log.Debug("<< UI.clipboardReceive"); - }, - - clipboardSend() { - const text = document.getElementById('noVNC_clipboard_text').value; - Log.Debug(">> UI.clipboardSend: " + text.substr(0, 40) + "..."); - UI.rfb.clipboardPasteFrom(text); - Log.Debug("<< UI.clipboardSend"); - }, - -/* ------^------- - * /CLIPBOARD - * ============== - * CONNECTION - * ------v------*/ - - openConnectPanel() { - document.getElementById('noVNC_connect_dlg') - .classList.add("noVNC_open"); - }, - - closeConnectPanel() { - document.getElementById('noVNC_connect_dlg') - .classList.remove("noVNC_open"); - }, - - connect(event, password) { - - // Ignore when rfb already exists - if (typeof UI.rfb !== 'undefined') { - return; - } - - const host = UI.getSetting('host'); - const port = UI.getSetting('port'); - const path = UI.getSetting('path'); - - if (typeof password === 'undefined') { - password = WebUtil.getConfigVar('password'); - UI.reconnectPassword = password; - } - - if (password === null) { - password = undefined; - } - - UI.hideStatus(); - - if (!host) { - Log.Error("Can't connect when host is: " + host); - UI.showStatus(_("Must set host"), 'error'); - return; - } - - UI.closeConnectPanel(); - - UI.updateVisualState('connecting'); - - let url; - - url = UI.getSetting('encrypt') ? 'wss' : 'ws'; - - url += '://' + host; - if (port) { - url += ':' + port; - } - url += '/' + path; - - try { - UI.rfb = new RFB(document.getElementById('noVNC_container'), url, - { shared: UI.getSetting('shared'), - repeaterID: UI.getSetting('repeaterID'), - credentials: { password: password } }); - } catch (exc) { - Log.Error("Failed to connect to server: " + exc); - UI.updateVisualState('disconnected'); - UI.showStatus(_("Failed to connect to server: ") + exc, 'error'); - return; - } - - UI.rfb.addEventListener("connect", UI.connectFinished); - UI.rfb.addEventListener("disconnect", UI.disconnectFinished); - UI.rfb.addEventListener("serververification", UI.serverVerify); - UI.rfb.addEventListener("credentialsrequired", UI.credentials); - UI.rfb.addEventListener("securityfailure", UI.securityFailed); - UI.rfb.addEventListener("clippingviewport", UI.updateViewDrag); - UI.rfb.addEventListener("capabilities", UI.updatePowerButton); - UI.rfb.addEventListener("clipboard", UI.clipboardReceive); - UI.rfb.addEventListener("bell", UI.bell); - UI.rfb.addEventListener("desktopname", UI.updateDesktopName); - UI.rfb.clipViewport = UI.getSetting('view_clip'); - UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale'; - UI.rfb.resizeSession = UI.getSetting('resize') === 'remote'; - UI.rfb.qualityLevel = parseInt(UI.getSetting('quality')); - UI.rfb.compressionLevel = parseInt(UI.getSetting('compression')); - UI.rfb.showDotCursor = UI.getSetting('show_dot'); - - UI.updateViewOnly(); // requires UI.rfb - }, - - disconnect() { - UI.rfb.disconnect(); - - UI.connected = false; - - // Disable automatic reconnecting - UI.inhibitReconnect = true; - - UI.updateVisualState('disconnecting'); - - // Don't display the connection settings until we're actually disconnected - }, - - reconnect() { - UI.reconnectCallback = null; - - // if reconnect has been disabled in the meantime, do nothing. - if (UI.inhibitReconnect) { - return; - } - - UI.connect(null, UI.reconnectPassword); - }, - - cancelReconnect() { - if (UI.reconnectCallback !== null) { - clearTimeout(UI.reconnectCallback); - UI.reconnectCallback = null; - } - - UI.updateVisualState('disconnected'); - - UI.openControlbar(); - UI.openConnectPanel(); - }, - - connectFinished(e) { - UI.connected = true; - UI.inhibitReconnect = false; - - let msg; - if (UI.getSetting('encrypt')) { - msg = _("Connected (encrypted) to ") + UI.desktopName; - } else { - msg = _("Connected (unencrypted) to ") + UI.desktopName; - } - UI.showStatus(msg); - UI.updateVisualState('connected'); - - // Do this last because it can only be used on rendered elements - UI.rfb.focus(); - }, - - disconnectFinished(e) { - const wasConnected = UI.connected; - - // This variable is ideally set when disconnection starts, but - // when the disconnection isn't clean or if it is initiated by - // the server, we need to do it here as well since - // UI.disconnect() won't be used in those cases. - UI.connected = false; - - UI.rfb = undefined; - - if (!e.detail.clean) { - UI.updateVisualState('disconnected'); - if (wasConnected) { - UI.showStatus(_("Something went wrong, connection is closed"), - 'error'); - } else { - UI.showStatus(_("Failed to connect to server"), 'error'); - } - } - // If reconnecting is allowed process it now - if (UI.getSetting('reconnect', false) === true && !UI.inhibitReconnect) { - UI.updateVisualState('reconnecting'); - - const delay = parseInt(UI.getSetting('reconnect_delay')); - UI.reconnectCallback = setTimeout(UI.reconnect, delay); - return; - } else { - UI.updateVisualState('disconnected'); - UI.showStatus(_("Disconnected"), 'normal'); - } - - document.title = PAGE_TITLE; - - UI.openControlbar(); - UI.openConnectPanel(); - }, - - securityFailed(e) { - let msg = ""; - // On security failures we might get a string with a reason - // directly from the server. Note that we can't control if - // this string is translated or not. - if ('reason' in e.detail) { - msg = _("New connection has been rejected with reason: ") + - e.detail.reason; - } else { - msg = _("New connection has been rejected"); - } - UI.showStatus(msg, 'error'); - }, - -/* ------^------- - * /CONNECTION - * ============== - * SERVER VERIFY - * ------v------*/ - - async serverVerify(e) { - const type = e.detail.type; - if (type === 'RSA') { - const publickey = e.detail.publickey; - let fingerprint = await window.crypto.subtle.digest("SHA-1", publickey); - // The same fingerprint format as RealVNC - fingerprint = Array.from(new Uint8Array(fingerprint).slice(0, 8)).map( - x => x.toString(16).padStart(2, '0')).join('-'); - document.getElementById('noVNC_verify_server_dlg').classList.add('noVNC_open'); - document.getElementById('noVNC_fingerprint').innerHTML = fingerprint; - } - }, - - approveServer(e) { - e.preventDefault(); - document.getElementById('noVNC_verify_server_dlg').classList.remove('noVNC_open'); - UI.rfb.approveServer(); - }, - - rejectServer(e) { - e.preventDefault(); - document.getElementById('noVNC_verify_server_dlg').classList.remove('noVNC_open'); - UI.disconnect(); - }, - -/* ------^------- - * /SERVER VERIFY - * ============== - * PASSWORD - * ------v------*/ - - credentials(e) { - // FIXME: handle more types - - document.getElementById("noVNC_username_block").classList.remove("noVNC_hidden"); - document.getElementById("noVNC_password_block").classList.remove("noVNC_hidden"); - - let inputFocus = "none"; - if (e.detail.types.indexOf("username") === -1) { - document.getElementById("noVNC_username_block").classList.add("noVNC_hidden"); - } else { - inputFocus = inputFocus === "none" ? "noVNC_username_input" : inputFocus; - } - if (e.detail.types.indexOf("password") === -1) { - document.getElementById("noVNC_password_block").classList.add("noVNC_hidden"); - } else { - inputFocus = inputFocus === "none" ? "noVNC_password_input" : inputFocus; - } - document.getElementById('noVNC_credentials_dlg') - .classList.add('noVNC_open'); - - setTimeout(() => document - .getElementById(inputFocus).focus(), 100); - - Log.Warn("Server asked for credentials"); - UI.showStatus(_("Credentials are required"), "warning"); - }, - - setCredentials(e) { - // Prevent actually submitting the form - e.preventDefault(); - - let inputElemUsername = document.getElementById('noVNC_username_input'); - const username = inputElemUsername.value; - - let inputElemPassword = document.getElementById('noVNC_password_input'); - const password = inputElemPassword.value; - // Clear the input after reading the password - inputElemPassword.value = ""; - - UI.rfb.sendCredentials({ username: username, password: password }); - UI.reconnectPassword = password; - document.getElementById('noVNC_credentials_dlg') - .classList.remove('noVNC_open'); - }, - -/* ------^------- - * /PASSWORD - * ============== - * FULLSCREEN - * ------v------*/ - - toggleFullscreen() { - if (document.fullscreenElement || // alternative standard method - document.mozFullScreenElement || // currently working methods - document.webkitFullscreenElement || - document.msFullscreenElement) { - if (document.exitFullscreen) { - document.exitFullscreen(); - } else if (document.mozCancelFullScreen) { - document.mozCancelFullScreen(); - } else if (document.webkitExitFullscreen) { - document.webkitExitFullscreen(); - } else if (document.msExitFullscreen) { - document.msExitFullscreen(); - } - } else { - if (document.documentElement.requestFullscreen) { - document.documentElement.requestFullscreen(); - } else if (document.documentElement.mozRequestFullScreen) { - document.documentElement.mozRequestFullScreen(); - } else if (document.documentElement.webkitRequestFullscreen) { - document.documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); - } else if (document.body.msRequestFullscreen) { - document.body.msRequestFullscreen(); - } - } - UI.updateFullscreenButton(); - }, - - updateFullscreenButton() { - if (document.fullscreenElement || // alternative standard method - document.mozFullScreenElement || // currently working methods - document.webkitFullscreenElement || - document.msFullscreenElement ) { - document.getElementById('noVNC_fullscreen_button') - .classList.add("noVNC_selected"); - } else { - document.getElementById('noVNC_fullscreen_button') - .classList.remove("noVNC_selected"); - } - }, - -/* ------^------- - * /FULLSCREEN - * ============== - * RESIZE - * ------v------*/ - - // Apply remote resizing or local scaling - applyResizeMode() { - if (!UI.rfb) return; - - UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale'; - UI.rfb.resizeSession = UI.getSetting('resize') === 'remote'; - }, - -/* ------^------- - * /RESIZE - * ============== - * VIEW CLIPPING - * ------v------*/ - - // Update viewport clipping property for the connection. The normal - // case is to get the value from the setting. There are special cases - // for when the viewport is scaled or when a touch device is used. - updateViewClip() { - if (!UI.rfb) return; - - const scaling = UI.getSetting('resize') === 'scale'; - - // Some platforms have overlay scrollbars that are difficult - // to use in our case, which means we have to force panning - // FIXME: Working scrollbars can still be annoying to use with - // touch, so we should ideally be able to have both - // panning and scrollbars at the same time - - let brokenScrollbars = false; - - if (!hasScrollbarGutter) { - if (isIOS() || isAndroid() || isMac() || isChromeOS()) { - brokenScrollbars = true; - } - } - - if (scaling) { - // Can't be clipping if viewport is scaled to fit - UI.forceSetting('view_clip', false); - UI.rfb.clipViewport = false; - } else if (brokenScrollbars) { - UI.forceSetting('view_clip', true); - UI.rfb.clipViewport = true; - } else { - UI.enableSetting('view_clip'); - UI.rfb.clipViewport = UI.getSetting('view_clip'); - } - - // Changing the viewport may change the state of - // the dragging button - UI.updateViewDrag(); - }, - -/* ------^------- - * /VIEW CLIPPING - * ============== - * VIEWDRAG - * ------v------*/ - - toggleViewDrag() { - if (!UI.rfb) return; - - UI.rfb.dragViewport = !UI.rfb.dragViewport; - UI.updateViewDrag(); - }, - - updateViewDrag() { - if (!UI.connected) return; - - const viewDragButton = document.getElementById('noVNC_view_drag_button'); - - if ((!UI.rfb.clipViewport || !UI.rfb.clippingViewport) && - UI.rfb.dragViewport) { - // We are no longer clipping the viewport. Make sure - // viewport drag isn't active when it can't be used. - UI.rfb.dragViewport = false; - } - - if (UI.rfb.dragViewport) { - viewDragButton.classList.add("noVNC_selected"); - } else { - viewDragButton.classList.remove("noVNC_selected"); - } - - if (UI.rfb.clipViewport) { - viewDragButton.classList.remove("noVNC_hidden"); - } else { - viewDragButton.classList.add("noVNC_hidden"); - } - - viewDragButton.disabled = !UI.rfb.clippingViewport; - }, - -/* ------^------- - * /VIEWDRAG - * ============== - * QUALITY - * ------v------*/ - - updateQuality() { - if (!UI.rfb) return; - - UI.rfb.qualityLevel = parseInt(UI.getSetting('quality')); - }, - -/* ------^------- - * /QUALITY - * ============== - * COMPRESSION - * ------v------*/ - - updateCompression() { - if (!UI.rfb) return; - - UI.rfb.compressionLevel = parseInt(UI.getSetting('compression')); - }, - -/* ------^------- - * /COMPRESSION - * ============== - * KEYBOARD - * ------v------*/ - - showVirtualKeyboard() { - if (!isTouchDevice) return; - - const input = document.getElementById('noVNC_keyboardinput'); - - if (document.activeElement == input) return; - - input.focus(); - - try { - const l = input.value.length; - // Move the caret to the end - input.setSelectionRange(l, l); - } catch (err) { - // setSelectionRange is undefined in Google Chrome - } - }, - - hideVirtualKeyboard() { - if (!isTouchDevice) return; - - const input = document.getElementById('noVNC_keyboardinput'); - - if (document.activeElement != input) return; - - input.blur(); - }, - - toggleVirtualKeyboard() { - if (document.getElementById('noVNC_keyboard_button') - .classList.contains("noVNC_selected")) { - UI.hideVirtualKeyboard(); - } else { - UI.showVirtualKeyboard(); - } - }, - - onfocusVirtualKeyboard(event) { - document.getElementById('noVNC_keyboard_button') - .classList.add("noVNC_selected"); - if (UI.rfb) { - UI.rfb.focusOnClick = false; - } - }, - - onblurVirtualKeyboard(event) { - document.getElementById('noVNC_keyboard_button') - .classList.remove("noVNC_selected"); - if (UI.rfb) { - UI.rfb.focusOnClick = true; - } - }, - - keepVirtualKeyboard(event) { - const input = document.getElementById('noVNC_keyboardinput'); - - // Only prevent focus change if the virtual keyboard is active - if (document.activeElement != input) { - return; - } - - // Only allow focus to move to other elements that need - // focus to function properly - if (event.target.form !== undefined) { - switch (event.target.type) { - case 'text': - case 'email': - case 'search': - case 'password': - case 'tel': - case 'url': - case 'textarea': - case 'select-one': - case 'select-multiple': - return; - } - } - - event.preventDefault(); - }, - - keyboardinputReset() { - const kbi = document.getElementById('noVNC_keyboardinput'); - kbi.value = new Array(UI.defaultKeyboardinputLen).join("_"); - UI.lastKeyboardinput = kbi.value; - }, - - keyEvent(keysym, code, down) { - if (!UI.rfb) return; - - UI.rfb.sendKey(keysym, code, down); - }, - - // When normal keyboard events are left uncought, use the input events from - // the keyboardinput element instead and generate the corresponding key events. - // This code is required since some browsers on Android are inconsistent in - // sending keyCodes in the normal keyboard events when using on screen keyboards. - keyInput(event) { - - if (!UI.rfb) return; - - const newValue = event.target.value; - - if (!UI.lastKeyboardinput) { - UI.keyboardinputReset(); - } - const oldValue = UI.lastKeyboardinput; - - let newLen; - try { - // Try to check caret position since whitespace at the end - // will not be considered by value.length in some browsers - newLen = Math.max(event.target.selectionStart, newValue.length); - } catch (err) { - // selectionStart is undefined in Google Chrome - newLen = newValue.length; - } - const oldLen = oldValue.length; - - let inputs = newLen - oldLen; - let backspaces = inputs < 0 ? -inputs : 0; - - // Compare the old string with the new to account for - // text-corrections or other input that modify existing text - for (let i = 0; i < Math.min(oldLen, newLen); i++) { - if (newValue.charAt(i) != oldValue.charAt(i)) { - inputs = newLen - i; - backspaces = oldLen - i; - break; - } - } - - // Send the key events - for (let i = 0; i < backspaces; i++) { - UI.rfb.sendKey(KeyTable.XK_BackSpace, "Backspace"); - } - for (let i = newLen - inputs; i < newLen; i++) { - UI.rfb.sendKey(keysyms.lookup(newValue.charCodeAt(i))); - } - - // Control the text content length in the keyboardinput element - if (newLen > 2 * UI.defaultKeyboardinputLen) { - UI.keyboardinputReset(); - } else if (newLen < 1) { - // There always have to be some text in the keyboardinput - // element with which backspace can interact. - UI.keyboardinputReset(); - // This sometimes causes the keyboard to disappear for a second - // but it is required for the android keyboard to recognize that - // text has been added to the field - event.target.blur(); - // This has to be ran outside of the input handler in order to work - setTimeout(event.target.focus.bind(event.target), 0); - } else { - UI.lastKeyboardinput = newValue; - } - }, - -/* ------^------- - * /KEYBOARD - * ============== - * EXTRA KEYS - * ------v------*/ - - openExtraKeys() { - UI.closeAllPanels(); - UI.openControlbar(); - - document.getElementById('noVNC_modifiers') - .classList.add("noVNC_open"); - document.getElementById('noVNC_toggle_extra_keys_button') - .classList.add("noVNC_selected"); - }, - - closeExtraKeys() { - document.getElementById('noVNC_modifiers') - .classList.remove("noVNC_open"); - document.getElementById('noVNC_toggle_extra_keys_button') - .classList.remove("noVNC_selected"); - }, - - toggleExtraKeys() { - if (document.getElementById('noVNC_modifiers') - .classList.contains("noVNC_open")) { - UI.closeExtraKeys(); - } else { - UI.openExtraKeys(); - } - }, - - sendEsc() { - UI.sendKey(KeyTable.XK_Escape, "Escape"); - }, - - sendTab() { - UI.sendKey(KeyTable.XK_Tab, "Tab"); - }, - - toggleCtrl() { - const btn = document.getElementById('noVNC_toggle_ctrl_button'); - if (btn.classList.contains("noVNC_selected")) { - UI.sendKey(KeyTable.XK_Control_L, "ControlLeft", false); - btn.classList.remove("noVNC_selected"); - } else { - UI.sendKey(KeyTable.XK_Control_L, "ControlLeft", true); - btn.classList.add("noVNC_selected"); - } - }, - - toggleWindows() { - const btn = document.getElementById('noVNC_toggle_windows_button'); - if (btn.classList.contains("noVNC_selected")) { - UI.sendKey(KeyTable.XK_Super_L, "MetaLeft", false); - btn.classList.remove("noVNC_selected"); - } else { - UI.sendKey(KeyTable.XK_Super_L, "MetaLeft", true); - btn.classList.add("noVNC_selected"); - } - }, - - toggleAlt() { - const btn = document.getElementById('noVNC_toggle_alt_button'); - if (btn.classList.contains("noVNC_selected")) { - UI.sendKey(KeyTable.XK_Alt_L, "AltLeft", false); - btn.classList.remove("noVNC_selected"); - } else { - UI.sendKey(KeyTable.XK_Alt_L, "AltLeft", true); - btn.classList.add("noVNC_selected"); - } - }, - - sendCtrlAltDel() { - UI.rfb.sendCtrlAltDel(); - // See below - UI.rfb.focus(); - UI.idleControlbar(); - }, - - sendKey(keysym, code, down) { - UI.rfb.sendKey(keysym, code, down); - - // Move focus to the screen in order to be able to use the - // keyboard right after these extra keys. - // The exception is when a virtual keyboard is used, because - // if we focus the screen the virtual keyboard would be closed. - // In this case we focus our special virtual keyboard input - // element instead. - if (document.getElementById('noVNC_keyboard_button') - .classList.contains("noVNC_selected")) { - document.getElementById('noVNC_keyboardinput').focus(); - } else { - UI.rfb.focus(); - } - // fade out the controlbar to highlight that - // the focus has been moved to the screen - UI.idleControlbar(); - }, - -/* ------^------- - * /EXTRA KEYS - * ============== - * MISC - * ------v------*/ - - updateViewOnly() { - if (!UI.rfb) return; - UI.rfb.viewOnly = UI.getSetting('view_only'); - - // Hide input related buttons in view only mode - if (UI.rfb.viewOnly) { - document.getElementById('noVNC_keyboard_button') - .classList.add('noVNC_hidden'); - document.getElementById('noVNC_toggle_extra_keys_button') - .classList.add('noVNC_hidden'); - document.getElementById('noVNC_clipboard_button') - .classList.add('noVNC_hidden'); - } else { - document.getElementById('noVNC_keyboard_button') - .classList.remove('noVNC_hidden'); - document.getElementById('noVNC_toggle_extra_keys_button') - .classList.remove('noVNC_hidden'); - document.getElementById('noVNC_clipboard_button') - .classList.remove('noVNC_hidden'); - } - }, - - updateShowDotCursor() { - if (!UI.rfb) return; - UI.rfb.showDotCursor = UI.getSetting('show_dot'); - }, - - updateLogging() { - WebUtil.initLogging(UI.getSetting('logging')); - }, - - updateDesktopName(e) { - UI.desktopName = e.detail.name; - // Display the desktop name in the document title - document.title = e.detail.name + " - " + PAGE_TITLE; - }, - - bell(e) { - if (WebUtil.getConfigVar('bell', 'on') === 'on') { - const promise = document.getElementById('noVNC_bell').play(); - // The standards disagree on the return value here - if (promise) { - promise.catch((e) => { - if (e.name === "NotAllowedError") { - // Ignore when the browser doesn't let us play audio. - // It is common that the browsers require audio to be - // initiated from a user action. - } else { - Log.Error("Unable to play bell: " + e); - } - }); - } - } - }, - - //Helper to add options to dropdown. - addOption(selectbox, text, value) { - const optn = document.createElement("OPTION"); - optn.text = text; - optn.value = value; - selectbox.options.add(optn); - }, - -/* ------^------- - * /MISC - * ============== - */ -}; - -// Set up translations -const LINGUAS = ["cs", "de", "el", "es", "fr", "it", "ja", "ko", "nl", "pl", "pt_BR", "ru", "sv", "tr", "zh_CN", "zh_TW"]; -l10n.setup(LINGUAS, "app/locale/") - .catch(err => Log.Error("Failed to load translations: " + err)) - .then(UI.prime); - -export default UI; diff --git a/base/app/novnc/app/webutil.js b/base/app/novnc/app/webutil.js deleted file mode 100644 index 6011442..0000000 --- a/base/app/novnc/app/webutil.js +++ /dev/null @@ -1,250 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2019 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - */ - -import * as Log from '../core/util/logging.js'; - -// init log level reading the logging HTTP param -export function initLogging(level) { - "use strict"; - if (typeof level !== "undefined") { - Log.initLogging(level); - } else { - const param = document.location.href.match(/logging=([A-Za-z0-9._-]*)/); - Log.initLogging(param || undefined); - } -} - -// Read a query string variable -// A URL with a query parameter can look like this (But will most probably get logged on the http server): -// https://www.example.com?myqueryparam=myvalue -// -// For privacy (Using a hastag #, the parameters will not be sent to the server) -// the url can be requested in the following way: -// https://www.example.com#myqueryparam=myvalue&password=secretvalue -// -// Even Mixing public and non public parameters will work: -// https://www.example.com?nonsecretparam=example.com#password=secretvalue -export function getQueryVar(name, defVal) { - "use strict"; - const re = new RegExp('.*[?&]' + name + '=([^&#]*)'), - match = document.location.href.match(re); - if (typeof defVal === 'undefined') { defVal = null; } - - if (match) { - return decodeURIComponent(match[1]); - } - - return defVal; -} - -// Read a hash fragment variable -export function getHashVar(name, defVal) { - "use strict"; - const re = new RegExp('.*[&#]' + name + '=([^&]*)'), - match = document.location.hash.match(re); - if (typeof defVal === 'undefined') { defVal = null; } - - if (match) { - return decodeURIComponent(match[1]); - } - - return defVal; -} - -// Read a variable from the fragment or the query string -// Fragment takes precedence -export function getConfigVar(name, defVal) { - "use strict"; - const val = getHashVar(name); - - if (val === null) { - return getQueryVar(name, defVal); - } - - return val; -} - -/* - * Cookie handling. Dervied from: http://www.quirksmode.org/js/cookies.html - */ - -// No days means only for this browser session -export function createCookie(name, value, days) { - "use strict"; - let date, expires; - if (days) { - date = new Date(); - date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); - expires = "; expires=" + date.toGMTString(); - } else { - expires = ""; - } - - let secure; - if (document.location.protocol === "https:") { - secure = "; secure"; - } else { - secure = ""; - } - document.cookie = name + "=" + value + expires + "; path=/" + secure; -} - -export function readCookie(name, defaultValue) { - "use strict"; - const nameEQ = name + "="; - const ca = document.cookie.split(';'); - - for (let i = 0; i < ca.length; i += 1) { - let c = ca[i]; - while (c.charAt(0) === ' ') { - c = c.substring(1, c.length); - } - if (c.indexOf(nameEQ) === 0) { - return c.substring(nameEQ.length, c.length); - } - } - - return (typeof defaultValue !== 'undefined') ? defaultValue : null; -} - -export function eraseCookie(name) { - "use strict"; - createCookie(name, "", -1); -} - -/* - * Setting handling. - */ - -let settings = {}; - -export function initSettings() { - if (!window.chrome || !window.chrome.storage) { - settings = {}; - return Promise.resolve(); - } - - return new Promise(resolve => window.chrome.storage.sync.get(resolve)) - .then((cfg) => { settings = cfg; }); -} - -// Update the settings cache, but do not write to permanent storage -export function setSetting(name, value) { - settings[name] = value; -} - -// No days means only for this browser session -export function writeSetting(name, value) { - "use strict"; - if (settings[name] === value) return; - settings[name] = value; - if (window.chrome && window.chrome.storage) { - window.chrome.storage.sync.set(settings); - } else { - localStorageSet(name, value); - } -} - -export function readSetting(name, defaultValue) { - "use strict"; - let value; - if ((name in settings) || (window.chrome && window.chrome.storage)) { - value = settings[name]; - } else { - value = localStorageGet(name); - settings[name] = value; - } - if (typeof value === "undefined") { - value = null; - } - - if (value === null && typeof defaultValue !== "undefined") { - return defaultValue; - } - - return value; -} - -export function eraseSetting(name) { - "use strict"; - // Deleting here means that next time the setting is read when using local - // storage, it will be pulled from local storage again. - // If the setting in local storage is changed (e.g. in another tab) - // between this delete and the next read, it could lead to an unexpected - // value change. - delete settings[name]; - if (window.chrome && window.chrome.storage) { - window.chrome.storage.sync.remove(name); - } else { - localStorageRemove(name); - } -} - -let loggedMsgs = []; -function logOnce(msg, level = "warn") { - if (!loggedMsgs.includes(msg)) { - switch (level) { - case "error": - Log.Error(msg); - break; - case "warn": - Log.Warn(msg); - break; - case "debug": - Log.Debug(msg); - break; - default: - Log.Info(msg); - } - loggedMsgs.push(msg); - } -} - -let cookiesMsg = "Couldn't access noVNC settings, are cookies disabled?"; - -function localStorageGet(name) { - let r; - try { - r = localStorage.getItem(name); - } catch (e) { - if (e instanceof DOMException) { - logOnce(cookiesMsg); - logOnce("'localStorage.getItem(" + name + ")' failed: " + e, - "debug"); - } else { - throw e; - } - } - return r; -} -function localStorageSet(name, value) { - try { - localStorage.setItem(name, value); - } catch (e) { - if (e instanceof DOMException) { - logOnce(cookiesMsg); - logOnce("'localStorage.setItem(" + name + "," + value + - ")' failed: " + e, "debug"); - } else { - throw e; - } - } -} -function localStorageRemove(name) { - try { - localStorage.removeItem(name); - } catch (e) { - if (e instanceof DOMException) { - logOnce(cookiesMsg); - logOnce("'localStorage.removeItem(" + name + ")' failed: " + e, - "debug"); - } else { - throw e; - } - } -} diff --git a/base/app/novnc/core/base64.js b/base/app/novnc/core/base64.js deleted file mode 100644 index db572c2..0000000 --- a/base/app/novnc/core/base64.js +++ /dev/null @@ -1,104 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// From: http://hg.mozilla.org/mozilla-central/raw-file/ec10630b1a54/js/src/devtools/jint/sunspider/string-base64.js - -import * as Log from './util/logging.js'; - -export default { - /* Convert data (an array of integers) to a Base64 string. */ - toBase64Table: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''), - base64Pad: '=', - - encode(data) { - "use strict"; - let result = ''; - const length = data.length; - const lengthpad = (length % 3); - // Convert every three bytes to 4 ascii characters. - - for (let i = 0; i < (length - 2); i += 3) { - result += this.toBase64Table[data[i] >> 2]; - result += this.toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)]; - result += this.toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)]; - result += this.toBase64Table[data[i + 2] & 0x3f]; - } - - // Convert the remaining 1 or 2 bytes, pad out to 4 characters. - const j = length - lengthpad; - if (lengthpad === 2) { - result += this.toBase64Table[data[j] >> 2]; - result += this.toBase64Table[((data[j] & 0x03) << 4) + (data[j + 1] >> 4)]; - result += this.toBase64Table[(data[j + 1] & 0x0f) << 2]; - result += this.toBase64Table[64]; - } else if (lengthpad === 1) { - result += this.toBase64Table[data[j] >> 2]; - result += this.toBase64Table[(data[j] & 0x03) << 4]; - result += this.toBase64Table[64]; - result += this.toBase64Table[64]; - } - - return result; - }, - - /* Convert Base64 data to a string */ - /* eslint-disable comma-spacing */ - toBinaryTable: [ - -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, - -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, - -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, - 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1, - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, - 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, - -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, - 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 - ], - /* eslint-enable comma-spacing */ - - decode(data, offset = 0) { - let dataLength = data.indexOf('=') - offset; - if (dataLength < 0) { dataLength = data.length - offset; } - - /* Every four characters is 3 resulting numbers */ - const resultLength = (dataLength >> 2) * 3 + Math.floor((dataLength % 4) / 1.5); - const result = new Array(resultLength); - - // Convert one by one. - - let leftbits = 0; // number of bits decoded, but yet to be appended - let leftdata = 0; // bits decoded, but yet to be appended - for (let idx = 0, i = offset; i < data.length; i++) { - const c = this.toBinaryTable[data.charCodeAt(i) & 0x7f]; - const padding = (data.charAt(i) === this.base64Pad); - // Skip illegal characters and whitespace - if (c === -1) { - Log.Error("Illegal character code " + data.charCodeAt(i) + " at position " + i); - continue; - } - - // Collect data into leftdata, update bitcount - leftdata = (leftdata << 6) | c; - leftbits += 6; - - // If we have 8 or more bits, append 8 bits to the result - if (leftbits >= 8) { - leftbits -= 8; - // Append if not padding. - if (!padding) { - result[idx++] = (leftdata >> leftbits) & 0xff; - } - leftdata &= (1 << leftbits) - 1; - } - } - - // If there are any bits left, the base64 string was corrupted - if (leftbits) { - const err = new Error('Corrupted base64 string'); - err.name = 'Base64-Error'; - throw err; - } - - return result; - } -}; /* End of Base64 namespace */ diff --git a/base/app/novnc/core/crypto/aes.js b/base/app/novnc/core/crypto/aes.js deleted file mode 100644 index e6aaea7..0000000 --- a/base/app/novnc/core/crypto/aes.js +++ /dev/null @@ -1,178 +0,0 @@ -export class AESECBCipher { - constructor() { - this._key = null; - } - - get algorithm() { - return { name: "AES-ECB" }; - } - - static async importKey(key, _algorithm, extractable, keyUsages) { - const cipher = new AESECBCipher; - await cipher._importKey(key, extractable, keyUsages); - return cipher; - } - - async _importKey(key, extractable, keyUsages) { - this._key = await window.crypto.subtle.importKey( - "raw", key, {name: "AES-CBC"}, extractable, keyUsages); - } - - async encrypt(_algorithm, plaintext) { - const x = new Uint8Array(plaintext); - if (x.length % 16 !== 0 || this._key === null) { - return null; - } - const n = x.length / 16; - for (let i = 0; i < n; i++) { - const y = new Uint8Array(await window.crypto.subtle.encrypt({ - name: "AES-CBC", - iv: new Uint8Array(16), - }, this._key, x.slice(i * 16, i * 16 + 16))).slice(0, 16); - x.set(y, i * 16); - } - return x; - } -} - -export class AESEAXCipher { - constructor() { - this._rawKey = null; - this._ctrKey = null; - this._cbcKey = null; - this._zeroBlock = new Uint8Array(16); - this._prefixBlock0 = this._zeroBlock; - this._prefixBlock1 = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); - this._prefixBlock2 = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]); - } - - get algorithm() { - return { name: "AES-EAX" }; - } - - async _encryptBlock(block) { - const encrypted = await window.crypto.subtle.encrypt({ - name: "AES-CBC", - iv: this._zeroBlock, - }, this._cbcKey, block); - return new Uint8Array(encrypted).slice(0, 16); - } - - async _initCMAC() { - const k1 = await this._encryptBlock(this._zeroBlock); - const k2 = new Uint8Array(16); - const v = k1[0] >>> 6; - for (let i = 0; i < 15; i++) { - k2[i] = (k1[i + 1] >> 6) | (k1[i] << 2); - k1[i] = (k1[i + 1] >> 7) | (k1[i] << 1); - } - const lut = [0x0, 0x87, 0x0e, 0x89]; - k2[14] ^= v >>> 1; - k2[15] = (k1[15] << 2) ^ lut[v]; - k1[15] = (k1[15] << 1) ^ lut[v >> 1]; - this._k1 = k1; - this._k2 = k2; - } - - async _encryptCTR(data, counter) { - const encrypted = await window.crypto.subtle.encrypt({ - name: "AES-CTR", - counter: counter, - length: 128 - }, this._ctrKey, data); - return new Uint8Array(encrypted); - } - - async _decryptCTR(data, counter) { - const decrypted = await window.crypto.subtle.decrypt({ - name: "AES-CTR", - counter: counter, - length: 128 - }, this._ctrKey, data); - return new Uint8Array(decrypted); - } - - async _computeCMAC(data, prefixBlock) { - if (prefixBlock.length !== 16) { - return null; - } - const n = Math.floor(data.length / 16); - const m = Math.ceil(data.length / 16); - const r = data.length - n * 16; - const cbcData = new Uint8Array((m + 1) * 16); - cbcData.set(prefixBlock); - cbcData.set(data, 16); - if (r === 0) { - for (let i = 0; i < 16; i++) { - cbcData[n * 16 + i] ^= this._k1[i]; - } - } else { - cbcData[(n + 1) * 16 + r] = 0x80; - for (let i = 0; i < 16; i++) { - cbcData[(n + 1) * 16 + i] ^= this._k2[i]; - } - } - let cbcEncrypted = await window.crypto.subtle.encrypt({ - name: "AES-CBC", - iv: this._zeroBlock, - }, this._cbcKey, cbcData); - - cbcEncrypted = new Uint8Array(cbcEncrypted); - const mac = cbcEncrypted.slice(cbcEncrypted.length - 32, cbcEncrypted.length - 16); - return mac; - } - - static async importKey(key, _algorithm, _extractable, _keyUsages) { - const cipher = new AESEAXCipher; - await cipher._importKey(key); - return cipher; - } - - async _importKey(key) { - this._rawKey = key; - this._ctrKey = await window.crypto.subtle.importKey( - "raw", key, {name: "AES-CTR"}, false, ["encrypt", "decrypt"]); - this._cbcKey = await window.crypto.subtle.importKey( - "raw", key, {name: "AES-CBC"}, false, ["encrypt"]); - await this._initCMAC(); - } - - async encrypt(algorithm, message) { - const ad = algorithm.additionalData; - const nonce = algorithm.iv; - const nCMAC = await this._computeCMAC(nonce, this._prefixBlock0); - const encrypted = await this._encryptCTR(message, nCMAC); - const adCMAC = await this._computeCMAC(ad, this._prefixBlock1); - const mac = await this._computeCMAC(encrypted, this._prefixBlock2); - for (let i = 0; i < 16; i++) { - mac[i] ^= nCMAC[i] ^ adCMAC[i]; - } - const res = new Uint8Array(16 + encrypted.length); - res.set(encrypted); - res.set(mac, encrypted.length); - return res; - } - - async decrypt(algorithm, data) { - const encrypted = data.slice(0, data.length - 16); - const ad = algorithm.additionalData; - const nonce = algorithm.iv; - const mac = data.slice(data.length - 16); - const nCMAC = await this._computeCMAC(nonce, this._prefixBlock0); - const adCMAC = await this._computeCMAC(ad, this._prefixBlock1); - const computedMac = await this._computeCMAC(encrypted, this._prefixBlock2); - for (let i = 0; i < 16; i++) { - computedMac[i] ^= nCMAC[i] ^ adCMAC[i]; - } - if (computedMac.length !== mac.length) { - return null; - } - for (let i = 0; i < mac.length; i++) { - if (computedMac[i] !== mac[i]) { - return null; - } - } - const res = await this._decryptCTR(encrypted, nCMAC); - return res; - } -} diff --git a/base/app/novnc/core/crypto/bigint.js b/base/app/novnc/core/crypto/bigint.js deleted file mode 100644 index d344326..0000000 --- a/base/app/novnc/core/crypto/bigint.js +++ /dev/null @@ -1,34 +0,0 @@ -export function modPow(b, e, m) { - let r = 1n; - b = b % m; - while (e > 0n) { - if ((e & 1n) === 1n) { - r = (r * b) % m; - } - e = e >> 1n; - b = (b * b) % m; - } - return r; -} - -export function bigIntToU8Array(bigint, padLength=0) { - let hex = bigint.toString(16); - if (padLength === 0) { - padLength = Math.ceil(hex.length / 2); - } - hex = hex.padStart(padLength * 2, '0'); - const length = hex.length / 2; - const arr = new Uint8Array(length); - for (let i = 0; i < length; i++) { - arr[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16); - } - return arr; -} - -export function u8ArrayToBigInt(arr) { - let hex = '0x'; - for (let i = 0; i < arr.length; i++) { - hex += arr[i].toString(16).padStart(2, '0'); - } - return BigInt(hex); -} diff --git a/base/app/novnc/core/crypto/crypto.js b/base/app/novnc/core/crypto/crypto.js deleted file mode 100644 index cc17da2..0000000 --- a/base/app/novnc/core/crypto/crypto.js +++ /dev/null @@ -1,90 +0,0 @@ -import { AESECBCipher, AESEAXCipher } from "./aes.js"; -import { DESCBCCipher, DESECBCipher } from "./des.js"; -import { RSACipher } from "./rsa.js"; -import { DHCipher } from "./dh.js"; -import { MD5 } from "./md5.js"; - -// A single interface for the cryptographic algorithms not supported by SubtleCrypto. -// Both synchronous and asynchronous implmentations are allowed. -class LegacyCrypto { - constructor() { - this._algorithms = { - "AES-ECB": AESECBCipher, - "AES-EAX": AESEAXCipher, - "DES-ECB": DESECBCipher, - "DES-CBC": DESCBCCipher, - "RSA-PKCS1-v1_5": RSACipher, - "DH": DHCipher, - "MD5": MD5, - }; - } - - encrypt(algorithm, key, data) { - if (key.algorithm.name !== algorithm.name) { - throw new Error("algorithm does not match"); - } - if (typeof key.encrypt !== "function") { - throw new Error("key does not support encryption"); - } - return key.encrypt(algorithm, data); - } - - decrypt(algorithm, key, data) { - if (key.algorithm.name !== algorithm.name) { - throw new Error("algorithm does not match"); - } - if (typeof key.decrypt !== "function") { - throw new Error("key does not support encryption"); - } - return key.decrypt(algorithm, data); - } - - importKey(format, keyData, algorithm, extractable, keyUsages) { - if (format !== "raw") { - throw new Error("key format is not supported"); - } - const alg = this._algorithms[algorithm.name]; - if (typeof alg === "undefined" || typeof alg.importKey !== "function") { - throw new Error("algorithm is not supported"); - } - return alg.importKey(keyData, algorithm, extractable, keyUsages); - } - - generateKey(algorithm, extractable, keyUsages) { - const alg = this._algorithms[algorithm.name]; - if (typeof alg === "undefined" || typeof alg.generateKey !== "function") { - throw new Error("algorithm is not supported"); - } - return alg.generateKey(algorithm, extractable, keyUsages); - } - - exportKey(format, key) { - if (format !== "raw") { - throw new Error("key format is not supported"); - } - if (typeof key.exportKey !== "function") { - throw new Error("key does not support exportKey"); - } - return key.exportKey(); - } - - digest(algorithm, data) { - const alg = this._algorithms[algorithm]; - if (typeof alg !== "function") { - throw new Error("algorithm is not supported"); - } - return alg(data); - } - - deriveBits(algorithm, key, length) { - if (key.algorithm.name !== algorithm.name) { - throw new Error("algorithm does not match"); - } - if (typeof key.deriveBits !== "function") { - throw new Error("key does not support deriveBits"); - } - return key.deriveBits(algorithm, length); - } -} - -export default new LegacyCrypto; diff --git a/base/app/novnc/core/crypto/des.js b/base/app/novnc/core/crypto/des.js deleted file mode 100644 index 8dab31f..0000000 --- a/base/app/novnc/core/crypto/des.js +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Ported from Flashlight VNC ActionScript implementation: - * http://www.wizhelp.com/flashlight-vnc/ - * - * Full attribution follows: - * - * ------------------------------------------------------------------------- - * - * This DES class has been extracted from package Acme.Crypto for use in VNC. - * The unnecessary odd parity code has been removed. - * - * These changes are: - * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. - * - * 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. - * - - * DesCipher - the DES encryption method - * - * The meat of this code is by Dave Zimmerman <dzimm@widget.com>, and is: - * - * Copyright (c) 1996 Widget Workshop, Inc. All Rights Reserved. - * - * Permission to use, copy, modify, and distribute this software - * and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and - * without fee is hereby granted, provided that this copyright notice is kept - * intact. - * - * WIDGET WORKSHOP MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY - * OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. WIDGET WORKSHOP SHALL NOT BE LIABLE - * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. - * - * THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE - * CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE - * PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT - * NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE - * SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE - * SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE - * PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). WIDGET WORKSHOP - * SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR - * HIGH RISK ACTIVITIES. - * - * - * The rest is: - * - * Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * Visit the ACME Labs Java page for up-to-date versions of this and other - * fine Java utilities: http://www.acme.com/java/ - */ - -/* eslint-disable comma-spacing */ - -// Tables, permutations, S-boxes, etc. -const PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3, - 25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39, - 50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ], - totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28]; - -const z = 0x0; -let a,b,c,d,e,f; -a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e; -const SP1 = [c|e,z|z,a|z,c|f,c|d,a|f,z|d,a|z,z|e,c|e,c|f,z|e,b|f,c|d,b|z,z|d, - z|f,b|e,b|e,a|e,a|e,c|z,c|z,b|f,a|d,b|d,b|d,a|d,z|z,z|f,a|f,b|z, - a|z,c|f,z|d,c|z,c|e,b|z,b|z,z|e,c|d,a|z,a|e,b|d,z|e,z|d,b|f,a|f, - c|f,a|d,c|z,b|f,b|d,z|f,a|f,c|e,z|f,b|e,b|e,z|z,a|d,a|e,z|z,c|d]; -a=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e; -const SP2 = [c|f,b|e,z|e,a|f,a|z,z|d,c|d,b|f,b|d,c|f,c|e,b|z,b|e,a|z,z|d,c|d, - a|e,a|d,b|f,z|z,b|z,z|e,a|f,c|z,a|d,b|d,z|z,a|e,z|f,c|e,c|z,z|f, - z|z,a|f,c|d,a|z,b|f,c|z,c|e,z|e,c|z,b|e,z|d,c|f,a|f,z|d,z|e,b|z, - z|f,c|e,a|z,b|d,a|d,b|f,b|d,a|d,a|e,z|z,b|e,z|f,b|z,c|d,c|f,a|e]; -a=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e; -const SP3 = [z|f,c|e,z|z,c|d,b|e,z|z,a|f,b|e,a|d,b|d,b|d,a|z,c|f,a|d,c|z,z|f, - b|z,z|d,c|e,z|e,a|e,c|z,c|d,a|f,b|f,a|e,a|z,b|f,z|d,c|f,z|e,b|z, - c|e,b|z,a|d,z|f,a|z,c|e,b|e,z|z,z|e,a|d,c|f,b|e,b|d,z|e,z|z,c|d, - b|f,a|z,b|z,c|f,z|d,a|f,a|e,b|d,c|z,b|f,z|f,c|z,a|f,z|d,c|d,a|e]; -a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|e; -const SP4 = [c|d,a|f,a|f,z|e,c|e,b|f,b|d,a|d,z|z,c|z,c|z,c|f,z|f,z|z,b|e,b|d, - z|d,a|z,b|z,c|d,z|e,b|z,a|d,a|e,b|f,z|d,a|e,b|e,a|z,c|e,c|f,z|f, - b|e,b|d,c|z,c|f,z|f,z|z,z|z,c|z,a|e,b|e,b|f,z|d,c|d,a|f,a|f,z|e, - c|f,z|f,z|d,a|z,b|d,a|d,c|e,b|f,a|d,a|e,b|z,c|d,z|e,b|z,a|z,c|e]; -a=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|e; -const SP5 = [z|d,a|f,a|e,c|d,z|e,z|d,b|z,a|e,b|f,z|e,a|d,b|f,c|d,c|e,z|f,b|z, - a|z,b|e,b|e,z|z,b|d,c|f,c|f,a|d,c|e,b|d,z|z,c|z,a|f,a|z,c|z,z|f, - z|e,c|d,z|d,a|z,b|z,a|e,c|d,b|f,a|d,b|z,c|e,a|f,b|f,z|d,a|z,c|e, - c|f,z|f,c|z,c|f,a|e,z|z,b|e,c|z,z|f,a|d,b|d,z|e,z|z,b|e,a|f,b|d]; -a=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|e; -const SP6 = [b|d,c|z,z|e,c|f,c|z,z|d,c|f,a|z,b|e,a|f,a|z,b|d,a|d,b|e,b|z,z|f, - z|z,a|d,b|f,z|e,a|e,b|f,z|d,c|d,c|d,z|z,a|f,c|e,z|f,a|e,c|e,b|z, - b|e,z|d,c|d,a|e,c|f,a|z,z|f,b|d,a|z,b|e,b|z,z|f,b|d,c|f,a|e,c|z, - a|f,c|e,z|z,c|d,z|d,z|e,c|z,a|f,z|e,a|d,b|f,z|z,c|e,b|z,a|d,b|f]; -a=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|e; -const SP7 = [a|z,c|d,b|f,z|z,z|e,b|f,a|f,c|e,c|f,a|z,z|z,b|d,z|d,b|z,c|d,z|f, - b|e,a|f,a|d,b|e,b|d,c|z,c|e,a|d,c|z,z|e,z|f,c|f,a|e,z|d,b|z,a|e, - b|z,a|e,a|z,b|f,b|f,c|d,c|d,z|d,a|d,b|z,b|e,a|z,c|e,z|f,a|f,c|e, - z|f,b|d,c|f,c|z,a|e,z|z,z|d,c|f,z|z,a|f,c|z,z|e,b|d,b|e,z|e,a|d]; -a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|e; -const SP8 = [b|f,z|e,a|z,c|f,b|z,b|f,z|d,b|z,a|d,c|z,c|f,a|e,c|e,a|f,z|e,z|d, - c|z,b|d,b|e,z|f,a|e,a|d,c|d,c|e,z|f,z|z,z|z,c|d,b|d,b|e,a|f,a|z, - a|f,a|z,c|e,z|e,z|d,c|d,z|e,a|f,b|e,z|d,b|d,c|z,c|d,b|z,a|z,b|f, - z|z,c|f,a|d,b|d,c|z,b|e,b|f,z|z,c|f,a|e,a|e,z|f,z|f,a|d,b|z,c|e]; - -/* eslint-enable comma-spacing */ - -class DES { - constructor(password) { - this.keys = []; - - // Set the key. - const pc1m = [], pcr = [], kn = []; - - for (let j = 0, l = 56; j < 56; ++j, l -= 8) { - l += l < -5 ? 65 : l < -3 ? 31 : l < -1 ? 63 : l === 27 ? 35 : 0; // PC1 - const m = l & 0x7; - pc1m[j] = ((password[l >>> 3] & (1<<m)) !== 0) ? 1: 0; - } - - for (let i = 0; i < 16; ++i) { - const m = i << 1; - const n = m + 1; - kn[m] = kn[n] = 0; - for (let o = 28; o < 59; o += 28) { - for (let j = o - 28; j < o; ++j) { - const l = j + totrot[i]; - pcr[j] = l < o ? pc1m[l] : pc1m[l - 28]; - } - } - for (let j = 0; j < 24; ++j) { - if (pcr[PC2[j]] !== 0) { - kn[m] |= 1 << (23 - j); - } - if (pcr[PC2[j + 24]] !== 0) { - kn[n] |= 1 << (23 - j); - } - } - } - - // cookey - for (let i = 0, rawi = 0, KnLi = 0; i < 16; ++i) { - const raw0 = kn[rawi++]; - const raw1 = kn[rawi++]; - this.keys[KnLi] = (raw0 & 0x00fc0000) << 6; - this.keys[KnLi] |= (raw0 & 0x00000fc0) << 10; - this.keys[KnLi] |= (raw1 & 0x00fc0000) >>> 10; - this.keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6; - ++KnLi; - this.keys[KnLi] = (raw0 & 0x0003f000) << 12; - this.keys[KnLi] |= (raw0 & 0x0000003f) << 16; - this.keys[KnLi] |= (raw1 & 0x0003f000) >>> 4; - this.keys[KnLi] |= (raw1 & 0x0000003f); - ++KnLi; - } - } - - // Encrypt 8 bytes of text - enc8(text) { - const b = text.slice(); - let i = 0, l, r, x; // left, right, accumulator - - // Squash 8 bytes to 2 ints - l = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++]; - r = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++]; - - x = ((l >>> 4) ^ r) & 0x0f0f0f0f; - r ^= x; - l ^= (x << 4); - x = ((l >>> 16) ^ r) & 0x0000ffff; - r ^= x; - l ^= (x << 16); - x = ((r >>> 2) ^ l) & 0x33333333; - l ^= x; - r ^= (x << 2); - x = ((r >>> 8) ^ l) & 0x00ff00ff; - l ^= x; - r ^= (x << 8); - r = (r << 1) | ((r >>> 31) & 1); - x = (l ^ r) & 0xaaaaaaaa; - l ^= x; - r ^= x; - l = (l << 1) | ((l >>> 31) & 1); - - for (let i = 0, keysi = 0; i < 8; ++i) { - x = (r << 28) | (r >>> 4); - x ^= this.keys[keysi++]; - let fval = SP7[x & 0x3f]; - fval |= SP5[(x >>> 8) & 0x3f]; - fval |= SP3[(x >>> 16) & 0x3f]; - fval |= SP1[(x >>> 24) & 0x3f]; - x = r ^ this.keys[keysi++]; - fval |= SP8[x & 0x3f]; - fval |= SP6[(x >>> 8) & 0x3f]; - fval |= SP4[(x >>> 16) & 0x3f]; - fval |= SP2[(x >>> 24) & 0x3f]; - l ^= fval; - x = (l << 28) | (l >>> 4); - x ^= this.keys[keysi++]; - fval = SP7[x & 0x3f]; - fval |= SP5[(x >>> 8) & 0x3f]; - fval |= SP3[(x >>> 16) & 0x3f]; - fval |= SP1[(x >>> 24) & 0x3f]; - x = l ^ this.keys[keysi++]; - fval |= SP8[x & 0x0000003f]; - fval |= SP6[(x >>> 8) & 0x3f]; - fval |= SP4[(x >>> 16) & 0x3f]; - fval |= SP2[(x >>> 24) & 0x3f]; - r ^= fval; - } - - r = (r << 31) | (r >>> 1); - x = (l ^ r) & 0xaaaaaaaa; - l ^= x; - r ^= x; - l = (l << 31) | (l >>> 1); - x = ((l >>> 8) ^ r) & 0x00ff00ff; - r ^= x; - l ^= (x << 8); - x = ((l >>> 2) ^ r) & 0x33333333; - r ^= x; - l ^= (x << 2); - x = ((r >>> 16) ^ l) & 0x0000ffff; - l ^= x; - r ^= (x << 16); - x = ((r >>> 4) ^ l) & 0x0f0f0f0f; - l ^= x; - r ^= (x << 4); - - // Spread ints to bytes - x = [r, l]; - for (i = 0; i < 8; i++) { - b[i] = (x[i>>>2] >>> (8 * (3 - (i % 4)))) % 256; - if (b[i] < 0) { b[i] += 256; } // unsigned - } - return b; - } -} - -export class DESECBCipher { - constructor() { - this._cipher = null; - } - - get algorithm() { - return { name: "DES-ECB" }; - } - - static importKey(key, _algorithm, _extractable, _keyUsages) { - const cipher = new DESECBCipher; - cipher._importKey(key); - return cipher; - } - - _importKey(key, _extractable, _keyUsages) { - this._cipher = new DES(key); - } - - encrypt(_algorithm, plaintext) { - const x = new Uint8Array(plaintext); - if (x.length % 8 !== 0 || this._cipher === null) { - return null; - } - const n = x.length / 8; - for (let i = 0; i < n; i++) { - x.set(this._cipher.enc8(x.slice(i * 8, i * 8 + 8)), i * 8); - } - return x; - } -} - -export class DESCBCCipher { - constructor() { - this._cipher = null; - } - - get algorithm() { - return { name: "DES-CBC" }; - } - - static importKey(key, _algorithm, _extractable, _keyUsages) { - const cipher = new DESCBCCipher; - cipher._importKey(key); - return cipher; - } - - _importKey(key) { - this._cipher = new DES(key); - } - - encrypt(algorithm, plaintext) { - const x = new Uint8Array(plaintext); - let y = new Uint8Array(algorithm.iv); - if (x.length % 8 !== 0 || this._cipher === null) { - return null; - } - const n = x.length / 8; - for (let i = 0; i < n; i++) { - for (let j = 0; j < 8; j++) { - y[j] ^= plaintext[i * 8 + j]; - } - y = this._cipher.enc8(y); - x.set(y, i * 8); - } - return x; - } -} diff --git a/base/app/novnc/core/crypto/dh.js b/base/app/novnc/core/crypto/dh.js deleted file mode 100644 index bd705d9..0000000 --- a/base/app/novnc/core/crypto/dh.js +++ /dev/null @@ -1,55 +0,0 @@ -import { modPow, bigIntToU8Array, u8ArrayToBigInt } from "./bigint.js"; - -class DHPublicKey { - constructor(key) { - this._key = key; - } - - get algorithm() { - return { name: "DH" }; - } - - exportKey() { - return this._key; - } -} - -export class DHCipher { - constructor() { - this._g = null; - this._p = null; - this._gBigInt = null; - this._pBigInt = null; - this._privateKey = null; - } - - get algorithm() { - return { name: "DH" }; - } - - static generateKey(algorithm, _extractable) { - const cipher = new DHCipher; - cipher._generateKey(algorithm); - return { privateKey: cipher, publicKey: new DHPublicKey(cipher._publicKey) }; - } - - _generateKey(algorithm) { - const g = algorithm.g; - const p = algorithm.p; - this._keyBytes = p.length; - this._gBigInt = u8ArrayToBigInt(g); - this._pBigInt = u8ArrayToBigInt(p); - this._privateKey = window.crypto.getRandomValues(new Uint8Array(this._keyBytes)); - this._privateKeyBigInt = u8ArrayToBigInt(this._privateKey); - this._publicKey = bigIntToU8Array(modPow( - this._gBigInt, this._privateKeyBigInt, this._pBigInt), this._keyBytes); - } - - deriveBits(algorithm, length) { - const bytes = Math.ceil(length / 8); - const pkey = new Uint8Array(algorithm.public); - const len = bytes > this._keyBytes ? bytes : this._keyBytes; - const secret = modPow(u8ArrayToBigInt(pkey), this._privateKeyBigInt, this._pBigInt); - return bigIntToU8Array(secret, len).slice(0, len); - } -} diff --git a/base/app/novnc/core/crypto/md5.js b/base/app/novnc/core/crypto/md5.js deleted file mode 100644 index fcfefff..0000000 --- a/base/app/novnc/core/crypto/md5.js +++ /dev/null @@ -1,82 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2021 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - */ - -/* - * Performs MD5 hashing on an array of bytes, returns an array of bytes - */ - -export async function MD5(d) { - let s = ""; - for (let i = 0; i < d.length; i++) { - s += String.fromCharCode(d[i]); - } - return M(V(Y(X(s), 8 * s.length))); -} - -function M(d) { - let f = new Uint8Array(d.length); - for (let i=0;i<d.length;i++) { - f[i] = d.charCodeAt(i); - } - return f; -} - -function X(d) { - let r = Array(d.length >> 2); - for (let m = 0; m < r.length; m++) r[m] = 0; - for (let m = 0; m < 8 * d.length; m += 8) r[m >> 5] |= (255 & d.charCodeAt(m / 8)) << m % 32; - return r; -} - -function V(d) { - let r = ""; - for (let m = 0; m < 32 * d.length; m += 8) r += String.fromCharCode(d[m >> 5] >>> m % 32 & 255); - return r; -} - -function Y(d, g) { - d[g >> 5] |= 128 << g % 32, d[14 + (g + 64 >>> 9 << 4)] = g; - let m = 1732584193, f = -271733879, r = -1732584194, i = 271733878; - for (let n = 0; n < d.length; n += 16) { - let h = m, - t = f, - g = r, - e = i; - f = ii(f = ii(f = ii(f = ii(f = hh(f = hh(f = hh(f = hh(f = gg(f = gg(f = gg(f = gg(f = ff(f = ff(f = ff(f = ff(f, r = ff(r, i = ff(i, m = ff(m, f, r, i, d[n + 0], 7, -680876936), f, r, d[n + 1], 12, -389564586), m, f, d[n + 2], 17, 606105819), i, m, d[n + 3], 22, -1044525330), r = ff(r, i = ff(i, m = ff(m, f, r, i, d[n + 4], 7, -176418897), f, r, d[n + 5], 12, 1200080426), m, f, d[n + 6], 17, -1473231341), i, m, d[n + 7], 22, -45705983), r = ff(r, i = ff(i, m = ff(m, f, r, i, d[n + 8], 7, 1770035416), f, r, d[n + 9], 12, -1958414417), m, f, d[n + 10], 17, -42063), i, m, d[n + 11], 22, -1990404162), r = ff(r, i = ff(i, m = ff(m, f, r, i, d[n + 12], 7, 1804603682), f, r, d[n + 13], 12, -40341101), m, f, d[n + 14], 17, -1502002290), i, m, d[n + 15], 22, 1236535329), r = gg(r, i = gg(i, m = gg(m, f, r, i, d[n + 1], 5, -165796510), f, r, d[n + 6], 9, -1069501632), m, f, d[n + 11], 14, 643717713), i, m, d[n + 0], 20, -373897302), r = gg(r, i = gg(i, m = gg(m, f, r, i, d[n + 5], 5, -701558691), f, r, d[n + 10], 9, 38016083), m, f, d[n + 15], 14, -660478335), i, m, d[n + 4], 20, -405537848), r = gg(r, i = gg(i, m = gg(m, f, r, i, d[n + 9], 5, 568446438), f, r, d[n + 14], 9, -1019803690), m, f, d[n + 3], 14, -187363961), i, m, d[n + 8], 20, 1163531501), r = gg(r, i = gg(i, m = gg(m, f, r, i, d[n + 13], 5, -1444681467), f, r, d[n + 2], 9, -51403784), m, f, d[n + 7], 14, 1735328473), i, m, d[n + 12], 20, -1926607734), r = hh(r, i = hh(i, m = hh(m, f, r, i, d[n + 5], 4, -378558), f, r, d[n + 8], 11, -2022574463), m, f, d[n + 11], 16, 1839030562), i, m, d[n + 14], 23, -35309556), r = hh(r, i = hh(i, m = hh(m, f, r, i, d[n + 1], 4, -1530992060), f, r, d[n + 4], 11, 1272893353), m, f, d[n + 7], 16, -155497632), i, m, d[n + 10], 23, -1094730640), r = hh(r, i = hh(i, m = hh(m, f, r, i, d[n + 13], 4, 681279174), f, r, d[n + 0], 11, -358537222), m, f, d[n + 3], 16, -722521979), i, m, d[n + 6], 23, 76029189), r = hh(r, i = hh(i, m = hh(m, f, r, i, d[n + 9], 4, -640364487), f, r, d[n + 12], 11, -421815835), m, f, d[n + 15], 16, 530742520), i, m, d[n + 2], 23, -995338651), r = ii(r, i = ii(i, m = ii(m, f, r, i, d[n + 0], 6, -198630844), f, r, d[n + 7], 10, 1126891415), m, f, d[n + 14], 15, -1416354905), i, m, d[n + 5], 21, -57434055), r = ii(r, i = ii(i, m = ii(m, f, r, i, d[n + 12], 6, 1700485571), f, r, d[n + 3], 10, -1894986606), m, f, d[n + 10], 15, -1051523), i, m, d[n + 1], 21, -2054922799), r = ii(r, i = ii(i, m = ii(m, f, r, i, d[n + 8], 6, 1873313359), f, r, d[n + 15], 10, -30611744), m, f, d[n + 6], 15, -1560198380), i, m, d[n + 13], 21, 1309151649), r = ii(r, i = ii(i, m = ii(m, f, r, i, d[n + 4], 6, -145523070), f, r, d[n + 11], 10, -1120210379), m, f, d[n + 2], 15, 718787259), i, m, d[n + 9], 21, -343485551), m = add(m, h), f = add(f, t), r = add(r, g), i = add(i, e); - } - return Array(m, f, r, i); -} - -function cmn(d, g, m, f, r, i) { - return add(rol(add(add(g, d), add(f, i)), r), m); -} - -function ff(d, g, m, f, r, i, n) { - return cmn(g & m | ~g & f, d, g, r, i, n); -} - -function gg(d, g, m, f, r, i, n) { - return cmn(g & f | m & ~f, d, g, r, i, n); -} - -function hh(d, g, m, f, r, i, n) { - return cmn(g ^ m ^ f, d, g, r, i, n); -} - -function ii(d, g, m, f, r, i, n) { - return cmn(m ^ (g | ~f), d, g, r, i, n); -} - -function add(d, g) { - let m = (65535 & d) + (65535 & g); - return (d >> 16) + (g >> 16) + (m >> 16) << 16 | 65535 & m; -} - -function rol(d, g) { - return d << g | d >>> 32 - g; -} diff --git a/base/app/novnc/core/crypto/rsa.js b/base/app/novnc/core/crypto/rsa.js deleted file mode 100644 index 68e8e86..0000000 --- a/base/app/novnc/core/crypto/rsa.js +++ /dev/null @@ -1,132 +0,0 @@ -import Base64 from "../base64.js"; -import { modPow, bigIntToU8Array, u8ArrayToBigInt } from "./bigint.js"; - -export class RSACipher { - constructor() { - this._keyLength = 0; - this._keyBytes = 0; - this._n = null; - this._e = null; - this._d = null; - this._nBigInt = null; - this._eBigInt = null; - this._dBigInt = null; - this._extractable = false; - } - - get algorithm() { - return { name: "RSA-PKCS1-v1_5" }; - } - - _base64urlDecode(data) { - data = data.replace(/-/g, "+").replace(/_/g, "/"); - data = data.padEnd(Math.ceil(data.length / 4) * 4, "="); - return Base64.decode(data); - } - - _padArray(arr, length) { - const res = new Uint8Array(length); - res.set(arr, length - arr.length); - return res; - } - - static async generateKey(algorithm, extractable, _keyUsages) { - const cipher = new RSACipher; - await cipher._generateKey(algorithm, extractable); - return { privateKey: cipher }; - } - - async _generateKey(algorithm, extractable) { - this._keyLength = algorithm.modulusLength; - this._keyBytes = Math.ceil(this._keyLength / 8); - const key = await window.crypto.subtle.generateKey( - { - name: "RSA-OAEP", - modulusLength: algorithm.modulusLength, - publicExponent: algorithm.publicExponent, - hash: {name: "SHA-256"}, - }, - true, ["encrypt", "decrypt"]); - const privateKey = await window.crypto.subtle.exportKey("jwk", key.privateKey); - this._n = this._padArray(this._base64urlDecode(privateKey.n), this._keyBytes); - this._nBigInt = u8ArrayToBigInt(this._n); - this._e = this._padArray(this._base64urlDecode(privateKey.e), this._keyBytes); - this._eBigInt = u8ArrayToBigInt(this._e); - this._d = this._padArray(this._base64urlDecode(privateKey.d), this._keyBytes); - this._dBigInt = u8ArrayToBigInt(this._d); - this._extractable = extractable; - } - - static async importKey(key, _algorithm, extractable, keyUsages) { - if (keyUsages.length !== 1 || keyUsages[0] !== "encrypt") { - throw new Error("only support importing RSA public key"); - } - const cipher = new RSACipher; - await cipher._importKey(key, extractable); - return cipher; - } - - async _importKey(key, extractable) { - const n = key.n; - const e = key.e; - if (n.length !== e.length) { - throw new Error("the sizes of modulus and public exponent do not match"); - } - this._keyBytes = n.length; - this._keyLength = this._keyBytes * 8; - this._n = new Uint8Array(this._keyBytes); - this._e = new Uint8Array(this._keyBytes); - this._n.set(n); - this._e.set(e); - this._nBigInt = u8ArrayToBigInt(this._n); - this._eBigInt = u8ArrayToBigInt(this._e); - this._extractable = extractable; - } - - async encrypt(_algorithm, message) { - if (message.length > this._keyBytes - 11) { - return null; - } - const ps = new Uint8Array(this._keyBytes - message.length - 3); - window.crypto.getRandomValues(ps); - for (let i = 0; i < ps.length; i++) { - ps[i] = Math.floor(ps[i] * 254 / 255 + 1); - } - const em = new Uint8Array(this._keyBytes); - em[1] = 0x02; - em.set(ps, 2); - em.set(message, ps.length + 3); - const emBigInt = u8ArrayToBigInt(em); - const c = modPow(emBigInt, this._eBigInt, this._nBigInt); - return bigIntToU8Array(c, this._keyBytes); - } - - async decrypt(_algorithm, message) { - if (message.length !== this._keyBytes) { - return null; - } - const msgBigInt = u8ArrayToBigInt(message); - const emBigInt = modPow(msgBigInt, this._dBigInt, this._nBigInt); - const em = bigIntToU8Array(emBigInt, this._keyBytes); - if (em[0] !== 0x00 || em[1] !== 0x02) { - return null; - } - let i = 2; - for (; i < em.length; i++) { - if (em[i] === 0x00) { - break; - } - } - if (i === em.length) { - return null; - } - return em.slice(i + 1, em.length); - } - - async exportKey() { - if (!this._extractable) { - throw new Error("key is not extractable"); - } - return { n: this._n, e: this._e, d: this._d }; - } -} diff --git a/base/app/novnc/core/decoders/copyrect.js b/base/app/novnc/core/decoders/copyrect.js deleted file mode 100644 index 9e6391a..0000000 --- a/base/app/novnc/core/decoders/copyrect.js +++ /dev/null @@ -1,27 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2019 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - * - */ - -export default class CopyRectDecoder { - decodeRect(x, y, width, height, sock, display, depth) { - if (sock.rQwait("COPYRECT", 4)) { - return false; - } - - let deltaX = sock.rQshift16(); - let deltaY = sock.rQshift16(); - - if ((width === 0) || (height === 0)) { - return true; - } - - display.copyImage(deltaX, deltaY, x, y, width, height); - - return true; - } -} diff --git a/base/app/novnc/core/decoders/hextile.js b/base/app/novnc/core/decoders/hextile.js deleted file mode 100644 index cc33e0e..0000000 --- a/base/app/novnc/core/decoders/hextile.js +++ /dev/null @@ -1,181 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2019 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - * - */ - -import * as Log from '../util/logging.js'; - -export default class HextileDecoder { - constructor() { - this._tiles = 0; - this._lastsubencoding = 0; - this._tileBuffer = new Uint8Array(16 * 16 * 4); - } - - decodeRect(x, y, width, height, sock, display, depth) { - if (this._tiles === 0) { - this._tilesX = Math.ceil(width / 16); - this._tilesY = Math.ceil(height / 16); - this._totalTiles = this._tilesX * this._tilesY; - this._tiles = this._totalTiles; - } - - while (this._tiles > 0) { - let bytes = 1; - - if (sock.rQwait("HEXTILE", bytes)) { - return false; - } - - let subencoding = sock.rQpeek8(); - if (subencoding > 30) { // Raw - throw new Error("Illegal hextile subencoding (subencoding: " + - subencoding + ")"); - } - - const currTile = this._totalTiles - this._tiles; - const tileX = currTile % this._tilesX; - const tileY = Math.floor(currTile / this._tilesX); - const tx = x + tileX * 16; - const ty = y + tileY * 16; - const tw = Math.min(16, (x + width) - tx); - const th = Math.min(16, (y + height) - ty); - - // Figure out how much we are expecting - if (subencoding & 0x01) { // Raw - bytes += tw * th * 4; - } else { - if (subencoding & 0x02) { // Background - bytes += 4; - } - if (subencoding & 0x04) { // Foreground - bytes += 4; - } - if (subencoding & 0x08) { // AnySubrects - bytes++; // Since we aren't shifting it off - - if (sock.rQwait("HEXTILE", bytes)) { - return false; - } - - let subrects = sock.rQpeekBytes(bytes).at(-1); - if (subencoding & 0x10) { // SubrectsColoured - bytes += subrects * (4 + 2); - } else { - bytes += subrects * 2; - } - } - } - - if (sock.rQwait("HEXTILE", bytes)) { - return false; - } - - // We know the encoding and have a whole tile - sock.rQshift8(); - if (subencoding === 0) { - if (this._lastsubencoding & 0x01) { - // Weird: ignore blanks are RAW - Log.Debug(" Ignoring blank after RAW"); - } else { - display.fillRect(tx, ty, tw, th, this._background); - } - } else if (subencoding & 0x01) { // Raw - let pixels = tw * th; - let data = sock.rQshiftBytes(pixels * 4, false); - // Max sure the image is fully opaque - for (let i = 0;i < pixels;i++) { - data[i * 4 + 3] = 255; - } - display.blitImage(tx, ty, tw, th, data, 0); - } else { - if (subencoding & 0x02) { // Background - this._background = new Uint8Array(sock.rQshiftBytes(4)); - } - if (subencoding & 0x04) { // Foreground - this._foreground = new Uint8Array(sock.rQshiftBytes(4)); - } - - this._startTile(tx, ty, tw, th, this._background); - if (subencoding & 0x08) { // AnySubrects - let subrects = sock.rQshift8(); - - for (let s = 0; s < subrects; s++) { - let color; - if (subencoding & 0x10) { // SubrectsColoured - color = sock.rQshiftBytes(4); - } else { - color = this._foreground; - } - const xy = sock.rQshift8(); - const sx = (xy >> 4); - const sy = (xy & 0x0f); - - const wh = sock.rQshift8(); - const sw = (wh >> 4) + 1; - const sh = (wh & 0x0f) + 1; - - this._subTile(sx, sy, sw, sh, color); - } - } - this._finishTile(display); - } - this._lastsubencoding = subencoding; - this._tiles--; - } - - return true; - } - - // start updating a tile - _startTile(x, y, width, height, color) { - this._tileX = x; - this._tileY = y; - this._tileW = width; - this._tileH = height; - - const red = color[0]; - const green = color[1]; - const blue = color[2]; - - const data = this._tileBuffer; - for (let i = 0; i < width * height * 4; i += 4) { - data[i] = red; - data[i + 1] = green; - data[i + 2] = blue; - data[i + 3] = 255; - } - } - - // update sub-rectangle of the current tile - _subTile(x, y, w, h, color) { - const red = color[0]; - const green = color[1]; - const blue = color[2]; - const xend = x + w; - const yend = y + h; - - const data = this._tileBuffer; - const width = this._tileW; - for (let j = y; j < yend; j++) { - for (let i = x; i < xend; i++) { - const p = (i + (j * width)) * 4; - data[p] = red; - data[p + 1] = green; - data[p + 2] = blue; - data[p + 3] = 255; - } - } - } - - // draw the current tile to the screen - _finishTile(display) { - display.blitImage(this._tileX, this._tileY, - this._tileW, this._tileH, - this._tileBuffer, 0); - } -} diff --git a/base/app/novnc/core/decoders/jpeg.js b/base/app/novnc/core/decoders/jpeg.js deleted file mode 100644 index feb2aeb..0000000 --- a/base/app/novnc/core/decoders/jpeg.js +++ /dev/null @@ -1,146 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2019 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - * - */ - -export default class JPEGDecoder { - constructor() { - // RealVNC will reuse the quantization tables - // and Huffman tables, so we need to cache them. - this._cachedQuantTables = []; - this._cachedHuffmanTables = []; - - this._segments = []; - } - - decodeRect(x, y, width, height, sock, display, depth) { - // A rect of JPEG encodings is simply a JPEG file - while (true) { - let segment = this._readSegment(sock); - if (segment === null) { - return false; - } - this._segments.push(segment); - // End of image? - if (segment[1] === 0xD9) { - break; - } - } - - let huffmanTables = []; - let quantTables = []; - for (let segment of this._segments) { - let type = segment[1]; - if (type === 0xC4) { - // Huffman tables - huffmanTables.push(segment); - } else if (type === 0xDB) { - // Quantization tables - quantTables.push(segment); - } - } - - const sofIndex = this._segments.findIndex( - x => x[1] == 0xC0 || x[1] == 0xC2 - ); - if (sofIndex == -1) { - throw new Error("Illegal JPEG image without SOF"); - } - - if (quantTables.length === 0) { - this._segments.splice(sofIndex+1, 0, - ...this._cachedQuantTables); - } - if (huffmanTables.length === 0) { - this._segments.splice(sofIndex+1, 0, - ...this._cachedHuffmanTables); - } - - let length = 0; - for (let segment of this._segments) { - length += segment.length; - } - - let data = new Uint8Array(length); - length = 0; - for (let segment of this._segments) { - data.set(segment, length); - length += segment.length; - } - - display.imageRect(x, y, width, height, "image/jpeg", data); - - if (huffmanTables.length !== 0) { - this._cachedHuffmanTables = huffmanTables; - } - if (quantTables.length !== 0) { - this._cachedQuantTables = quantTables; - } - - this._segments = []; - - return true; - } - - _readSegment(sock) { - if (sock.rQwait("JPEG", 2)) { - return null; - } - - let marker = sock.rQshift8(); - if (marker != 0xFF) { - throw new Error("Illegal JPEG marker received (byte: " + - marker + ")"); - } - let type = sock.rQshift8(); - if (type >= 0xD0 && type <= 0xD9 || type == 0x01) { - // No length after marker - return new Uint8Array([marker, type]); - } - - if (sock.rQwait("JPEG", 2, 2)) { - return null; - } - - let length = sock.rQshift16(); - if (length < 2) { - throw new Error("Illegal JPEG length received (length: " + - length + ")"); - } - - if (sock.rQwait("JPEG", length-2, 4)) { - return null; - } - - let extra = 0; - if (type === 0xDA) { - // start of scan - extra += 2; - while (true) { - if (sock.rQwait("JPEG", length-2+extra, 4)) { - return null; - } - let data = sock.rQpeekBytes(length-2+extra, false); - if (data.at(-2) === 0xFF && data.at(-1) !== 0x00 && - !(data.at(-1) >= 0xD0 && data.at(-1) <= 0xD7)) { - extra -= 2; - break; - } - extra++; - } - } - - let segment = new Uint8Array(2 + length + extra); - segment[0] = marker; - segment[1] = type; - segment[2] = length >> 8; - segment[3] = length; - segment.set(sock.rQshiftBytes(length-2+extra, false), 4); - - return segment; - } -} diff --git a/base/app/novnc/core/decoders/raw.js b/base/app/novnc/core/decoders/raw.js deleted file mode 100644 index 3c16614..0000000 --- a/base/app/novnc/core/decoders/raw.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2019 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - * - */ - -export default class RawDecoder { - constructor() { - this._lines = 0; - } - - decodeRect(x, y, width, height, sock, display, depth) { - if ((width === 0) || (height === 0)) { - return true; - } - - if (this._lines === 0) { - this._lines = height; - } - - const pixelSize = depth == 8 ? 1 : 4; - const bytesPerLine = width * pixelSize; - - while (this._lines > 0) { - if (sock.rQwait("RAW", bytesPerLine)) { - return false; - } - - const curY = y + (height - this._lines); - - let data = sock.rQshiftBytes(bytesPerLine, false); - - // Convert data if needed - if (depth == 8) { - const newdata = new Uint8Array(width * 4); - for (let i = 0; i < width; i++) { - newdata[i * 4 + 0] = ((data[i] >> 0) & 0x3) * 255 / 3; - newdata[i * 4 + 1] = ((data[i] >> 2) & 0x3) * 255 / 3; - newdata[i * 4 + 2] = ((data[i] >> 4) & 0x3) * 255 / 3; - newdata[i * 4 + 3] = 255; - } - data = newdata; - } - - // Max sure the image is fully opaque - for (let i = 0; i < width; i++) { - data[i * 4 + 3] = 255; - } - - display.blitImage(x, curY, width, 1, data, 0); - this._lines--; - } - - return true; - } -} diff --git a/base/app/novnc/core/decoders/rre.js b/base/app/novnc/core/decoders/rre.js deleted file mode 100644 index 6219369..0000000 --- a/base/app/novnc/core/decoders/rre.js +++ /dev/null @@ -1,44 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2019 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - * - */ - -export default class RREDecoder { - constructor() { - this._subrects = 0; - } - - decodeRect(x, y, width, height, sock, display, depth) { - if (this._subrects === 0) { - if (sock.rQwait("RRE", 4 + 4)) { - return false; - } - - this._subrects = sock.rQshift32(); - - let color = sock.rQshiftBytes(4); // Background - display.fillRect(x, y, width, height, color); - } - - while (this._subrects > 0) { - if (sock.rQwait("RRE", 4 + 8)) { - return false; - } - - let color = sock.rQshiftBytes(4); - let sx = sock.rQshift16(); - let sy = sock.rQshift16(); - let swidth = sock.rQshift16(); - let sheight = sock.rQshift16(); - display.fillRect(x + sx, y + sy, swidth, sheight, color); - - this._subrects--; - } - - return true; - } -} diff --git a/base/app/novnc/core/decoders/tight.js b/base/app/novnc/core/decoders/tight.js deleted file mode 100644 index 8bc977a..0000000 --- a/base/app/novnc/core/decoders/tight.js +++ /dev/null @@ -1,393 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2019 The noVNC Authors - * (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca) - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - * - */ - -import * as Log from '../util/logging.js'; -import Inflator from "../inflator.js"; - -export default class TightDecoder { - constructor() { - this._ctl = null; - this._filter = null; - this._numColors = 0; - this._palette = new Uint8Array(1024); // 256 * 4 (max palette size * max bytes-per-pixel) - this._len = 0; - - this._zlibs = []; - for (let i = 0; i < 4; i++) { - this._zlibs[i] = new Inflator(); - } - } - - decodeRect(x, y, width, height, sock, display, depth) { - if (this._ctl === null) { - if (sock.rQwait("TIGHT compression-control", 1)) { - return false; - } - - this._ctl = sock.rQshift8(); - - // Reset streams if the server requests it - for (let i = 0; i < 4; i++) { - if ((this._ctl >> i) & 1) { - this._zlibs[i].reset(); - Log.Info("Reset zlib stream " + i); - } - } - - // Figure out filter - this._ctl = this._ctl >> 4; - } - - let ret; - - if (this._ctl === 0x08) { - ret = this._fillRect(x, y, width, height, - sock, display, depth); - } else if (this._ctl === 0x09) { - ret = this._jpegRect(x, y, width, height, - sock, display, depth); - } else if (this._ctl === 0x0A) { - ret = this._pngRect(x, y, width, height, - sock, display, depth); - } else if ((this._ctl & 0x08) == 0) { - ret = this._basicRect(this._ctl, x, y, width, height, - sock, display, depth); - } else { - throw new Error("Illegal tight compression received (ctl: " + - this._ctl + ")"); - } - - if (ret) { - this._ctl = null; - } - - return ret; - } - - _fillRect(x, y, width, height, sock, display, depth) { - if (sock.rQwait("TIGHT", 3)) { - return false; - } - - let pixel = sock.rQshiftBytes(3); - display.fillRect(x, y, width, height, pixel, false); - - return true; - } - - _jpegRect(x, y, width, height, sock, display, depth) { - let data = this._readData(sock); - if (data === null) { - return false; - } - - display.imageRect(x, y, width, height, "image/jpeg", data); - - return true; - } - - _pngRect(x, y, width, height, sock, display, depth) { - throw new Error("PNG received in standard Tight rect"); - } - - _basicRect(ctl, x, y, width, height, sock, display, depth) { - if (this._filter === null) { - if (ctl & 0x4) { - if (sock.rQwait("TIGHT", 1)) { - return false; - } - - this._filter = sock.rQshift8(); - } else { - // Implicit CopyFilter - this._filter = 0; - } - } - - let streamId = ctl & 0x3; - - let ret; - - switch (this._filter) { - case 0: // CopyFilter - ret = this._copyFilter(streamId, x, y, width, height, - sock, display, depth); - break; - case 1: // PaletteFilter - ret = this._paletteFilter(streamId, x, y, width, height, - sock, display, depth); - break; - case 2: // GradientFilter - ret = this._gradientFilter(streamId, x, y, width, height, - sock, display, depth); - break; - default: - throw new Error("Illegal tight filter received (ctl: " + - this._filter + ")"); - } - - if (ret) { - this._filter = null; - } - - return ret; - } - - _copyFilter(streamId, x, y, width, height, sock, display, depth) { - const uncompressedSize = width * height * 3; - let data; - - if (uncompressedSize === 0) { - return true; - } - - if (uncompressedSize < 12) { - if (sock.rQwait("TIGHT", uncompressedSize)) { - return false; - } - - data = sock.rQshiftBytes(uncompressedSize); - } else { - data = this._readData(sock); - if (data === null) { - return false; - } - - this._zlibs[streamId].setInput(data); - data = this._zlibs[streamId].inflate(uncompressedSize); - this._zlibs[streamId].setInput(null); - } - - let rgbx = new Uint8Array(width * height * 4); - for (let i = 0, j = 0; i < width * height * 4; i += 4, j += 3) { - rgbx[i] = data[j]; - rgbx[i + 1] = data[j + 1]; - rgbx[i + 2] = data[j + 2]; - rgbx[i + 3] = 255; // Alpha - } - - display.blitImage(x, y, width, height, rgbx, 0, false); - - return true; - } - - _paletteFilter(streamId, x, y, width, height, sock, display, depth) { - if (this._numColors === 0) { - if (sock.rQwait("TIGHT palette", 1)) { - return false; - } - - const numColors = sock.rQpeek8() + 1; - const paletteSize = numColors * 3; - - if (sock.rQwait("TIGHT palette", 1 + paletteSize)) { - return false; - } - - this._numColors = numColors; - sock.rQskipBytes(1); - - sock.rQshiftTo(this._palette, paletteSize); - } - - const bpp = (this._numColors <= 2) ? 1 : 8; - const rowSize = Math.floor((width * bpp + 7) / 8); - const uncompressedSize = rowSize * height; - - let data; - - if (uncompressedSize === 0) { - return true; - } - - if (uncompressedSize < 12) { - if (sock.rQwait("TIGHT", uncompressedSize)) { - return false; - } - - data = sock.rQshiftBytes(uncompressedSize); - } else { - data = this._readData(sock); - if (data === null) { - return false; - } - - this._zlibs[streamId].setInput(data); - data = this._zlibs[streamId].inflate(uncompressedSize); - this._zlibs[streamId].setInput(null); - } - - // Convert indexed (palette based) image data to RGB - if (this._numColors == 2) { - this._monoRect(x, y, width, height, data, this._palette, display); - } else { - this._paletteRect(x, y, width, height, data, this._palette, display); - } - - this._numColors = 0; - - return true; - } - - _monoRect(x, y, width, height, data, palette, display) { - // Convert indexed (palette based) image data to RGB - // TODO: reduce number of calculations inside loop - const dest = this._getScratchBuffer(width * height * 4); - const w = Math.floor((width + 7) / 8); - const w1 = Math.floor(width / 8); - - for (let y = 0; y < height; y++) { - let dp, sp, x; - for (x = 0; x < w1; x++) { - for (let b = 7; b >= 0; b--) { - dp = (y * width + x * 8 + 7 - b) * 4; - sp = (data[y * w + x] >> b & 1) * 3; - dest[dp] = palette[sp]; - dest[dp + 1] = palette[sp + 1]; - dest[dp + 2] = palette[sp + 2]; - dest[dp + 3] = 255; - } - } - - for (let b = 7; b >= 8 - width % 8; b--) { - dp = (y * width + x * 8 + 7 - b) * 4; - sp = (data[y * w + x] >> b & 1) * 3; - dest[dp] = palette[sp]; - dest[dp + 1] = palette[sp + 1]; - dest[dp + 2] = palette[sp + 2]; - dest[dp + 3] = 255; - } - } - - display.blitImage(x, y, width, height, dest, 0, false); - } - - _paletteRect(x, y, width, height, data, palette, display) { - // Convert indexed (palette based) image data to RGB - const dest = this._getScratchBuffer(width * height * 4); - const total = width * height * 4; - for (let i = 0, j = 0; i < total; i += 4, j++) { - const sp = data[j] * 3; - dest[i] = palette[sp]; - dest[i + 1] = palette[sp + 1]; - dest[i + 2] = palette[sp + 2]; - dest[i + 3] = 255; - } - - display.blitImage(x, y, width, height, dest, 0, false); - } - - _gradientFilter(streamId, x, y, width, height, sock, display, depth) { - // assume the TPIXEL is 3 bytes long - const uncompressedSize = width * height * 3; - let data; - - if (uncompressedSize === 0) { - return true; - } - - if (uncompressedSize < 12) { - if (sock.rQwait("TIGHT", uncompressedSize)) { - return false; - } - - data = sock.rQshiftBytes(uncompressedSize); - } else { - data = this._readData(sock); - if (data === null) { - return false; - } - - this._zlibs[streamId].setInput(data); - data = this._zlibs[streamId].inflate(uncompressedSize); - this._zlibs[streamId].setInput(null); - } - - let rgbx = new Uint8Array(4 * width * height); - - let rgbxIndex = 0, dataIndex = 0; - let left = new Uint8Array(3); - for (let x = 0; x < width; x++) { - for (let c = 0; c < 3; c++) { - const prediction = left[c]; - const value = data[dataIndex++] + prediction; - rgbx[rgbxIndex++] = value; - left[c] = value; - } - rgbx[rgbxIndex++] = 255; - } - - let upperIndex = 0; - let upper = new Uint8Array(3), - upperleft = new Uint8Array(3); - for (let y = 1; y < height; y++) { - left.fill(0); - upperleft.fill(0); - for (let x = 0; x < width; x++) { - for (let c = 0; c < 3; c++) { - upper[c] = rgbx[upperIndex++]; - let prediction = left[c] + upper[c] - upperleft[c]; - if (prediction < 0) { - prediction = 0; - } else if (prediction > 255) { - prediction = 255; - } - const value = data[dataIndex++] + prediction; - rgbx[rgbxIndex++] = value; - upperleft[c] = upper[c]; - left[c] = value; - } - rgbx[rgbxIndex++] = 255; - upperIndex++; - } - } - - display.blitImage(x, y, width, height, rgbx, 0, false); - - return true; - } - - _readData(sock) { - if (this._len === 0) { - if (sock.rQwait("TIGHT", 3)) { - return null; - } - - let byte; - - byte = sock.rQshift8(); - this._len = byte & 0x7f; - if (byte & 0x80) { - byte = sock.rQshift8(); - this._len |= (byte & 0x7f) << 7; - if (byte & 0x80) { - byte = sock.rQshift8(); - this._len |= byte << 14; - } - } - } - - if (sock.rQwait("TIGHT", this._len)) { - return null; - } - - let data = sock.rQshiftBytes(this._len, false); - this._len = 0; - - return data; - } - - _getScratchBuffer(size) { - if (!this._scratchBuffer || (this._scratchBuffer.length < size)) { - this._scratchBuffer = new Uint8Array(size); - } - return this._scratchBuffer; - } -} diff --git a/base/app/novnc/core/decoders/tightpng.js b/base/app/novnc/core/decoders/tightpng.js deleted file mode 100644 index 82f492d..0000000 --- a/base/app/novnc/core/decoders/tightpng.js +++ /dev/null @@ -1,27 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2019 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - * - */ - -import TightDecoder from './tight.js'; - -export default class TightPNGDecoder extends TightDecoder { - _pngRect(x, y, width, height, sock, display, depth) { - let data = this._readData(sock); - if (data === null) { - return false; - } - - display.imageRect(x, y, width, height, "image/png", data); - - return true; - } - - _basicRect(ctl, x, y, width, height, sock, display, depth) { - throw new Error("BasicCompression received in TightPNG rect"); - } -} diff --git a/base/app/novnc/core/decoders/zrle.js b/base/app/novnc/core/decoders/zrle.js deleted file mode 100644 index 49128e7..0000000 --- a/base/app/novnc/core/decoders/zrle.js +++ /dev/null @@ -1,185 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2021 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - * - */ - -import Inflate from "../inflator.js"; - -const ZRLE_TILE_WIDTH = 64; -const ZRLE_TILE_HEIGHT = 64; - -export default class ZRLEDecoder { - constructor() { - this._length = 0; - this._inflator = new Inflate(); - - this._pixelBuffer = new Uint8Array(ZRLE_TILE_WIDTH * ZRLE_TILE_HEIGHT * 4); - this._tileBuffer = new Uint8Array(ZRLE_TILE_WIDTH * ZRLE_TILE_HEIGHT * 4); - } - - decodeRect(x, y, width, height, sock, display, depth) { - if (this._length === 0) { - if (sock.rQwait("ZLib data length", 4)) { - return false; - } - this._length = sock.rQshift32(); - } - if (sock.rQwait("Zlib data", this._length)) { - return false; - } - - const data = sock.rQshiftBytes(this._length, false); - - this._inflator.setInput(data); - - for (let ty = y; ty < y + height; ty += ZRLE_TILE_HEIGHT) { - let th = Math.min(ZRLE_TILE_HEIGHT, y + height - ty); - - for (let tx = x; tx < x + width; tx += ZRLE_TILE_WIDTH) { - let tw = Math.min(ZRLE_TILE_WIDTH, x + width - tx); - - const tileSize = tw * th; - const subencoding = this._inflator.inflate(1)[0]; - if (subencoding === 0) { - // raw data - const data = this._readPixels(tileSize); - display.blitImage(tx, ty, tw, th, data, 0, false); - } else if (subencoding === 1) { - // solid - const background = this._readPixels(1); - display.fillRect(tx, ty, tw, th, [background[0], background[1], background[2]]); - } else if (subencoding >= 2 && subencoding <= 16) { - const data = this._decodePaletteTile(subencoding, tileSize, tw, th); - display.blitImage(tx, ty, tw, th, data, 0, false); - } else if (subencoding === 128) { - const data = this._decodeRLETile(tileSize); - display.blitImage(tx, ty, tw, th, data, 0, false); - } else if (subencoding >= 130 && subencoding <= 255) { - const data = this._decodeRLEPaletteTile(subencoding - 128, tileSize); - display.blitImage(tx, ty, tw, th, data, 0, false); - } else { - throw new Error('Unknown subencoding: ' + subencoding); - } - } - } - this._length = 0; - return true; - } - - _getBitsPerPixelInPalette(paletteSize) { - if (paletteSize <= 2) { - return 1; - } else if (paletteSize <= 4) { - return 2; - } else if (paletteSize <= 16) { - return 4; - } - } - - _readPixels(pixels) { - let data = this._pixelBuffer; - const buffer = this._inflator.inflate(3*pixels); - for (let i = 0, j = 0; i < pixels*4; i += 4, j += 3) { - data[i] = buffer[j]; - data[i + 1] = buffer[j + 1]; - data[i + 2] = buffer[j + 2]; - data[i + 3] = 255; // Add the Alpha - } - return data; - } - - _decodePaletteTile(paletteSize, tileSize, tilew, tileh) { - const data = this._tileBuffer; - const palette = this._readPixels(paletteSize); - const bitsPerPixel = this._getBitsPerPixelInPalette(paletteSize); - const mask = (1 << bitsPerPixel) - 1; - - let offset = 0; - let encoded = this._inflator.inflate(1)[0]; - - for (let y=0; y<tileh; y++) { - let shift = 8-bitsPerPixel; - for (let x=0; x<tilew; x++) { - if (shift<0) { - shift=8-bitsPerPixel; - encoded = this._inflator.inflate(1)[0]; - } - let indexInPalette = (encoded>>shift) & mask; - - data[offset] = palette[indexInPalette * 4]; - data[offset + 1] = palette[indexInPalette * 4 + 1]; - data[offset + 2] = palette[indexInPalette * 4 + 2]; - data[offset + 3] = palette[indexInPalette * 4 + 3]; - offset += 4; - shift-=bitsPerPixel; - } - if (shift<8-bitsPerPixel && y<tileh-1) { - encoded = this._inflator.inflate(1)[0]; - } - } - return data; - } - - _decodeRLETile(tileSize) { - const data = this._tileBuffer; - let i = 0; - while (i < tileSize) { - const pixel = this._readPixels(1); - const length = this._readRLELength(); - for (let j = 0; j < length; j++) { - data[i * 4] = pixel[0]; - data[i * 4 + 1] = pixel[1]; - data[i * 4 + 2] = pixel[2]; - data[i * 4 + 3] = pixel[3]; - i++; - } - } - return data; - } - - _decodeRLEPaletteTile(paletteSize, tileSize) { - const data = this._tileBuffer; - - // palette - const palette = this._readPixels(paletteSize); - - let offset = 0; - while (offset < tileSize) { - let indexInPalette = this._inflator.inflate(1)[0]; - let length = 1; - if (indexInPalette >= 128) { - indexInPalette -= 128; - length = this._readRLELength(); - } - if (indexInPalette > paletteSize) { - throw new Error('Too big index in palette: ' + indexInPalette + ', palette size: ' + paletteSize); - } - if (offset + length > tileSize) { - throw new Error('Too big rle length in palette mode: ' + length + ', allowed length is: ' + (tileSize - offset)); - } - - for (let j = 0; j < length; j++) { - data[offset * 4] = palette[indexInPalette * 4]; - data[offset * 4 + 1] = palette[indexInPalette * 4 + 1]; - data[offset * 4 + 2] = palette[indexInPalette * 4 + 2]; - data[offset * 4 + 3] = palette[indexInPalette * 4 + 3]; - offset++; - } - } - return data; - } - - _readRLELength() { - let length = 0; - let current = 0; - do { - current = this._inflator.inflate(1)[0]; - length += current; - } while (current === 255); - return length + 1; - } -} diff --git a/base/app/novnc/core/deflator.js b/base/app/novnc/core/deflator.js deleted file mode 100644 index 22f6770..0000000 --- a/base/app/novnc/core/deflator.js +++ /dev/null @@ -1,84 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2020 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - */ - -import { deflateInit, deflate } from "../vendor/pako/lib/zlib/deflate.js"; -import { Z_FULL_FLUSH, Z_DEFAULT_COMPRESSION } from "../vendor/pako/lib/zlib/deflate.js"; -import ZStream from "../vendor/pako/lib/zlib/zstream.js"; - -export default class Deflator { - constructor() { - this.strm = new ZStream(); - this.chunkSize = 1024 * 10 * 10; - this.outputBuffer = new Uint8Array(this.chunkSize); - - deflateInit(this.strm, Z_DEFAULT_COMPRESSION); - } - - deflate(inData) { - /* eslint-disable camelcase */ - this.strm.input = inData; - this.strm.avail_in = this.strm.input.length; - this.strm.next_in = 0; - this.strm.output = this.outputBuffer; - this.strm.avail_out = this.chunkSize; - this.strm.next_out = 0; - /* eslint-enable camelcase */ - - let lastRet = deflate(this.strm, Z_FULL_FLUSH); - let outData = new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out); - - if (lastRet < 0) { - throw new Error("zlib deflate failed"); - } - - if (this.strm.avail_in > 0) { - // Read chunks until done - - let chunks = [outData]; - let totalLen = outData.length; - do { - /* eslint-disable camelcase */ - this.strm.output = new Uint8Array(this.chunkSize); - this.strm.next_out = 0; - this.strm.avail_out = this.chunkSize; - /* eslint-enable camelcase */ - - lastRet = deflate(this.strm, Z_FULL_FLUSH); - - if (lastRet < 0) { - throw new Error("zlib deflate failed"); - } - - let chunk = new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out); - totalLen += chunk.length; - chunks.push(chunk); - } while (this.strm.avail_in > 0); - - // Combine chunks into a single data - - let newData = new Uint8Array(totalLen); - let offset = 0; - - for (let i = 0; i < chunks.length; i++) { - newData.set(chunks[i], offset); - offset += chunks[i].length; - } - - outData = newData; - } - - /* eslint-disable camelcase */ - this.strm.input = null; - this.strm.avail_in = 0; - this.strm.next_in = 0; - /* eslint-enable camelcase */ - - return outData; - } - -} diff --git a/base/app/novnc/core/display.js b/base/app/novnc/core/display.js deleted file mode 100644 index fcd6269..0000000 --- a/base/app/novnc/core/display.js +++ /dev/null @@ -1,528 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2019 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - */ - -import * as Log from './util/logging.js'; -import Base64 from "./base64.js"; -import { toSigned32bit } from './util/int.js'; - -export default class Display { - constructor(target) { - this._drawCtx = null; - - this._renderQ = []; // queue drawing actions for in-oder rendering - this._flushPromise = null; - - // the full frame buffer (logical canvas) size - this._fbWidth = 0; - this._fbHeight = 0; - - this._prevDrawStyle = ""; - - Log.Debug(">> Display.constructor"); - - // The visible canvas - this._target = target; - - if (!this._target) { - throw new Error("Target must be set"); - } - - if (typeof this._target === 'string') { - throw new Error('target must be a DOM element'); - } - - if (!this._target.getContext) { - throw new Error("no getContext method"); - } - - this._targetCtx = this._target.getContext('2d'); - - // the visible canvas viewport (i.e. what actually gets seen) - this._viewportLoc = { 'x': 0, 'y': 0, 'w': this._target.width, 'h': this._target.height }; - - // The hidden canvas, where we do the actual rendering - this._backbuffer = document.createElement('canvas'); - this._drawCtx = this._backbuffer.getContext('2d'); - - this._damageBounds = { left: 0, top: 0, - right: this._backbuffer.width, - bottom: this._backbuffer.height }; - - Log.Debug("User Agent: " + navigator.userAgent); - - Log.Debug("<< Display.constructor"); - - // ===== PROPERTIES ===== - - this._scale = 1.0; - this._clipViewport = false; - } - - // ===== PROPERTIES ===== - - get scale() { return this._scale; } - set scale(scale) { - this._rescale(scale); - } - - get clipViewport() { return this._clipViewport; } - set clipViewport(viewport) { - this._clipViewport = viewport; - // May need to readjust the viewport dimensions - const vp = this._viewportLoc; - this.viewportChangeSize(vp.w, vp.h); - this.viewportChangePos(0, 0); - } - - get width() { - return this._fbWidth; - } - - get height() { - return this._fbHeight; - } - - // ===== PUBLIC METHODS ===== - - viewportChangePos(deltaX, deltaY) { - const vp = this._viewportLoc; - deltaX = Math.floor(deltaX); - deltaY = Math.floor(deltaY); - - if (!this._clipViewport) { - deltaX = -vp.w; // clamped later of out of bounds - deltaY = -vp.h; - } - - const vx2 = vp.x + vp.w - 1; - const vy2 = vp.y + vp.h - 1; - - // Position change - - if (deltaX < 0 && vp.x + deltaX < 0) { - deltaX = -vp.x; - } - if (vx2 + deltaX >= this._fbWidth) { - deltaX -= vx2 + deltaX - this._fbWidth + 1; - } - - if (vp.y + deltaY < 0) { - deltaY = -vp.y; - } - if (vy2 + deltaY >= this._fbHeight) { - deltaY -= (vy2 + deltaY - this._fbHeight + 1); - } - - if (deltaX === 0 && deltaY === 0) { - return; - } - Log.Debug("viewportChange deltaX: " + deltaX + ", deltaY: " + deltaY); - - vp.x += deltaX; - vp.y += deltaY; - - this._damage(vp.x, vp.y, vp.w, vp.h); - - this.flip(); - } - - viewportChangeSize(width, height) { - - if (!this._clipViewport || - typeof(width) === "undefined" || - typeof(height) === "undefined") { - - Log.Debug("Setting viewport to full display region"); - width = this._fbWidth; - height = this._fbHeight; - } - - width = Math.floor(width); - height = Math.floor(height); - - if (width > this._fbWidth) { - width = this._fbWidth; - } - if (height > this._fbHeight) { - height = this._fbHeight; - } - - const vp = this._viewportLoc; - if (vp.w !== width || vp.h !== height) { - vp.w = width; - vp.h = height; - - const canvas = this._target; - canvas.width = width; - canvas.height = height; - - // The position might need to be updated if we've grown - this.viewportChangePos(0, 0); - - this._damage(vp.x, vp.y, vp.w, vp.h); - this.flip(); - - // Update the visible size of the target canvas - this._rescale(this._scale); - } - } - - absX(x) { - if (this._scale === 0) { - return 0; - } - return toSigned32bit(x / this._scale + this._viewportLoc.x); - } - - absY(y) { - if (this._scale === 0) { - return 0; - } - return toSigned32bit(y / this._scale + this._viewportLoc.y); - } - - resize(width, height) { - this._prevDrawStyle = ""; - - this._fbWidth = width; - this._fbHeight = height; - - const canvas = this._backbuffer; - if (canvas.width !== width || canvas.height !== height) { - - // We have to save the canvas data since changing the size will clear it - let saveImg = null; - if (canvas.width > 0 && canvas.height > 0) { - saveImg = this._drawCtx.getImageData(0, 0, canvas.width, canvas.height); - } - - if (canvas.width !== width) { - canvas.width = width; - } - if (canvas.height !== height) { - canvas.height = height; - } - - if (saveImg) { - this._drawCtx.putImageData(saveImg, 0, 0); - } - } - - // Readjust the viewport as it may be incorrectly sized - // and positioned - const vp = this._viewportLoc; - this.viewportChangeSize(vp.w, vp.h); - this.viewportChangePos(0, 0); - } - - getImageData() { - return this._drawCtx.getImageData(0, 0, this.width, this.height); - } - - toDataURL(type, encoderOptions) { - return this._backbuffer.toDataURL(type, encoderOptions); - } - - toBlob(callback, type, quality) { - return this._backbuffer.toBlob(callback, type, quality); - } - - // Track what parts of the visible canvas that need updating - _damage(x, y, w, h) { - if (x < this._damageBounds.left) { - this._damageBounds.left = x; - } - if (y < this._damageBounds.top) { - this._damageBounds.top = y; - } - if ((x + w) > this._damageBounds.right) { - this._damageBounds.right = x + w; - } - if ((y + h) > this._damageBounds.bottom) { - this._damageBounds.bottom = y + h; - } - } - - // Update the visible canvas with the contents of the - // rendering canvas - flip(fromQueue) { - if (this._renderQ.length !== 0 && !fromQueue) { - this._renderQPush({ - 'type': 'flip' - }); - } else { - let x = this._damageBounds.left; - let y = this._damageBounds.top; - let w = this._damageBounds.right - x; - let h = this._damageBounds.bottom - y; - - let vx = x - this._viewportLoc.x; - let vy = y - this._viewportLoc.y; - - if (vx < 0) { - w += vx; - x -= vx; - vx = 0; - } - if (vy < 0) { - h += vy; - y -= vy; - vy = 0; - } - - if ((vx + w) > this._viewportLoc.w) { - w = this._viewportLoc.w - vx; - } - if ((vy + h) > this._viewportLoc.h) { - h = this._viewportLoc.h - vy; - } - - if ((w > 0) && (h > 0)) { - // FIXME: We may need to disable image smoothing here - // as well (see copyImage()), but we haven't - // noticed any problem yet. - this._targetCtx.drawImage(this._backbuffer, - x, y, w, h, - vx, vy, w, h); - } - - this._damageBounds.left = this._damageBounds.top = 65535; - this._damageBounds.right = this._damageBounds.bottom = 0; - } - } - - pending() { - return this._renderQ.length > 0; - } - - flush() { - if (this._renderQ.length === 0) { - return Promise.resolve(); - } else { - if (this._flushPromise === null) { - this._flushPromise = new Promise((resolve) => { - this._flushResolve = resolve; - }); - } - return this._flushPromise; - } - } - - fillRect(x, y, width, height, color, fromQueue) { - if (this._renderQ.length !== 0 && !fromQueue) { - this._renderQPush({ - 'type': 'fill', - 'x': x, - 'y': y, - 'width': width, - 'height': height, - 'color': color - }); - } else { - this._setFillColor(color); - this._drawCtx.fillRect(x, y, width, height); - this._damage(x, y, width, height); - } - } - - copyImage(oldX, oldY, newX, newY, w, h, fromQueue) { - if (this._renderQ.length !== 0 && !fromQueue) { - this._renderQPush({ - 'type': 'copy', - 'oldX': oldX, - 'oldY': oldY, - 'x': newX, - 'y': newY, - 'width': w, - 'height': h, - }); - } else { - // Due to this bug among others [1] we need to disable the image-smoothing to - // avoid getting a blur effect when copying data. - // - // 1. https://bugzilla.mozilla.org/show_bug.cgi?id=1194719 - // - // We need to set these every time since all properties are reset - // when the the size is changed - this._drawCtx.mozImageSmoothingEnabled = false; - this._drawCtx.webkitImageSmoothingEnabled = false; - this._drawCtx.msImageSmoothingEnabled = false; - this._drawCtx.imageSmoothingEnabled = false; - - this._drawCtx.drawImage(this._backbuffer, - oldX, oldY, w, h, - newX, newY, w, h); - this._damage(newX, newY, w, h); - } - } - - imageRect(x, y, width, height, mime, arr) { - /* The internal logic cannot handle empty images, so bail early */ - if ((width === 0) || (height === 0)) { - return; - } - - const img = new Image(); - img.src = "data: " + mime + ";base64," + Base64.encode(arr); - - this._renderQPush({ - 'type': 'img', - 'img': img, - 'x': x, - 'y': y, - 'width': width, - 'height': height - }); - } - - blitImage(x, y, width, height, arr, offset, fromQueue) { - if (this._renderQ.length !== 0 && !fromQueue) { - // NB(directxman12): it's technically more performant here to use preallocated arrays, - // but it's a lot of extra work for not a lot of payoff -- if we're using the render queue, - // this probably isn't getting called *nearly* as much - const newArr = new Uint8Array(width * height * 4); - newArr.set(new Uint8Array(arr.buffer, 0, newArr.length)); - this._renderQPush({ - 'type': 'blit', - 'data': newArr, - 'x': x, - 'y': y, - 'width': width, - 'height': height, - }); - } else { - // NB(directxman12): arr must be an Type Array view - let data = new Uint8ClampedArray(arr.buffer, - arr.byteOffset + offset, - width * height * 4); - let img = new ImageData(data, width, height); - this._drawCtx.putImageData(img, x, y); - this._damage(x, y, width, height); - } - } - - drawImage(img, x, y) { - this._drawCtx.drawImage(img, x, y); - this._damage(x, y, img.width, img.height); - } - - autoscale(containerWidth, containerHeight) { - let scaleRatio; - - if (containerWidth === 0 || containerHeight === 0) { - scaleRatio = 0; - - } else { - - const vp = this._viewportLoc; - const targetAspectRatio = containerWidth / containerHeight; - const fbAspectRatio = vp.w / vp.h; - - if (fbAspectRatio >= targetAspectRatio) { - scaleRatio = containerWidth / vp.w; - } else { - scaleRatio = containerHeight / vp.h; - } - } - - this._rescale(scaleRatio); - } - - // ===== PRIVATE METHODS ===== - - _rescale(factor) { - this._scale = factor; - const vp = this._viewportLoc; - - // NB(directxman12): If you set the width directly, or set the - // style width to a number, the canvas is cleared. - // However, if you set the style width to a string - // ('NNNpx'), the canvas is scaled without clearing. - const width = factor * vp.w + 'px'; - const height = factor * vp.h + 'px'; - - if ((this._target.style.width !== width) || - (this._target.style.height !== height)) { - this._target.style.width = width; - this._target.style.height = height; - } - } - - _setFillColor(color) { - const newStyle = 'rgb(' + color[0] + ',' + color[1] + ',' + color[2] + ')'; - if (newStyle !== this._prevDrawStyle) { - this._drawCtx.fillStyle = newStyle; - this._prevDrawStyle = newStyle; - } - } - - _renderQPush(action) { - this._renderQ.push(action); - if (this._renderQ.length === 1) { - // If this can be rendered immediately it will be, otherwise - // the scanner will wait for the relevant event - this._scanRenderQ(); - } - } - - _resumeRenderQ() { - // "this" is the object that is ready, not the - // display object - this.removeEventListener('load', this._noVNCDisplay._resumeRenderQ); - this._noVNCDisplay._scanRenderQ(); - } - - _scanRenderQ() { - let ready = true; - while (ready && this._renderQ.length > 0) { - const a = this._renderQ[0]; - switch (a.type) { - case 'flip': - this.flip(true); - break; - case 'copy': - this.copyImage(a.oldX, a.oldY, a.x, a.y, a.width, a.height, true); - break; - case 'fill': - this.fillRect(a.x, a.y, a.width, a.height, a.color, true); - break; - case 'blit': - this.blitImage(a.x, a.y, a.width, a.height, a.data, 0, true); - break; - case 'img': - if (a.img.complete) { - if (a.img.width !== a.width || a.img.height !== a.height) { - Log.Error("Decoded image has incorrect dimensions. Got " + - a.img.width + "x" + a.img.height + ". Expected " + - a.width + "x" + a.height + "."); - return; - } - this.drawImage(a.img, a.x, a.y); - } else { - a.img._noVNCDisplay = this; - a.img.addEventListener('load', this._resumeRenderQ); - // We need to wait for this image to 'load' - // to keep things in-order - ready = false; - } - break; - } - - if (ready) { - this._renderQ.shift(); - } - } - - if (this._renderQ.length === 0 && - this._flushPromise !== null) { - this._flushResolve(); - this._flushPromise = null; - this._flushResolve = null; - } - } -} diff --git a/base/app/novnc/core/encodings.js b/base/app/novnc/core/encodings.js deleted file mode 100644 index 1a79989..0000000 --- a/base/app/novnc/core/encodings.js +++ /dev/null @@ -1,49 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2019 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - */ - -export const encodings = { - encodingRaw: 0, - encodingCopyRect: 1, - encodingRRE: 2, - encodingHextile: 5, - encodingTight: 7, - encodingZRLE: 16, - encodingTightPNG: -260, - encodingJPEG: 21, - - pseudoEncodingQualityLevel9: -23, - pseudoEncodingQualityLevel0: -32, - pseudoEncodingDesktopSize: -223, - pseudoEncodingLastRect: -224, - pseudoEncodingCursor: -239, - pseudoEncodingQEMUExtendedKeyEvent: -258, - pseudoEncodingQEMULedEvent: -261, - pseudoEncodingDesktopName: -307, - pseudoEncodingExtendedDesktopSize: -308, - pseudoEncodingXvp: -309, - pseudoEncodingFence: -312, - pseudoEncodingContinuousUpdates: -313, - pseudoEncodingCompressLevel9: -247, - pseudoEncodingCompressLevel0: -256, - pseudoEncodingVMwareCursor: 0x574d5664, - pseudoEncodingExtendedClipboard: 0xc0a1e5ce -}; - -export function encodingName(num) { - switch (num) { - case encodings.encodingRaw: return "Raw"; - case encodings.encodingCopyRect: return "CopyRect"; - case encodings.encodingRRE: return "RRE"; - case encodings.encodingHextile: return "Hextile"; - case encodings.encodingTight: return "Tight"; - case encodings.encodingZRLE: return "ZRLE"; - case encodings.encodingTightPNG: return "TightPNG"; - case encodings.encodingJPEG: return "JPEG"; - default: return "[unknown encoding " + num + "]"; - } -} diff --git a/base/app/novnc/core/inflator.js b/base/app/novnc/core/inflator.js deleted file mode 100644 index f851f2a..0000000 --- a/base/app/novnc/core/inflator.js +++ /dev/null @@ -1,65 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2020 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - */ - -import { inflateInit, inflate, inflateReset } from "../vendor/pako/lib/zlib/inflate.js"; -import ZStream from "../vendor/pako/lib/zlib/zstream.js"; - -export default class Inflate { - constructor() { - this.strm = new ZStream(); - this.chunkSize = 1024 * 10 * 10; - this.strm.output = new Uint8Array(this.chunkSize); - - inflateInit(this.strm); - } - - setInput(data) { - if (!data) { - //FIXME: flush remaining data. - /* eslint-disable camelcase */ - this.strm.input = null; - this.strm.avail_in = 0; - this.strm.next_in = 0; - } else { - this.strm.input = data; - this.strm.avail_in = this.strm.input.length; - this.strm.next_in = 0; - /* eslint-enable camelcase */ - } - } - - inflate(expected) { - // resize our output buffer if it's too small - // (we could just use multiple chunks, but that would cause an extra - // allocation each time to flatten the chunks) - if (expected > this.chunkSize) { - this.chunkSize = expected; - this.strm.output = new Uint8Array(this.chunkSize); - } - - /* eslint-disable camelcase */ - this.strm.next_out = 0; - this.strm.avail_out = expected; - /* eslint-enable camelcase */ - - let ret = inflate(this.strm, 0); // Flush argument not used. - if (ret < 0) { - throw new Error("zlib inflate failed"); - } - - if (this.strm.next_out != expected) { - throw new Error("Incomplete zlib block"); - } - - return new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out); - } - - reset() { - inflateReset(this.strm); - } -} diff --git a/base/app/novnc/core/input/domkeytable.js b/base/app/novnc/core/input/domkeytable.js deleted file mode 100644 index f79aead..0000000 --- a/base/app/novnc/core/input/domkeytable.js +++ /dev/null @@ -1,311 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2018 The noVNC Authors - * Licensed under MPL 2.0 or any later version (see LICENSE.txt) - */ - -import KeyTable from "./keysym.js"; - -/* - * Mapping between HTML key values and VNC/X11 keysyms for "special" - * keys that cannot be handled via their Unicode codepoint. - * - * See https://www.w3.org/TR/uievents-key/ for possible values. - */ - -const DOMKeyTable = {}; - -function addStandard(key, standard) { - if (standard === undefined) throw new Error("Undefined keysym for key \"" + key + "\""); - if (key in DOMKeyTable) throw new Error("Duplicate entry for key \"" + key + "\""); - DOMKeyTable[key] = [standard, standard, standard, standard]; -} - -function addLeftRight(key, left, right) { - if (left === undefined) throw new Error("Undefined keysym for key \"" + key + "\""); - if (right === undefined) throw new Error("Undefined keysym for key \"" + key + "\""); - if (key in DOMKeyTable) throw new Error("Duplicate entry for key \"" + key + "\""); - DOMKeyTable[key] = [left, left, right, left]; -} - -function addNumpad(key, standard, numpad) { - if (standard === undefined) throw new Error("Undefined keysym for key \"" + key + "\""); - if (numpad === undefined) throw new Error("Undefined keysym for key \"" + key + "\""); - if (key in DOMKeyTable) throw new Error("Duplicate entry for key \"" + key + "\""); - DOMKeyTable[key] = [standard, standard, standard, numpad]; -} - -// 3.2. Modifier Keys - -addLeftRight("Alt", KeyTable.XK_Alt_L, KeyTable.XK_Alt_R); -addStandard("AltGraph", KeyTable.XK_ISO_Level3_Shift); -addStandard("CapsLock", KeyTable.XK_Caps_Lock); -addLeftRight("Control", KeyTable.XK_Control_L, KeyTable.XK_Control_R); -// - Fn -// - FnLock -addLeftRight("Meta", KeyTable.XK_Super_L, KeyTable.XK_Super_R); -addStandard("NumLock", KeyTable.XK_Num_Lock); -addStandard("ScrollLock", KeyTable.XK_Scroll_Lock); -addLeftRight("Shift", KeyTable.XK_Shift_L, KeyTable.XK_Shift_R); -// - Symbol -// - SymbolLock -// - Hyper -// - Super - -// 3.3. Whitespace Keys - -addNumpad("Enter", KeyTable.XK_Return, KeyTable.XK_KP_Enter); -addStandard("Tab", KeyTable.XK_Tab); -addNumpad(" ", KeyTable.XK_space, KeyTable.XK_KP_Space); - -// 3.4. Navigation Keys - -addNumpad("ArrowDown", KeyTable.XK_Down, KeyTable.XK_KP_Down); -addNumpad("ArrowLeft", KeyTable.XK_Left, KeyTable.XK_KP_Left); -addNumpad("ArrowRight", KeyTable.XK_Right, KeyTable.XK_KP_Right); -addNumpad("ArrowUp", KeyTable.XK_Up, KeyTable.XK_KP_Up); -addNumpad("End", KeyTable.XK_End, KeyTable.XK_KP_End); -addNumpad("Home", KeyTable.XK_Home, KeyTable.XK_KP_Home); -addNumpad("PageDown", KeyTable.XK_Next, KeyTable.XK_KP_Next); -addNumpad("PageUp", KeyTable.XK_Prior, KeyTable.XK_KP_Prior); - -// 3.5. Editing Keys - -addStandard("Backspace", KeyTable.XK_BackSpace); -// Browsers send "Clear" for the numpad 5 without NumLock because -// Windows uses VK_Clear for that key. But Unix expects KP_Begin for -// that scenario. -addNumpad("Clear", KeyTable.XK_Clear, KeyTable.XK_KP_Begin); -addStandard("Copy", KeyTable.XF86XK_Copy); -// - CrSel -addStandard("Cut", KeyTable.XF86XK_Cut); -addNumpad("Delete", KeyTable.XK_Delete, KeyTable.XK_KP_Delete); -// - EraseEof -// - ExSel -addNumpad("Insert", KeyTable.XK_Insert, KeyTable.XK_KP_Insert); -addStandard("Paste", KeyTable.XF86XK_Paste); -addStandard("Redo", KeyTable.XK_Redo); -addStandard("Undo", KeyTable.XK_Undo); - -// 3.6. UI Keys - -// - Accept -// - Again (could just be XK_Redo) -// - Attn -addStandard("Cancel", KeyTable.XK_Cancel); -addStandard("ContextMenu", KeyTable.XK_Menu); -addStandard("Escape", KeyTable.XK_Escape); -addStandard("Execute", KeyTable.XK_Execute); -addStandard("Find", KeyTable.XK_Find); -addStandard("Help", KeyTable.XK_Help); -addStandard("Pause", KeyTable.XK_Pause); -// - Play -// - Props -addStandard("Select", KeyTable.XK_Select); -addStandard("ZoomIn", KeyTable.XF86XK_ZoomIn); -addStandard("ZoomOut", KeyTable.XF86XK_ZoomOut); - -// 3.7. Device Keys - -addStandard("BrightnessDown", KeyTable.XF86XK_MonBrightnessDown); -addStandard("BrightnessUp", KeyTable.XF86XK_MonBrightnessUp); -addStandard("Eject", KeyTable.XF86XK_Eject); -addStandard("LogOff", KeyTable.XF86XK_LogOff); -addStandard("Power", KeyTable.XF86XK_PowerOff); -addStandard("PowerOff", KeyTable.XF86XK_PowerDown); -addStandard("PrintScreen", KeyTable.XK_Print); -addStandard("Hibernate", KeyTable.XF86XK_Hibernate); -addStandard("Standby", KeyTable.XF86XK_Standby); -addStandard("WakeUp", KeyTable.XF86XK_WakeUp); - -// 3.8. IME and Composition Keys - -addStandard("AllCandidates", KeyTable.XK_MultipleCandidate); -addStandard("Alphanumeric", KeyTable.XK_Eisu_toggle); -addStandard("CodeInput", KeyTable.XK_Codeinput); -addStandard("Compose", KeyTable.XK_Multi_key); -addStandard("Convert", KeyTable.XK_Henkan); -// - Dead -// - FinalMode -addStandard("GroupFirst", KeyTable.XK_ISO_First_Group); -addStandard("GroupLast", KeyTable.XK_ISO_Last_Group); -addStandard("GroupNext", KeyTable.XK_ISO_Next_Group); -addStandard("GroupPrevious", KeyTable.XK_ISO_Prev_Group); -// - ModeChange (XK_Mode_switch is often used for AltGr) -// - NextCandidate -addStandard("NonConvert", KeyTable.XK_Muhenkan); -addStandard("PreviousCandidate", KeyTable.XK_PreviousCandidate); -// - Process -addStandard("SingleCandidate", KeyTable.XK_SingleCandidate); -addStandard("HangulMode", KeyTable.XK_Hangul); -addStandard("HanjaMode", KeyTable.XK_Hangul_Hanja); -addStandard("JunjaMode", KeyTable.XK_Hangul_Jeonja); -addStandard("Eisu", KeyTable.XK_Eisu_toggle); -addStandard("Hankaku", KeyTable.XK_Hankaku); -addStandard("Hiragana", KeyTable.XK_Hiragana); -addStandard("HiraganaKatakana", KeyTable.XK_Hiragana_Katakana); -addStandard("KanaMode", KeyTable.XK_Kana_Shift); // could also be _Kana_Lock -addStandard("KanjiMode", KeyTable.XK_Kanji); -addStandard("Katakana", KeyTable.XK_Katakana); -addStandard("Romaji", KeyTable.XK_Romaji); -addStandard("Zenkaku", KeyTable.XK_Zenkaku); -addStandard("ZenkakuHankaku", KeyTable.XK_Zenkaku_Hankaku); - -// 3.9. General-Purpose Function Keys - -addStandard("F1", KeyTable.XK_F1); -addStandard("F2", KeyTable.XK_F2); -addStandard("F3", KeyTable.XK_F3); -addStandard("F4", KeyTable.XK_F4); -addStandard("F5", KeyTable.XK_F5); -addStandard("F6", KeyTable.XK_F6); -addStandard("F7", KeyTable.XK_F7); -addStandard("F8", KeyTable.XK_F8); -addStandard("F9", KeyTable.XK_F9); -addStandard("F10", KeyTable.XK_F10); -addStandard("F11", KeyTable.XK_F11); -addStandard("F12", KeyTable.XK_F12); -addStandard("F13", KeyTable.XK_F13); -addStandard("F14", KeyTable.XK_F14); -addStandard("F15", KeyTable.XK_F15); -addStandard("F16", KeyTable.XK_F16); -addStandard("F17", KeyTable.XK_F17); -addStandard("F18", KeyTable.XK_F18); -addStandard("F19", KeyTable.XK_F19); -addStandard("F20", KeyTable.XK_F20); -addStandard("F21", KeyTable.XK_F21); -addStandard("F22", KeyTable.XK_F22); -addStandard("F23", KeyTable.XK_F23); -addStandard("F24", KeyTable.XK_F24); -addStandard("F25", KeyTable.XK_F25); -addStandard("F26", KeyTable.XK_F26); -addStandard("F27", KeyTable.XK_F27); -addStandard("F28", KeyTable.XK_F28); -addStandard("F29", KeyTable.XK_F29); -addStandard("F30", KeyTable.XK_F30); -addStandard("F31", KeyTable.XK_F31); -addStandard("F32", KeyTable.XK_F32); -addStandard("F33", KeyTable.XK_F33); -addStandard("F34", KeyTable.XK_F34); -addStandard("F35", KeyTable.XK_F35); -// - Soft1... - -// 3.10. Multimedia Keys - -// - ChannelDown -// - ChannelUp -addStandard("Close", KeyTable.XF86XK_Close); -addStandard("MailForward", KeyTable.XF86XK_MailForward); -addStandard("MailReply", KeyTable.XF86XK_Reply); -addStandard("MailSend", KeyTable.XF86XK_Send); -// - MediaClose -addStandard("MediaFastForward", KeyTable.XF86XK_AudioForward); -addStandard("MediaPause", KeyTable.XF86XK_AudioPause); -addStandard("MediaPlay", KeyTable.XF86XK_AudioPlay); -// - MediaPlayPause -addStandard("MediaRecord", KeyTable.XF86XK_AudioRecord); -addStandard("MediaRewind", KeyTable.XF86XK_AudioRewind); -addStandard("MediaStop", KeyTable.XF86XK_AudioStop); -addStandard("MediaTrackNext", KeyTable.XF86XK_AudioNext); -addStandard("MediaTrackPrevious", KeyTable.XF86XK_AudioPrev); -addStandard("New", KeyTable.XF86XK_New); -addStandard("Open", KeyTable.XF86XK_Open); -addStandard("Print", KeyTable.XK_Print); -addStandard("Save", KeyTable.XF86XK_Save); -addStandard("SpellCheck", KeyTable.XF86XK_Spell); - -// 3.11. Multimedia Numpad Keys - -// - Key11 -// - Key12 - -// 3.12. Audio Keys - -// - AudioBalanceLeft -// - AudioBalanceRight -// - AudioBassBoostDown -// - AudioBassBoostToggle -// - AudioBassBoostUp -// - AudioFaderFront -// - AudioFaderRear -// - AudioSurroundModeNext -// - AudioTrebleDown -// - AudioTrebleUp -addStandard("AudioVolumeDown", KeyTable.XF86XK_AudioLowerVolume); -addStandard("AudioVolumeUp", KeyTable.XF86XK_AudioRaiseVolume); -addStandard("AudioVolumeMute", KeyTable.XF86XK_AudioMute); -// - MicrophoneToggle -// - MicrophoneVolumeDown -// - MicrophoneVolumeUp -addStandard("MicrophoneVolumeMute", KeyTable.XF86XK_AudioMicMute); - -// 3.13. Speech Keys - -// - SpeechCorrectionList -// - SpeechInputToggle - -// 3.14. Application Keys - -addStandard("LaunchApplication1", KeyTable.XF86XK_MyComputer); -addStandard("LaunchApplication2", KeyTable.XF86XK_Calculator); -addStandard("LaunchCalendar", KeyTable.XF86XK_Calendar); -// - LaunchContacts -addStandard("LaunchMail", KeyTable.XF86XK_Mail); -addStandard("LaunchMediaPlayer", KeyTable.XF86XK_AudioMedia); -addStandard("LaunchMusicPlayer", KeyTable.XF86XK_Music); -addStandard("LaunchPhone", KeyTable.XF86XK_Phone); -addStandard("LaunchScreenSaver", KeyTable.XF86XK_ScreenSaver); -addStandard("LaunchSpreadsheet", KeyTable.XF86XK_Excel); -addStandard("LaunchWebBrowser", KeyTable.XF86XK_WWW); -addStandard("LaunchWebCam", KeyTable.XF86XK_WebCam); -addStandard("LaunchWordProcessor", KeyTable.XF86XK_Word); - -// 3.15. Browser Keys - -addStandard("BrowserBack", KeyTable.XF86XK_Back); -addStandard("BrowserFavorites", KeyTable.XF86XK_Favorites); -addStandard("BrowserForward", KeyTable.XF86XK_Forward); -addStandard("BrowserHome", KeyTable.XF86XK_HomePage); -addStandard("BrowserRefresh", KeyTable.XF86XK_Refresh); -addStandard("BrowserSearch", KeyTable.XF86XK_Search); -addStandard("BrowserStop", KeyTable.XF86XK_Stop); - -// 3.16. Mobile Phone Keys - -// - A whole bunch... - -// 3.17. TV Keys - -// - A whole bunch... - -// 3.18. Media Controller Keys - -// - A whole bunch... -addStandard("Dimmer", KeyTable.XF86XK_BrightnessAdjust); -addStandard("MediaAudioTrack", KeyTable.XF86XK_AudioCycleTrack); -addStandard("RandomToggle", KeyTable.XF86XK_AudioRandomPlay); -addStandard("SplitScreenToggle", KeyTable.XF86XK_SplitScreen); -addStandard("Subtitle", KeyTable.XF86XK_Subtitle); -addStandard("VideoModeNext", KeyTable.XF86XK_Next_VMode); - -// Extra: Numpad - -addNumpad("=", KeyTable.XK_equal, KeyTable.XK_KP_Equal); -addNumpad("+", KeyTable.XK_plus, KeyTable.XK_KP_Add); -addNumpad("-", KeyTable.XK_minus, KeyTable.XK_KP_Subtract); -addNumpad("*", KeyTable.XK_asterisk, KeyTable.XK_KP_Multiply); -addNumpad("/", KeyTable.XK_slash, KeyTable.XK_KP_Divide); -addNumpad(".", KeyTable.XK_period, KeyTable.XK_KP_Decimal); -addNumpad(",", KeyTable.XK_comma, KeyTable.XK_KP_Separator); -addNumpad("0", KeyTable.XK_0, KeyTable.XK_KP_0); -addNumpad("1", KeyTable.XK_1, KeyTable.XK_KP_1); -addNumpad("2", KeyTable.XK_2, KeyTable.XK_KP_2); -addNumpad("3", KeyTable.XK_3, KeyTable.XK_KP_3); -addNumpad("4", KeyTable.XK_4, KeyTable.XK_KP_4); -addNumpad("5", KeyTable.XK_5, KeyTable.XK_KP_5); -addNumpad("6", KeyTable.XK_6, KeyTable.XK_KP_6); -addNumpad("7", KeyTable.XK_7, KeyTable.XK_KP_7); -addNumpad("8", KeyTable.XK_8, KeyTable.XK_KP_8); -addNumpad("9", KeyTable.XK_9, KeyTable.XK_KP_9); - -export default DOMKeyTable; diff --git a/base/app/novnc/core/input/fixedkeys.js b/base/app/novnc/core/input/fixedkeys.js deleted file mode 100644 index 4d09f2f..0000000 --- a/base/app/novnc/core/input/fixedkeys.js +++ /dev/null @@ -1,129 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2018 The noVNC Authors - * Licensed under MPL 2.0 or any later version (see LICENSE.txt) - */ - -/* - * Fallback mapping between HTML key codes (physical keys) and - * HTML key values. This only works for keys that don't vary - * between layouts. We also omit those who manage fine by mapping the - * Unicode representation. - * - * See https://www.w3.org/TR/uievents-code/ for possible codes. - * See https://www.w3.org/TR/uievents-key/ for possible values. - */ - -/* eslint-disable key-spacing */ - -export default { - -// 3.1.1.1. Writing System Keys - - 'Backspace': 'Backspace', - -// 3.1.1.2. Functional Keys - - 'AltLeft': 'Alt', - 'AltRight': 'Alt', // This could also be 'AltGraph' - 'CapsLock': 'CapsLock', - 'ContextMenu': 'ContextMenu', - 'ControlLeft': 'Control', - 'ControlRight': 'Control', - 'Enter': 'Enter', - 'MetaLeft': 'Meta', - 'MetaRight': 'Meta', - 'ShiftLeft': 'Shift', - 'ShiftRight': 'Shift', - 'Tab': 'Tab', - // FIXME: Japanese/Korean keys - -// 3.1.2. Control Pad Section - - 'Delete': 'Delete', - 'End': 'End', - 'Help': 'Help', - 'Home': 'Home', - 'Insert': 'Insert', - 'PageDown': 'PageDown', - 'PageUp': 'PageUp', - -// 3.1.3. Arrow Pad Section - - 'ArrowDown': 'ArrowDown', - 'ArrowLeft': 'ArrowLeft', - 'ArrowRight': 'ArrowRight', - 'ArrowUp': 'ArrowUp', - -// 3.1.4. Numpad Section - - 'NumLock': 'NumLock', - 'NumpadBackspace': 'Backspace', - 'NumpadClear': 'Clear', - -// 3.1.5. Function Section - - 'Escape': 'Escape', - 'F1': 'F1', - 'F2': 'F2', - 'F3': 'F3', - 'F4': 'F4', - 'F5': 'F5', - 'F6': 'F6', - 'F7': 'F7', - 'F8': 'F8', - 'F9': 'F9', - 'F10': 'F10', - 'F11': 'F11', - 'F12': 'F12', - 'F13': 'F13', - 'F14': 'F14', - 'F15': 'F15', - 'F16': 'F16', - 'F17': 'F17', - 'F18': 'F18', - 'F19': 'F19', - 'F20': 'F20', - 'F21': 'F21', - 'F22': 'F22', - 'F23': 'F23', - 'F24': 'F24', - 'F25': 'F25', - 'F26': 'F26', - 'F27': 'F27', - 'F28': 'F28', - 'F29': 'F29', - 'F30': 'F30', - 'F31': 'F31', - 'F32': 'F32', - 'F33': 'F33', - 'F34': 'F34', - 'F35': 'F35', - 'PrintScreen': 'PrintScreen', - 'ScrollLock': 'ScrollLock', - 'Pause': 'Pause', - -// 3.1.6. Media Keys - - 'BrowserBack': 'BrowserBack', - 'BrowserFavorites': 'BrowserFavorites', - 'BrowserForward': 'BrowserForward', - 'BrowserHome': 'BrowserHome', - 'BrowserRefresh': 'BrowserRefresh', - 'BrowserSearch': 'BrowserSearch', - 'BrowserStop': 'BrowserStop', - 'Eject': 'Eject', - 'LaunchApp1': 'LaunchMyComputer', - 'LaunchApp2': 'LaunchCalendar', - 'LaunchMail': 'LaunchMail', - 'MediaPlayPause': 'MediaPlay', - 'MediaStop': 'MediaStop', - 'MediaTrackNext': 'MediaTrackNext', - 'MediaTrackPrevious': 'MediaTrackPrevious', - 'Power': 'Power', - 'Sleep': 'Sleep', - 'AudioVolumeDown': 'AudioVolumeDown', - 'AudioVolumeMute': 'AudioVolumeMute', - 'AudioVolumeUp': 'AudioVolumeUp', - 'WakeUp': 'WakeUp', -}; diff --git a/base/app/novnc/core/input/gesturehandler.js b/base/app/novnc/core/input/gesturehandler.js deleted file mode 100644 index 6fa72d2..0000000 --- a/base/app/novnc/core/input/gesturehandler.js +++ /dev/null @@ -1,567 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2020 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - * - */ - -const GH_NOGESTURE = 0; -const GH_ONETAP = 1; -const GH_TWOTAP = 2; -const GH_THREETAP = 4; -const GH_DRAG = 8; -const GH_LONGPRESS = 16; -const GH_TWODRAG = 32; -const GH_PINCH = 64; - -const GH_INITSTATE = 127; - -const GH_MOVE_THRESHOLD = 50; -const GH_ANGLE_THRESHOLD = 90; // Degrees - -// Timeout when waiting for gestures (ms) -const GH_MULTITOUCH_TIMEOUT = 250; - -// Maximum time between press and release for a tap (ms) -const GH_TAP_TIMEOUT = 1000; - -// Timeout when waiting for longpress (ms) -const GH_LONGPRESS_TIMEOUT = 1000; - -// Timeout when waiting to decide between PINCH and TWODRAG (ms) -const GH_TWOTOUCH_TIMEOUT = 50; - -export default class GestureHandler { - constructor() { - this._target = null; - - this._state = GH_INITSTATE; - - this._tracked = []; - this._ignored = []; - - this._waitingRelease = false; - this._releaseStart = 0.0; - - this._longpressTimeoutId = null; - this._twoTouchTimeoutId = null; - - this._boundEventHandler = this._eventHandler.bind(this); - } - - attach(target) { - this.detach(); - - this._target = target; - this._target.addEventListener('touchstart', - this._boundEventHandler); - this._target.addEventListener('touchmove', - this._boundEventHandler); - this._target.addEventListener('touchend', - this._boundEventHandler); - this._target.addEventListener('touchcancel', - this._boundEventHandler); - } - - detach() { - if (!this._target) { - return; - } - - this._stopLongpressTimeout(); - this._stopTwoTouchTimeout(); - - this._target.removeEventListener('touchstart', - this._boundEventHandler); - this._target.removeEventListener('touchmove', - this._boundEventHandler); - this._target.removeEventListener('touchend', - this._boundEventHandler); - this._target.removeEventListener('touchcancel', - this._boundEventHandler); - this._target = null; - } - - _eventHandler(e) { - let fn; - - e.stopPropagation(); - e.preventDefault(); - - switch (e.type) { - case 'touchstart': - fn = this._touchStart; - break; - case 'touchmove': - fn = this._touchMove; - break; - case 'touchend': - case 'touchcancel': - fn = this._touchEnd; - break; - } - - for (let i = 0; i < e.changedTouches.length; i++) { - let touch = e.changedTouches[i]; - fn.call(this, touch.identifier, touch.clientX, touch.clientY); - } - } - - _touchStart(id, x, y) { - // Ignore any new touches if there is already an active gesture, - // or we're in a cleanup state - if (this._hasDetectedGesture() || (this._state === GH_NOGESTURE)) { - this._ignored.push(id); - return; - } - - // Did it take too long between touches that we should no longer - // consider this a single gesture? - if ((this._tracked.length > 0) && - ((Date.now() - this._tracked[0].started) > GH_MULTITOUCH_TIMEOUT)) { - this._state = GH_NOGESTURE; - this._ignored.push(id); - return; - } - - // If we're waiting for fingers to release then we should no longer - // recognize new touches - if (this._waitingRelease) { - this._state = GH_NOGESTURE; - this._ignored.push(id); - return; - } - - this._tracked.push({ - id: id, - started: Date.now(), - active: true, - firstX: x, - firstY: y, - lastX: x, - lastY: y, - angle: 0 - }); - - switch (this._tracked.length) { - case 1: - this._startLongpressTimeout(); - break; - - case 2: - this._state &= ~(GH_ONETAP | GH_DRAG | GH_LONGPRESS); - this._stopLongpressTimeout(); - break; - - case 3: - this._state &= ~(GH_TWOTAP | GH_TWODRAG | GH_PINCH); - break; - - default: - this._state = GH_NOGESTURE; - } - } - - _touchMove(id, x, y) { - let touch = this._tracked.find(t => t.id === id); - - // If this is an update for a touch we're not tracking, ignore it - if (touch === undefined) { - return; - } - - // Update the touches last position with the event coordinates - touch.lastX = x; - touch.lastY = y; - - let deltaX = x - touch.firstX; - let deltaY = y - touch.firstY; - - // Update angle when the touch has moved - if ((touch.firstX !== touch.lastX) || - (touch.firstY !== touch.lastY)) { - touch.angle = Math.atan2(deltaY, deltaX) * 180 / Math.PI; - } - - if (!this._hasDetectedGesture()) { - // Ignore moves smaller than the minimum threshold - if (Math.hypot(deltaX, deltaY) < GH_MOVE_THRESHOLD) { - return; - } - - // Can't be a tap or long press as we've seen movement - this._state &= ~(GH_ONETAP | GH_TWOTAP | GH_THREETAP | GH_LONGPRESS); - this._stopLongpressTimeout(); - - if (this._tracked.length !== 1) { - this._state &= ~(GH_DRAG); - } - if (this._tracked.length !== 2) { - this._state &= ~(GH_TWODRAG | GH_PINCH); - } - - // We need to figure out which of our different two touch gestures - // this might be - if (this._tracked.length === 2) { - - // The other touch is the one where the id doesn't match - let prevTouch = this._tracked.find(t => t.id !== id); - - // How far the previous touch point has moved since start - let prevDeltaMove = Math.hypot(prevTouch.firstX - prevTouch.lastX, - prevTouch.firstY - prevTouch.lastY); - - // We know that the current touch moved far enough, - // but unless both touches moved further than their - // threshold we don't want to disqualify any gestures - if (prevDeltaMove > GH_MOVE_THRESHOLD) { - - // The angle difference between the direction of the touch points - let deltaAngle = Math.abs(touch.angle - prevTouch.angle); - deltaAngle = Math.abs(((deltaAngle + 180) % 360) - 180); - - // PINCH or TWODRAG can be eliminated depending on the angle - if (deltaAngle > GH_ANGLE_THRESHOLD) { - this._state &= ~GH_TWODRAG; - } else { - this._state &= ~GH_PINCH; - } - - if (this._isTwoTouchTimeoutRunning()) { - this._stopTwoTouchTimeout(); - } - } else if (!this._isTwoTouchTimeoutRunning()) { - // We can't determine the gesture right now, let's - // wait and see if more events are on their way - this._startTwoTouchTimeout(); - } - } - - if (!this._hasDetectedGesture()) { - return; - } - - this._pushEvent('gesturestart'); - } - - this._pushEvent('gesturemove'); - } - - _touchEnd(id, x, y) { - // Check if this is an ignored touch - if (this._ignored.indexOf(id) !== -1) { - // Remove this touch from ignored - this._ignored.splice(this._ignored.indexOf(id), 1); - - // And reset the state if there are no more touches - if ((this._ignored.length === 0) && - (this._tracked.length === 0)) { - this._state = GH_INITSTATE; - this._waitingRelease = false; - } - return; - } - - // We got a touchend before the timer triggered, - // this cannot result in a gesture anymore. - if (!this._hasDetectedGesture() && - this._isTwoTouchTimeoutRunning()) { - this._stopTwoTouchTimeout(); - this._state = GH_NOGESTURE; - } - - // Some gestures don't trigger until a touch is released - if (!this._hasDetectedGesture()) { - // Can't be a gesture that relies on movement - this._state &= ~(GH_DRAG | GH_TWODRAG | GH_PINCH); - // Or something that relies on more time - this._state &= ~GH_LONGPRESS; - this._stopLongpressTimeout(); - - if (!this._waitingRelease) { - this._releaseStart = Date.now(); - this._waitingRelease = true; - - // Can't be a tap that requires more touches than we current have - switch (this._tracked.length) { - case 1: - this._state &= ~(GH_TWOTAP | GH_THREETAP); - break; - - case 2: - this._state &= ~(GH_ONETAP | GH_THREETAP); - break; - } - } - } - - // Waiting for all touches to release? (i.e. some tap) - if (this._waitingRelease) { - // Were all touches released at roughly the same time? - if ((Date.now() - this._releaseStart) > GH_MULTITOUCH_TIMEOUT) { - this._state = GH_NOGESTURE; - } - - // Did too long time pass between press and release? - if (this._tracked.some(t => (Date.now() - t.started) > GH_TAP_TIMEOUT)) { - this._state = GH_NOGESTURE; - } - - let touch = this._tracked.find(t => t.id === id); - touch.active = false; - - // Are we still waiting for more releases? - if (this._hasDetectedGesture()) { - this._pushEvent('gesturestart'); - } else { - // Have we reached a dead end? - if (this._state !== GH_NOGESTURE) { - return; - } - } - } - - if (this._hasDetectedGesture()) { - this._pushEvent('gestureend'); - } - - // Ignore any remaining touches until they are ended - for (let i = 0; i < this._tracked.length; i++) { - if (this._tracked[i].active) { - this._ignored.push(this._tracked[i].id); - } - } - this._tracked = []; - - this._state = GH_NOGESTURE; - - // Remove this touch from ignored if it's in there - if (this._ignored.indexOf(id) !== -1) { - this._ignored.splice(this._ignored.indexOf(id), 1); - } - - // We reset the state if ignored is empty - if ((this._ignored.length === 0)) { - this._state = GH_INITSTATE; - this._waitingRelease = false; - } - } - - _hasDetectedGesture() { - if (this._state === GH_NOGESTURE) { - return false; - } - // Check to see if the bitmask value is a power of 2 - // (i.e. only one bit set). If it is, we have a state. - if (this._state & (this._state - 1)) { - return false; - } - - // For taps we also need to have all touches released - // before we've fully detected the gesture - if (this._state & (GH_ONETAP | GH_TWOTAP | GH_THREETAP)) { - if (this._tracked.some(t => t.active)) { - return false; - } - } - - return true; - } - - _startLongpressTimeout() { - this._stopLongpressTimeout(); - this._longpressTimeoutId = setTimeout(() => this._longpressTimeout(), - GH_LONGPRESS_TIMEOUT); - } - - _stopLongpressTimeout() { - clearTimeout(this._longpressTimeoutId); - this._longpressTimeoutId = null; - } - - _longpressTimeout() { - if (this._hasDetectedGesture()) { - throw new Error("A longpress gesture failed, conflict with a different gesture"); - } - - this._state = GH_LONGPRESS; - this._pushEvent('gesturestart'); - } - - _startTwoTouchTimeout() { - this._stopTwoTouchTimeout(); - this._twoTouchTimeoutId = setTimeout(() => this._twoTouchTimeout(), - GH_TWOTOUCH_TIMEOUT); - } - - _stopTwoTouchTimeout() { - clearTimeout(this._twoTouchTimeoutId); - this._twoTouchTimeoutId = null; - } - - _isTwoTouchTimeoutRunning() { - return this._twoTouchTimeoutId !== null; - } - - _twoTouchTimeout() { - if (this._tracked.length === 0) { - throw new Error("A pinch or two drag gesture failed, no tracked touches"); - } - - // How far each touch point has moved since start - let avgM = this._getAverageMovement(); - let avgMoveH = Math.abs(avgM.x); - let avgMoveV = Math.abs(avgM.y); - - // The difference in the distance between where - // the touch points started and where they are now - let avgD = this._getAverageDistance(); - let deltaTouchDistance = Math.abs(Math.hypot(avgD.first.x, avgD.first.y) - - Math.hypot(avgD.last.x, avgD.last.y)); - - if ((avgMoveV < deltaTouchDistance) && - (avgMoveH < deltaTouchDistance)) { - this._state = GH_PINCH; - } else { - this._state = GH_TWODRAG; - } - - this._pushEvent('gesturestart'); - this._pushEvent('gesturemove'); - } - - _pushEvent(type) { - let detail = { type: this._stateToGesture(this._state) }; - - // For most gesture events the current (average) position is the - // most useful - let avg = this._getPosition(); - let pos = avg.last; - - // However we have a slight distance to detect gestures, so for the - // first gesture event we want to use the first positions we saw - if (type === 'gesturestart') { - pos = avg.first; - } - - // For these gestures, we always want the event coordinates - // to be where the gesture began, not the current touch location. - switch (this._state) { - case GH_TWODRAG: - case GH_PINCH: - pos = avg.first; - break; - } - - detail['clientX'] = pos.x; - detail['clientY'] = pos.y; - - // FIXME: other coordinates? - - // Some gestures also have a magnitude - if (this._state === GH_PINCH) { - let distance = this._getAverageDistance(); - if (type === 'gesturestart') { - detail['magnitudeX'] = distance.first.x; - detail['magnitudeY'] = distance.first.y; - } else { - detail['magnitudeX'] = distance.last.x; - detail['magnitudeY'] = distance.last.y; - } - } else if (this._state === GH_TWODRAG) { - if (type === 'gesturestart') { - detail['magnitudeX'] = 0.0; - detail['magnitudeY'] = 0.0; - } else { - let movement = this._getAverageMovement(); - detail['magnitudeX'] = movement.x; - detail['magnitudeY'] = movement.y; - } - } - - let gev = new CustomEvent(type, { detail: detail }); - this._target.dispatchEvent(gev); - } - - _stateToGesture(state) { - switch (state) { - case GH_ONETAP: - return 'onetap'; - case GH_TWOTAP: - return 'twotap'; - case GH_THREETAP: - return 'threetap'; - case GH_DRAG: - return 'drag'; - case GH_LONGPRESS: - return 'longpress'; - case GH_TWODRAG: - return 'twodrag'; - case GH_PINCH: - return 'pinch'; - } - - throw new Error("Unknown gesture state: " + state); - } - - _getPosition() { - if (this._tracked.length === 0) { - throw new Error("Failed to get gesture position, no tracked touches"); - } - - let size = this._tracked.length; - let fx = 0, fy = 0, lx = 0, ly = 0; - - for (let i = 0; i < this._tracked.length; i++) { - fx += this._tracked[i].firstX; - fy += this._tracked[i].firstY; - lx += this._tracked[i].lastX; - ly += this._tracked[i].lastY; - } - - return { first: { x: fx / size, - y: fy / size }, - last: { x: lx / size, - y: ly / size } }; - } - - _getAverageMovement() { - if (this._tracked.length === 0) { - throw new Error("Failed to get gesture movement, no tracked touches"); - } - - let totalH, totalV; - totalH = totalV = 0; - let size = this._tracked.length; - - for (let i = 0; i < this._tracked.length; i++) { - totalH += this._tracked[i].lastX - this._tracked[i].firstX; - totalV += this._tracked[i].lastY - this._tracked[i].firstY; - } - - return { x: totalH / size, - y: totalV / size }; - } - - _getAverageDistance() { - if (this._tracked.length === 0) { - throw new Error("Failed to get gesture distance, no tracked touches"); - } - - // Distance between the first and last tracked touches - - let first = this._tracked[0]; - let last = this._tracked[this._tracked.length - 1]; - - let fdx = Math.abs(last.firstX - first.firstX); - let fdy = Math.abs(last.firstY - first.firstY); - - let ldx = Math.abs(last.lastX - first.lastX); - let ldy = Math.abs(last.lastY - first.lastY); - - return { first: { x: fdx, y: fdy }, - last: { x: ldx, y: ldy } }; - } -} diff --git a/base/app/novnc/core/input/keyboard.js b/base/app/novnc/core/input/keyboard.js deleted file mode 100644 index 68da231..0000000 --- a/base/app/novnc/core/input/keyboard.js +++ /dev/null @@ -1,292 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2019 The noVNC Authors - * Licensed under MPL 2.0 or any later version (see LICENSE.txt) - */ - -import * as Log from '../util/logging.js'; -import { stopEvent } from '../util/events.js'; -import * as KeyboardUtil from "./util.js"; -import KeyTable from "./keysym.js"; -import * as browser from "../util/browser.js"; - -// -// Keyboard event handler -// - -export default class Keyboard { - constructor(target) { - this._target = target || null; - - this._keyDownList = {}; // List of depressed keys - // (even if they are happy) - this._altGrArmed = false; // Windows AltGr detection - - // keep these here so we can refer to them later - this._eventHandlers = { - 'keyup': this._handleKeyUp.bind(this), - 'keydown': this._handleKeyDown.bind(this), - 'blur': this._allKeysUp.bind(this), - }; - - // ===== EVENT HANDLERS ===== - - this.onkeyevent = () => {}; // Handler for key press/release - } - - // ===== PRIVATE METHODS ===== - - _sendKeyEvent(keysym, code, down, numlock = null, capslock = null) { - if (down) { - this._keyDownList[code] = keysym; - } else { - // Do we really think this key is down? - if (!(code in this._keyDownList)) { - return; - } - delete this._keyDownList[code]; - } - - Log.Debug("onkeyevent " + (down ? "down" : "up") + - ", keysym: " + keysym, ", code: " + code + - ", numlock: " + numlock + ", capslock: " + capslock); - this.onkeyevent(keysym, code, down, numlock, capslock); - } - - _getKeyCode(e) { - const code = KeyboardUtil.getKeycode(e); - if (code !== 'Unidentified') { - return code; - } - - // Unstable, but we don't have anything else to go on - if (e.keyCode) { - // 229 is used for composition events - if (e.keyCode !== 229) { - return 'Platform' + e.keyCode; - } - } - - // A precursor to the final DOM3 standard. Unfortunately it - // is not layout independent, so it is as bad as using keyCode - if (e.keyIdentifier) { - // Non-character key? - if (e.keyIdentifier.substr(0, 2) !== 'U+') { - return e.keyIdentifier; - } - - const codepoint = parseInt(e.keyIdentifier.substr(2), 16); - const char = String.fromCharCode(codepoint).toUpperCase(); - - return 'Platform' + char.charCodeAt(); - } - - return 'Unidentified'; - } - - _handleKeyDown(e) { - const code = this._getKeyCode(e); - let keysym = KeyboardUtil.getKeysym(e); - let numlock = e.getModifierState('NumLock'); - let capslock = e.getModifierState('CapsLock'); - - // getModifierState for NumLock is not supported on mac and ios and always returns false. - // Set to null to indicate unknown/unsupported instead. - if (browser.isMac() || browser.isIOS()) { - numlock = null; - } - - // Windows doesn't have a proper AltGr, but handles it using - // fake Ctrl+Alt. However the remote end might not be Windows, - // so we need to merge those in to a single AltGr event. We - // detect this case by seeing the two key events directly after - // each other with a very short time between them (<50ms). - if (this._altGrArmed) { - this._altGrArmed = false; - clearTimeout(this._altGrTimeout); - - if ((code === "AltRight") && - ((e.timeStamp - this._altGrCtrlTime) < 50)) { - // FIXME: We fail to detect this if either Ctrl key is - // first manually pressed as Windows then no - // longer sends the fake Ctrl down event. It - // does however happily send real Ctrl events - // even when AltGr is already down. Some - // browsers detect this for us though and set the - // key to "AltGraph". - keysym = KeyTable.XK_ISO_Level3_Shift; - } else { - this._sendKeyEvent(KeyTable.XK_Control_L, "ControlLeft", true, numlock, capslock); - } - } - - // We cannot handle keys we cannot track, but we also need - // to deal with virtual keyboards which omit key info - if (code === 'Unidentified') { - if (keysym) { - // If it's a virtual keyboard then it should be - // sufficient to just send press and release right - // after each other - this._sendKeyEvent(keysym, code, true, numlock, capslock); - this._sendKeyEvent(keysym, code, false, numlock, capslock); - } - - stopEvent(e); - return; - } - - // Alt behaves more like AltGraph on macOS, so shuffle the - // keys around a bit to make things more sane for the remote - // server. This method is used by RealVNC and TigerVNC (and - // possibly others). - if (browser.isMac() || browser.isIOS()) { - switch (keysym) { - case KeyTable.XK_Super_L: - keysym = KeyTable.XK_Alt_L; - break; - case KeyTable.XK_Super_R: - keysym = KeyTable.XK_Super_L; - break; - case KeyTable.XK_Alt_L: - keysym = KeyTable.XK_Mode_switch; - break; - case KeyTable.XK_Alt_R: - keysym = KeyTable.XK_ISO_Level3_Shift; - break; - } - } - - // Is this key already pressed? If so, then we must use the - // same keysym or we'll confuse the server - if (code in this._keyDownList) { - keysym = this._keyDownList[code]; - } - - // macOS doesn't send proper key releases if a key is pressed - // while meta is held down - if ((browser.isMac() || browser.isIOS()) && - (e.metaKey && code !== 'MetaLeft' && code !== 'MetaRight')) { - this._sendKeyEvent(keysym, code, true, numlock, capslock); - this._sendKeyEvent(keysym, code, false, numlock, capslock); - stopEvent(e); - return; - } - - // macOS doesn't send proper key events for modifiers, only - // state change events. That gets extra confusing for CapsLock - // which toggles on each press, but not on release. So pretend - // it was a quick press and release of the button. - if ((browser.isMac() || browser.isIOS()) && (code === 'CapsLock')) { - this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', true, numlock, capslock); - this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', false, numlock, capslock); - stopEvent(e); - return; - } - - // Windows doesn't send proper key releases for a bunch of - // Japanese IM keys so we have to fake the release right away - const jpBadKeys = [ KeyTable.XK_Zenkaku_Hankaku, - KeyTable.XK_Eisu_toggle, - KeyTable.XK_Katakana, - KeyTable.XK_Hiragana, - KeyTable.XK_Romaji ]; - if (browser.isWindows() && jpBadKeys.includes(keysym)) { - this._sendKeyEvent(keysym, code, true, numlock, capslock); - this._sendKeyEvent(keysym, code, false, numlock, capslock); - stopEvent(e); - return; - } - - stopEvent(e); - - // Possible start of AltGr sequence? (see above) - if ((code === "ControlLeft") && browser.isWindows() && - !("ControlLeft" in this._keyDownList)) { - this._altGrArmed = true; - this._altGrTimeout = setTimeout(this._handleAltGrTimeout.bind(this), 100); - this._altGrCtrlTime = e.timeStamp; - return; - } - - this._sendKeyEvent(keysym, code, true, numlock, capslock); - } - - _handleKeyUp(e) { - stopEvent(e); - - const code = this._getKeyCode(e); - - // We can't get a release in the middle of an AltGr sequence, so - // abort that detection - if (this._altGrArmed) { - this._altGrArmed = false; - clearTimeout(this._altGrTimeout); - this._sendKeyEvent(KeyTable.XK_Control_L, "ControlLeft", true); - } - - // See comment in _handleKeyDown() - if ((browser.isMac() || browser.isIOS()) && (code === 'CapsLock')) { - this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', true); - this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', false); - return; - } - - this._sendKeyEvent(this._keyDownList[code], code, false); - - // Windows has a rather nasty bug where it won't send key - // release events for a Shift button if the other Shift is still - // pressed - if (browser.isWindows() && ((code === 'ShiftLeft') || - (code === 'ShiftRight'))) { - if ('ShiftRight' in this._keyDownList) { - this._sendKeyEvent(this._keyDownList['ShiftRight'], - 'ShiftRight', false); - } - if ('ShiftLeft' in this._keyDownList) { - this._sendKeyEvent(this._keyDownList['ShiftLeft'], - 'ShiftLeft', false); - } - } - } - - _handleAltGrTimeout() { - this._altGrArmed = false; - clearTimeout(this._altGrTimeout); - this._sendKeyEvent(KeyTable.XK_Control_L, "ControlLeft", true); - } - - _allKeysUp() { - Log.Debug(">> Keyboard.allKeysUp"); - for (let code in this._keyDownList) { - this._sendKeyEvent(this._keyDownList[code], code, false); - } - Log.Debug("<< Keyboard.allKeysUp"); - } - - // ===== PUBLIC METHODS ===== - - grab() { - //Log.Debug(">> Keyboard.grab"); - - this._target.addEventListener('keydown', this._eventHandlers.keydown); - this._target.addEventListener('keyup', this._eventHandlers.keyup); - - // Release (key up) if window loses focus - window.addEventListener('blur', this._eventHandlers.blur); - - //Log.Debug("<< Keyboard.grab"); - } - - ungrab() { - //Log.Debug(">> Keyboard.ungrab"); - - this._target.removeEventListener('keydown', this._eventHandlers.keydown); - this._target.removeEventListener('keyup', this._eventHandlers.keyup); - window.removeEventListener('blur', this._eventHandlers.blur); - - // Release (key up) all keys that are in a down state - this._allKeysUp(); - - //Log.Debug(">> Keyboard.ungrab"); - } -} diff --git a/base/app/novnc/core/input/keysym.js b/base/app/novnc/core/input/keysym.js deleted file mode 100644 index 22ba058..0000000 --- a/base/app/novnc/core/input/keysym.js +++ /dev/null @@ -1,616 +0,0 @@ -/* eslint-disable key-spacing */ - -export default { - XK_VoidSymbol: 0xffffff, /* Void symbol */ - - XK_BackSpace: 0xff08, /* Back space, back char */ - XK_Tab: 0xff09, - XK_Linefeed: 0xff0a, /* Linefeed, LF */ - XK_Clear: 0xff0b, - XK_Return: 0xff0d, /* Return, enter */ - XK_Pause: 0xff13, /* Pause, hold */ - XK_Scroll_Lock: 0xff14, - XK_Sys_Req: 0xff15, - XK_Escape: 0xff1b, - XK_Delete: 0xffff, /* Delete, rubout */ - - /* International & multi-key character composition */ - - XK_Multi_key: 0xff20, /* Multi-key character compose */ - XK_Codeinput: 0xff37, - XK_SingleCandidate: 0xff3c, - XK_MultipleCandidate: 0xff3d, - XK_PreviousCandidate: 0xff3e, - - /* Japanese keyboard support */ - - XK_Kanji: 0xff21, /* Kanji, Kanji convert */ - XK_Muhenkan: 0xff22, /* Cancel Conversion */ - XK_Henkan_Mode: 0xff23, /* Start/Stop Conversion */ - XK_Henkan: 0xff23, /* Alias for Henkan_Mode */ - XK_Romaji: 0xff24, /* to Romaji */ - XK_Hiragana: 0xff25, /* to Hiragana */ - XK_Katakana: 0xff26, /* to Katakana */ - XK_Hiragana_Katakana: 0xff27, /* Hiragana/Katakana toggle */ - XK_Zenkaku: 0xff28, /* to Zenkaku */ - XK_Hankaku: 0xff29, /* to Hankaku */ - XK_Zenkaku_Hankaku: 0xff2a, /* Zenkaku/Hankaku toggle */ - XK_Touroku: 0xff2b, /* Add to Dictionary */ - XK_Massyo: 0xff2c, /* Delete from Dictionary */ - XK_Kana_Lock: 0xff2d, /* Kana Lock */ - XK_Kana_Shift: 0xff2e, /* Kana Shift */ - XK_Eisu_Shift: 0xff2f, /* Alphanumeric Shift */ - XK_Eisu_toggle: 0xff30, /* Alphanumeric toggle */ - XK_Kanji_Bangou: 0xff37, /* Codeinput */ - XK_Zen_Koho: 0xff3d, /* Multiple/All Candidate(s) */ - XK_Mae_Koho: 0xff3e, /* Previous Candidate */ - - /* Cursor control & motion */ - - XK_Home: 0xff50, - XK_Left: 0xff51, /* Move left, left arrow */ - XK_Up: 0xff52, /* Move up, up arrow */ - XK_Right: 0xff53, /* Move right, right arrow */ - XK_Down: 0xff54, /* Move down, down arrow */ - XK_Prior: 0xff55, /* Prior, previous */ - XK_Page_Up: 0xff55, - XK_Next: 0xff56, /* Next */ - XK_Page_Down: 0xff56, - XK_End: 0xff57, /* EOL */ - XK_Begin: 0xff58, /* BOL */ - - - /* Misc functions */ - - XK_Select: 0xff60, /* Select, mark */ - XK_Print: 0xff61, - XK_Execute: 0xff62, /* Execute, run, do */ - XK_Insert: 0xff63, /* Insert, insert here */ - XK_Undo: 0xff65, - XK_Redo: 0xff66, /* Redo, again */ - XK_Menu: 0xff67, - XK_Find: 0xff68, /* Find, search */ - XK_Cancel: 0xff69, /* Cancel, stop, abort, exit */ - XK_Help: 0xff6a, /* Help */ - XK_Break: 0xff6b, - XK_Mode_switch: 0xff7e, /* Character set switch */ - XK_script_switch: 0xff7e, /* Alias for mode_switch */ - XK_Num_Lock: 0xff7f, - - /* Keypad functions, keypad numbers cleverly chosen to map to ASCII */ - - XK_KP_Space: 0xff80, /* Space */ - XK_KP_Tab: 0xff89, - XK_KP_Enter: 0xff8d, /* Enter */ - XK_KP_F1: 0xff91, /* PF1, KP_A, ... */ - XK_KP_F2: 0xff92, - XK_KP_F3: 0xff93, - XK_KP_F4: 0xff94, - XK_KP_Home: 0xff95, - XK_KP_Left: 0xff96, - XK_KP_Up: 0xff97, - XK_KP_Right: 0xff98, - XK_KP_Down: 0xff99, - XK_KP_Prior: 0xff9a, - XK_KP_Page_Up: 0xff9a, - XK_KP_Next: 0xff9b, - XK_KP_Page_Down: 0xff9b, - XK_KP_End: 0xff9c, - XK_KP_Begin: 0xff9d, - XK_KP_Insert: 0xff9e, - XK_KP_Delete: 0xff9f, - XK_KP_Equal: 0xffbd, /* Equals */ - XK_KP_Multiply: 0xffaa, - XK_KP_Add: 0xffab, - XK_KP_Separator: 0xffac, /* Separator, often comma */ - XK_KP_Subtract: 0xffad, - XK_KP_Decimal: 0xffae, - XK_KP_Divide: 0xffaf, - - XK_KP_0: 0xffb0, - XK_KP_1: 0xffb1, - XK_KP_2: 0xffb2, - XK_KP_3: 0xffb3, - XK_KP_4: 0xffb4, - XK_KP_5: 0xffb5, - XK_KP_6: 0xffb6, - XK_KP_7: 0xffb7, - XK_KP_8: 0xffb8, - XK_KP_9: 0xffb9, - - /* - * Auxiliary functions; note the duplicate definitions for left and right - * function keys; Sun keyboards and a few other manufacturers have such - * function key groups on the left and/or right sides of the keyboard. - * We've not found a keyboard with more than 35 function keys total. - */ - - XK_F1: 0xffbe, - XK_F2: 0xffbf, - XK_F3: 0xffc0, - XK_F4: 0xffc1, - XK_F5: 0xffc2, - XK_F6: 0xffc3, - XK_F7: 0xffc4, - XK_F8: 0xffc5, - XK_F9: 0xffc6, - XK_F10: 0xffc7, - XK_F11: 0xffc8, - XK_L1: 0xffc8, - XK_F12: 0xffc9, - XK_L2: 0xffc9, - XK_F13: 0xffca, - XK_L3: 0xffca, - XK_F14: 0xffcb, - XK_L4: 0xffcb, - XK_F15: 0xffcc, - XK_L5: 0xffcc, - XK_F16: 0xffcd, - XK_L6: 0xffcd, - XK_F17: 0xffce, - XK_L7: 0xffce, - XK_F18: 0xffcf, - XK_L8: 0xffcf, - XK_F19: 0xffd0, - XK_L9: 0xffd0, - XK_F20: 0xffd1, - XK_L10: 0xffd1, - XK_F21: 0xffd2, - XK_R1: 0xffd2, - XK_F22: 0xffd3, - XK_R2: 0xffd3, - XK_F23: 0xffd4, - XK_R3: 0xffd4, - XK_F24: 0xffd5, - XK_R4: 0xffd5, - XK_F25: 0xffd6, - XK_R5: 0xffd6, - XK_F26: 0xffd7, - XK_R6: 0xffd7, - XK_F27: 0xffd8, - XK_R7: 0xffd8, - XK_F28: 0xffd9, - XK_R8: 0xffd9, - XK_F29: 0xffda, - XK_R9: 0xffda, - XK_F30: 0xffdb, - XK_R10: 0xffdb, - XK_F31: 0xffdc, - XK_R11: 0xffdc, - XK_F32: 0xffdd, - XK_R12: 0xffdd, - XK_F33: 0xffde, - XK_R13: 0xffde, - XK_F34: 0xffdf, - XK_R14: 0xffdf, - XK_F35: 0xffe0, - XK_R15: 0xffe0, - - /* Modifiers */ - - XK_Shift_L: 0xffe1, /* Left shift */ - XK_Shift_R: 0xffe2, /* Right shift */ - XK_Control_L: 0xffe3, /* Left control */ - XK_Control_R: 0xffe4, /* Right control */ - XK_Caps_Lock: 0xffe5, /* Caps lock */ - XK_Shift_Lock: 0xffe6, /* Shift lock */ - - XK_Meta_L: 0xffe7, /* Left meta */ - XK_Meta_R: 0xffe8, /* Right meta */ - XK_Alt_L: 0xffe9, /* Left alt */ - XK_Alt_R: 0xffea, /* Right alt */ - XK_Super_L: 0xffeb, /* Left super */ - XK_Super_R: 0xffec, /* Right super */ - XK_Hyper_L: 0xffed, /* Left hyper */ - XK_Hyper_R: 0xffee, /* Right hyper */ - - /* - * Keyboard (XKB) Extension function and modifier keys - * (from Appendix C of "The X Keyboard Extension: Protocol Specification") - * Byte 3 = 0xfe - */ - - XK_ISO_Level3_Shift: 0xfe03, /* AltGr */ - XK_ISO_Next_Group: 0xfe08, - XK_ISO_Prev_Group: 0xfe0a, - XK_ISO_First_Group: 0xfe0c, - XK_ISO_Last_Group: 0xfe0e, - - /* - * Latin 1 - * (ISO/IEC 8859-1: Unicode U+0020..U+00FF) - * Byte 3: 0 - */ - - XK_space: 0x0020, /* U+0020 SPACE */ - XK_exclam: 0x0021, /* U+0021 EXCLAMATION MARK */ - XK_quotedbl: 0x0022, /* U+0022 QUOTATION MARK */ - XK_numbersign: 0x0023, /* U+0023 NUMBER SIGN */ - XK_dollar: 0x0024, /* U+0024 DOLLAR SIGN */ - XK_percent: 0x0025, /* U+0025 PERCENT SIGN */ - XK_ampersand: 0x0026, /* U+0026 AMPERSAND */ - XK_apostrophe: 0x0027, /* U+0027 APOSTROPHE */ - XK_quoteright: 0x0027, /* deprecated */ - XK_parenleft: 0x0028, /* U+0028 LEFT PARENTHESIS */ - XK_parenright: 0x0029, /* U+0029 RIGHT PARENTHESIS */ - XK_asterisk: 0x002a, /* U+002A ASTERISK */ - XK_plus: 0x002b, /* U+002B PLUS SIGN */ - XK_comma: 0x002c, /* U+002C COMMA */ - XK_minus: 0x002d, /* U+002D HYPHEN-MINUS */ - XK_period: 0x002e, /* U+002E FULL STOP */ - XK_slash: 0x002f, /* U+002F SOLIDUS */ - XK_0: 0x0030, /* U+0030 DIGIT ZERO */ - XK_1: 0x0031, /* U+0031 DIGIT ONE */ - XK_2: 0x0032, /* U+0032 DIGIT TWO */ - XK_3: 0x0033, /* U+0033 DIGIT THREE */ - XK_4: 0x0034, /* U+0034 DIGIT FOUR */ - XK_5: 0x0035, /* U+0035 DIGIT FIVE */ - XK_6: 0x0036, /* U+0036 DIGIT SIX */ - XK_7: 0x0037, /* U+0037 DIGIT SEVEN */ - XK_8: 0x0038, /* U+0038 DIGIT EIGHT */ - XK_9: 0x0039, /* U+0039 DIGIT NINE */ - XK_colon: 0x003a, /* U+003A COLON */ - XK_semicolon: 0x003b, /* U+003B SEMICOLON */ - XK_less: 0x003c, /* U+003C LESS-THAN SIGN */ - XK_equal: 0x003d, /* U+003D EQUALS SIGN */ - XK_greater: 0x003e, /* U+003E GREATER-THAN SIGN */ - XK_question: 0x003f, /* U+003F QUESTION MARK */ - XK_at: 0x0040, /* U+0040 COMMERCIAL AT */ - XK_A: 0x0041, /* U+0041 LATIN CAPITAL LETTER A */ - XK_B: 0x0042, /* U+0042 LATIN CAPITAL LETTER B */ - XK_C: 0x0043, /* U+0043 LATIN CAPITAL LETTER C */ - XK_D: 0x0044, /* U+0044 LATIN CAPITAL LETTER D */ - XK_E: 0x0045, /* U+0045 LATIN CAPITAL LETTER E */ - XK_F: 0x0046, /* U+0046 LATIN CAPITAL LETTER F */ - XK_G: 0x0047, /* U+0047 LATIN CAPITAL LETTER G */ - XK_H: 0x0048, /* U+0048 LATIN CAPITAL LETTER H */ - XK_I: 0x0049, /* U+0049 LATIN CAPITAL LETTER I */ - XK_J: 0x004a, /* U+004A LATIN CAPITAL LETTER J */ - XK_K: 0x004b, /* U+004B LATIN CAPITAL LETTER K */ - XK_L: 0x004c, /* U+004C LATIN CAPITAL LETTER L */ - XK_M: 0x004d, /* U+004D LATIN CAPITAL LETTER M */ - XK_N: 0x004e, /* U+004E LATIN CAPITAL LETTER N */ - XK_O: 0x004f, /* U+004F LATIN CAPITAL LETTER O */ - XK_P: 0x0050, /* U+0050 LATIN CAPITAL LETTER P */ - XK_Q: 0x0051, /* U+0051 LATIN CAPITAL LETTER Q */ - XK_R: 0x0052, /* U+0052 LATIN CAPITAL LETTER R */ - XK_S: 0x0053, /* U+0053 LATIN CAPITAL LETTER S */ - XK_T: 0x0054, /* U+0054 LATIN CAPITAL LETTER T */ - XK_U: 0x0055, /* U+0055 LATIN CAPITAL LETTER U */ - XK_V: 0x0056, /* U+0056 LATIN CAPITAL LETTER V */ - XK_W: 0x0057, /* U+0057 LATIN CAPITAL LETTER W */ - XK_X: 0x0058, /* U+0058 LATIN CAPITAL LETTER X */ - XK_Y: 0x0059, /* U+0059 LATIN CAPITAL LETTER Y */ - XK_Z: 0x005a, /* U+005A LATIN CAPITAL LETTER Z */ - XK_bracketleft: 0x005b, /* U+005B LEFT SQUARE BRACKET */ - XK_backslash: 0x005c, /* U+005C REVERSE SOLIDUS */ - XK_bracketright: 0x005d, /* U+005D RIGHT SQUARE BRACKET */ - XK_asciicircum: 0x005e, /* U+005E CIRCUMFLEX ACCENT */ - XK_underscore: 0x005f, /* U+005F LOW LINE */ - XK_grave: 0x0060, /* U+0060 GRAVE ACCENT */ - XK_quoteleft: 0x0060, /* deprecated */ - XK_a: 0x0061, /* U+0061 LATIN SMALL LETTER A */ - XK_b: 0x0062, /* U+0062 LATIN SMALL LETTER B */ - XK_c: 0x0063, /* U+0063 LATIN SMALL LETTER C */ - XK_d: 0x0064, /* U+0064 LATIN SMALL LETTER D */ - XK_e: 0x0065, /* U+0065 LATIN SMALL LETTER E */ - XK_f: 0x0066, /* U+0066 LATIN SMALL LETTER F */ - XK_g: 0x0067, /* U+0067 LATIN SMALL LETTER G */ - XK_h: 0x0068, /* U+0068 LATIN SMALL LETTER H */ - XK_i: 0x0069, /* U+0069 LATIN SMALL LETTER I */ - XK_j: 0x006a, /* U+006A LATIN SMALL LETTER J */ - XK_k: 0x006b, /* U+006B LATIN SMALL LETTER K */ - XK_l: 0x006c, /* U+006C LATIN SMALL LETTER L */ - XK_m: 0x006d, /* U+006D LATIN SMALL LETTER M */ - XK_n: 0x006e, /* U+006E LATIN SMALL LETTER N */ - XK_o: 0x006f, /* U+006F LATIN SMALL LETTER O */ - XK_p: 0x0070, /* U+0070 LATIN SMALL LETTER P */ - XK_q: 0x0071, /* U+0071 LATIN SMALL LETTER Q */ - XK_r: 0x0072, /* U+0072 LATIN SMALL LETTER R */ - XK_s: 0x0073, /* U+0073 LATIN SMALL LETTER S */ - XK_t: 0x0074, /* U+0074 LATIN SMALL LETTER T */ - XK_u: 0x0075, /* U+0075 LATIN SMALL LETTER U */ - XK_v: 0x0076, /* U+0076 LATIN SMALL LETTER V */ - XK_w: 0x0077, /* U+0077 LATIN SMALL LETTER W */ - XK_x: 0x0078, /* U+0078 LATIN SMALL LETTER X */ - XK_y: 0x0079, /* U+0079 LATIN SMALL LETTER Y */ - XK_z: 0x007a, /* U+007A LATIN SMALL LETTER Z */ - XK_braceleft: 0x007b, /* U+007B LEFT CURLY BRACKET */ - XK_bar: 0x007c, /* U+007C VERTICAL LINE */ - XK_braceright: 0x007d, /* U+007D RIGHT CURLY BRACKET */ - XK_asciitilde: 0x007e, /* U+007E TILDE */ - - XK_nobreakspace: 0x00a0, /* U+00A0 NO-BREAK SPACE */ - XK_exclamdown: 0x00a1, /* U+00A1 INVERTED EXCLAMATION MARK */ - XK_cent: 0x00a2, /* U+00A2 CENT SIGN */ - XK_sterling: 0x00a3, /* U+00A3 POUND SIGN */ - XK_currency: 0x00a4, /* U+00A4 CURRENCY SIGN */ - XK_yen: 0x00a5, /* U+00A5 YEN SIGN */ - XK_brokenbar: 0x00a6, /* U+00A6 BROKEN BAR */ - XK_section: 0x00a7, /* U+00A7 SECTION SIGN */ - XK_diaeresis: 0x00a8, /* U+00A8 DIAERESIS */ - XK_copyright: 0x00a9, /* U+00A9 COPYRIGHT SIGN */ - XK_ordfeminine: 0x00aa, /* U+00AA FEMININE ORDINAL INDICATOR */ - XK_guillemotleft: 0x00ab, /* U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK */ - XK_notsign: 0x00ac, /* U+00AC NOT SIGN */ - XK_hyphen: 0x00ad, /* U+00AD SOFT HYPHEN */ - XK_registered: 0x00ae, /* U+00AE REGISTERED SIGN */ - XK_macron: 0x00af, /* U+00AF MACRON */ - XK_degree: 0x00b0, /* U+00B0 DEGREE SIGN */ - XK_plusminus: 0x00b1, /* U+00B1 PLUS-MINUS SIGN */ - XK_twosuperior: 0x00b2, /* U+00B2 SUPERSCRIPT TWO */ - XK_threesuperior: 0x00b3, /* U+00B3 SUPERSCRIPT THREE */ - XK_acute: 0x00b4, /* U+00B4 ACUTE ACCENT */ - XK_mu: 0x00b5, /* U+00B5 MICRO SIGN */ - XK_paragraph: 0x00b6, /* U+00B6 PILCROW SIGN */ - XK_periodcentered: 0x00b7, /* U+00B7 MIDDLE DOT */ - XK_cedilla: 0x00b8, /* U+00B8 CEDILLA */ - XK_onesuperior: 0x00b9, /* U+00B9 SUPERSCRIPT ONE */ - XK_masculine: 0x00ba, /* U+00BA MASCULINE ORDINAL INDICATOR */ - XK_guillemotright: 0x00bb, /* U+00BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK */ - XK_onequarter: 0x00bc, /* U+00BC VULGAR FRACTION ONE QUARTER */ - XK_onehalf: 0x00bd, /* U+00BD VULGAR FRACTION ONE HALF */ - XK_threequarters: 0x00be, /* U+00BE VULGAR FRACTION THREE QUARTERS */ - XK_questiondown: 0x00bf, /* U+00BF INVERTED QUESTION MARK */ - XK_Agrave: 0x00c0, /* U+00C0 LATIN CAPITAL LETTER A WITH GRAVE */ - XK_Aacute: 0x00c1, /* U+00C1 LATIN CAPITAL LETTER A WITH ACUTE */ - XK_Acircumflex: 0x00c2, /* U+00C2 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ - XK_Atilde: 0x00c3, /* U+00C3 LATIN CAPITAL LETTER A WITH TILDE */ - XK_Adiaeresis: 0x00c4, /* U+00C4 LATIN CAPITAL LETTER A WITH DIAERESIS */ - XK_Aring: 0x00c5, /* U+00C5 LATIN CAPITAL LETTER A WITH RING ABOVE */ - XK_AE: 0x00c6, /* U+00C6 LATIN CAPITAL LETTER AE */ - XK_Ccedilla: 0x00c7, /* U+00C7 LATIN CAPITAL LETTER C WITH CEDILLA */ - XK_Egrave: 0x00c8, /* U+00C8 LATIN CAPITAL LETTER E WITH GRAVE */ - XK_Eacute: 0x00c9, /* U+00C9 LATIN CAPITAL LETTER E WITH ACUTE */ - XK_Ecircumflex: 0x00ca, /* U+00CA LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ - XK_Ediaeresis: 0x00cb, /* U+00CB LATIN CAPITAL LETTER E WITH DIAERESIS */ - XK_Igrave: 0x00cc, /* U+00CC LATIN CAPITAL LETTER I WITH GRAVE */ - XK_Iacute: 0x00cd, /* U+00CD LATIN CAPITAL LETTER I WITH ACUTE */ - XK_Icircumflex: 0x00ce, /* U+00CE LATIN CAPITAL LETTER I WITH CIRCUMFLEX */ - XK_Idiaeresis: 0x00cf, /* U+00CF LATIN CAPITAL LETTER I WITH DIAERESIS */ - XK_ETH: 0x00d0, /* U+00D0 LATIN CAPITAL LETTER ETH */ - XK_Eth: 0x00d0, /* deprecated */ - XK_Ntilde: 0x00d1, /* U+00D1 LATIN CAPITAL LETTER N WITH TILDE */ - XK_Ograve: 0x00d2, /* U+00D2 LATIN CAPITAL LETTER O WITH GRAVE */ - XK_Oacute: 0x00d3, /* U+00D3 LATIN CAPITAL LETTER O WITH ACUTE */ - XK_Ocircumflex: 0x00d4, /* U+00D4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ - XK_Otilde: 0x00d5, /* U+00D5 LATIN CAPITAL LETTER O WITH TILDE */ - XK_Odiaeresis: 0x00d6, /* U+00D6 LATIN CAPITAL LETTER O WITH DIAERESIS */ - XK_multiply: 0x00d7, /* U+00D7 MULTIPLICATION SIGN */ - XK_Oslash: 0x00d8, /* U+00D8 LATIN CAPITAL LETTER O WITH STROKE */ - XK_Ooblique: 0x00d8, /* U+00D8 LATIN CAPITAL LETTER O WITH STROKE */ - XK_Ugrave: 0x00d9, /* U+00D9 LATIN CAPITAL LETTER U WITH GRAVE */ - XK_Uacute: 0x00da, /* U+00DA LATIN CAPITAL LETTER U WITH ACUTE */ - XK_Ucircumflex: 0x00db, /* U+00DB LATIN CAPITAL LETTER U WITH CIRCUMFLEX */ - XK_Udiaeresis: 0x00dc, /* U+00DC LATIN CAPITAL LETTER U WITH DIAERESIS */ - XK_Yacute: 0x00dd, /* U+00DD LATIN CAPITAL LETTER Y WITH ACUTE */ - XK_THORN: 0x00de, /* U+00DE LATIN CAPITAL LETTER THORN */ - XK_Thorn: 0x00de, /* deprecated */ - XK_ssharp: 0x00df, /* U+00DF LATIN SMALL LETTER SHARP S */ - XK_agrave: 0x00e0, /* U+00E0 LATIN SMALL LETTER A WITH GRAVE */ - XK_aacute: 0x00e1, /* U+00E1 LATIN SMALL LETTER A WITH ACUTE */ - XK_acircumflex: 0x00e2, /* U+00E2 LATIN SMALL LETTER A WITH CIRCUMFLEX */ - XK_atilde: 0x00e3, /* U+00E3 LATIN SMALL LETTER A WITH TILDE */ - XK_adiaeresis: 0x00e4, /* U+00E4 LATIN SMALL LETTER A WITH DIAERESIS */ - XK_aring: 0x00e5, /* U+00E5 LATIN SMALL LETTER A WITH RING ABOVE */ - XK_ae: 0x00e6, /* U+00E6 LATIN SMALL LETTER AE */ - XK_ccedilla: 0x00e7, /* U+00E7 LATIN SMALL LETTER C WITH CEDILLA */ - XK_egrave: 0x00e8, /* U+00E8 LATIN SMALL LETTER E WITH GRAVE */ - XK_eacute: 0x00e9, /* U+00E9 LATIN SMALL LETTER E WITH ACUTE */ - XK_ecircumflex: 0x00ea, /* U+00EA LATIN SMALL LETTER E WITH CIRCUMFLEX */ - XK_ediaeresis: 0x00eb, /* U+00EB LATIN SMALL LETTER E WITH DIAERESIS */ - XK_igrave: 0x00ec, /* U+00EC LATIN SMALL LETTER I WITH GRAVE */ - XK_iacute: 0x00ed, /* U+00ED LATIN SMALL LETTER I WITH ACUTE */ - XK_icircumflex: 0x00ee, /* U+00EE LATIN SMALL LETTER I WITH CIRCUMFLEX */ - XK_idiaeresis: 0x00ef, /* U+00EF LATIN SMALL LETTER I WITH DIAERESIS */ - XK_eth: 0x00f0, /* U+00F0 LATIN SMALL LETTER ETH */ - XK_ntilde: 0x00f1, /* U+00F1 LATIN SMALL LETTER N WITH TILDE */ - XK_ograve: 0x00f2, /* U+00F2 LATIN SMALL LETTER O WITH GRAVE */ - XK_oacute: 0x00f3, /* U+00F3 LATIN SMALL LETTER O WITH ACUTE */ - XK_ocircumflex: 0x00f4, /* U+00F4 LATIN SMALL LETTER O WITH CIRCUMFLEX */ - XK_otilde: 0x00f5, /* U+00F5 LATIN SMALL LETTER O WITH TILDE */ - XK_odiaeresis: 0x00f6, /* U+00F6 LATIN SMALL LETTER O WITH DIAERESIS */ - XK_division: 0x00f7, /* U+00F7 DIVISION SIGN */ - XK_oslash: 0x00f8, /* U+00F8 LATIN SMALL LETTER O WITH STROKE */ - XK_ooblique: 0x00f8, /* U+00F8 LATIN SMALL LETTER O WITH STROKE */ - XK_ugrave: 0x00f9, /* U+00F9 LATIN SMALL LETTER U WITH GRAVE */ - XK_uacute: 0x00fa, /* U+00FA LATIN SMALL LETTER U WITH ACUTE */ - XK_ucircumflex: 0x00fb, /* U+00FB LATIN SMALL LETTER U WITH CIRCUMFLEX */ - XK_udiaeresis: 0x00fc, /* U+00FC LATIN SMALL LETTER U WITH DIAERESIS */ - XK_yacute: 0x00fd, /* U+00FD LATIN SMALL LETTER Y WITH ACUTE */ - XK_thorn: 0x00fe, /* U+00FE LATIN SMALL LETTER THORN */ - XK_ydiaeresis: 0x00ff, /* U+00FF LATIN SMALL LETTER Y WITH DIAERESIS */ - - /* - * Korean - * Byte 3 = 0x0e - */ - - XK_Hangul: 0xff31, /* Hangul start/stop(toggle) */ - XK_Hangul_Hanja: 0xff34, /* Start Hangul->Hanja Conversion */ - XK_Hangul_Jeonja: 0xff38, /* Jeonja mode */ - - /* - * XFree86 vendor specific keysyms. - * - * The XFree86 keysym range is 0x10080001 - 0x1008FFFF. - */ - - XF86XK_ModeLock: 0x1008FF01, - XF86XK_MonBrightnessUp: 0x1008FF02, - XF86XK_MonBrightnessDown: 0x1008FF03, - XF86XK_KbdLightOnOff: 0x1008FF04, - XF86XK_KbdBrightnessUp: 0x1008FF05, - XF86XK_KbdBrightnessDown: 0x1008FF06, - XF86XK_Standby: 0x1008FF10, - XF86XK_AudioLowerVolume: 0x1008FF11, - XF86XK_AudioMute: 0x1008FF12, - XF86XK_AudioRaiseVolume: 0x1008FF13, - XF86XK_AudioPlay: 0x1008FF14, - XF86XK_AudioStop: 0x1008FF15, - XF86XK_AudioPrev: 0x1008FF16, - XF86XK_AudioNext: 0x1008FF17, - XF86XK_HomePage: 0x1008FF18, - XF86XK_Mail: 0x1008FF19, - XF86XK_Start: 0x1008FF1A, - XF86XK_Search: 0x1008FF1B, - XF86XK_AudioRecord: 0x1008FF1C, - XF86XK_Calculator: 0x1008FF1D, - XF86XK_Memo: 0x1008FF1E, - XF86XK_ToDoList: 0x1008FF1F, - XF86XK_Calendar: 0x1008FF20, - XF86XK_PowerDown: 0x1008FF21, - XF86XK_ContrastAdjust: 0x1008FF22, - XF86XK_RockerUp: 0x1008FF23, - XF86XK_RockerDown: 0x1008FF24, - XF86XK_RockerEnter: 0x1008FF25, - XF86XK_Back: 0x1008FF26, - XF86XK_Forward: 0x1008FF27, - XF86XK_Stop: 0x1008FF28, - XF86XK_Refresh: 0x1008FF29, - XF86XK_PowerOff: 0x1008FF2A, - XF86XK_WakeUp: 0x1008FF2B, - XF86XK_Eject: 0x1008FF2C, - XF86XK_ScreenSaver: 0x1008FF2D, - XF86XK_WWW: 0x1008FF2E, - XF86XK_Sleep: 0x1008FF2F, - XF86XK_Favorites: 0x1008FF30, - XF86XK_AudioPause: 0x1008FF31, - XF86XK_AudioMedia: 0x1008FF32, - XF86XK_MyComputer: 0x1008FF33, - XF86XK_VendorHome: 0x1008FF34, - XF86XK_LightBulb: 0x1008FF35, - XF86XK_Shop: 0x1008FF36, - XF86XK_History: 0x1008FF37, - XF86XK_OpenURL: 0x1008FF38, - XF86XK_AddFavorite: 0x1008FF39, - XF86XK_HotLinks: 0x1008FF3A, - XF86XK_BrightnessAdjust: 0x1008FF3B, - XF86XK_Finance: 0x1008FF3C, - XF86XK_Community: 0x1008FF3D, - XF86XK_AudioRewind: 0x1008FF3E, - XF86XK_BackForward: 0x1008FF3F, - XF86XK_Launch0: 0x1008FF40, - XF86XK_Launch1: 0x1008FF41, - XF86XK_Launch2: 0x1008FF42, - XF86XK_Launch3: 0x1008FF43, - XF86XK_Launch4: 0x1008FF44, - XF86XK_Launch5: 0x1008FF45, - XF86XK_Launch6: 0x1008FF46, - XF86XK_Launch7: 0x1008FF47, - XF86XK_Launch8: 0x1008FF48, - XF86XK_Launch9: 0x1008FF49, - XF86XK_LaunchA: 0x1008FF4A, - XF86XK_LaunchB: 0x1008FF4B, - XF86XK_LaunchC: 0x1008FF4C, - XF86XK_LaunchD: 0x1008FF4D, - XF86XK_LaunchE: 0x1008FF4E, - XF86XK_LaunchF: 0x1008FF4F, - XF86XK_ApplicationLeft: 0x1008FF50, - XF86XK_ApplicationRight: 0x1008FF51, - XF86XK_Book: 0x1008FF52, - XF86XK_CD: 0x1008FF53, - XF86XK_Calculater: 0x1008FF54, - XF86XK_Clear: 0x1008FF55, - XF86XK_Close: 0x1008FF56, - XF86XK_Copy: 0x1008FF57, - XF86XK_Cut: 0x1008FF58, - XF86XK_Display: 0x1008FF59, - XF86XK_DOS: 0x1008FF5A, - XF86XK_Documents: 0x1008FF5B, - XF86XK_Excel: 0x1008FF5C, - XF86XK_Explorer: 0x1008FF5D, - XF86XK_Game: 0x1008FF5E, - XF86XK_Go: 0x1008FF5F, - XF86XK_iTouch: 0x1008FF60, - XF86XK_LogOff: 0x1008FF61, - XF86XK_Market: 0x1008FF62, - XF86XK_Meeting: 0x1008FF63, - XF86XK_MenuKB: 0x1008FF65, - XF86XK_MenuPB: 0x1008FF66, - XF86XK_MySites: 0x1008FF67, - XF86XK_New: 0x1008FF68, - XF86XK_News: 0x1008FF69, - XF86XK_OfficeHome: 0x1008FF6A, - XF86XK_Open: 0x1008FF6B, - XF86XK_Option: 0x1008FF6C, - XF86XK_Paste: 0x1008FF6D, - XF86XK_Phone: 0x1008FF6E, - XF86XK_Q: 0x1008FF70, - XF86XK_Reply: 0x1008FF72, - XF86XK_Reload: 0x1008FF73, - XF86XK_RotateWindows: 0x1008FF74, - XF86XK_RotationPB: 0x1008FF75, - XF86XK_RotationKB: 0x1008FF76, - XF86XK_Save: 0x1008FF77, - XF86XK_ScrollUp: 0x1008FF78, - XF86XK_ScrollDown: 0x1008FF79, - XF86XK_ScrollClick: 0x1008FF7A, - XF86XK_Send: 0x1008FF7B, - XF86XK_Spell: 0x1008FF7C, - XF86XK_SplitScreen: 0x1008FF7D, - XF86XK_Support: 0x1008FF7E, - XF86XK_TaskPane: 0x1008FF7F, - XF86XK_Terminal: 0x1008FF80, - XF86XK_Tools: 0x1008FF81, - XF86XK_Travel: 0x1008FF82, - XF86XK_UserPB: 0x1008FF84, - XF86XK_User1KB: 0x1008FF85, - XF86XK_User2KB: 0x1008FF86, - XF86XK_Video: 0x1008FF87, - XF86XK_WheelButton: 0x1008FF88, - XF86XK_Word: 0x1008FF89, - XF86XK_Xfer: 0x1008FF8A, - XF86XK_ZoomIn: 0x1008FF8B, - XF86XK_ZoomOut: 0x1008FF8C, - XF86XK_Away: 0x1008FF8D, - XF86XK_Messenger: 0x1008FF8E, - XF86XK_WebCam: 0x1008FF8F, - XF86XK_MailForward: 0x1008FF90, - XF86XK_Pictures: 0x1008FF91, - XF86XK_Music: 0x1008FF92, - XF86XK_Battery: 0x1008FF93, - XF86XK_Bluetooth: 0x1008FF94, - XF86XK_WLAN: 0x1008FF95, - XF86XK_UWB: 0x1008FF96, - XF86XK_AudioForward: 0x1008FF97, - XF86XK_AudioRepeat: 0x1008FF98, - XF86XK_AudioRandomPlay: 0x1008FF99, - XF86XK_Subtitle: 0x1008FF9A, - XF86XK_AudioCycleTrack: 0x1008FF9B, - XF86XK_CycleAngle: 0x1008FF9C, - XF86XK_FrameBack: 0x1008FF9D, - XF86XK_FrameForward: 0x1008FF9E, - XF86XK_Time: 0x1008FF9F, - XF86XK_Select: 0x1008FFA0, - XF86XK_View: 0x1008FFA1, - XF86XK_TopMenu: 0x1008FFA2, - XF86XK_Red: 0x1008FFA3, - XF86XK_Green: 0x1008FFA4, - XF86XK_Yellow: 0x1008FFA5, - XF86XK_Blue: 0x1008FFA6, - XF86XK_Suspend: 0x1008FFA7, - XF86XK_Hibernate: 0x1008FFA8, - XF86XK_TouchpadToggle: 0x1008FFA9, - XF86XK_TouchpadOn: 0x1008FFB0, - XF86XK_TouchpadOff: 0x1008FFB1, - XF86XK_AudioMicMute: 0x1008FFB2, - XF86XK_Switch_VT_1: 0x1008FE01, - XF86XK_Switch_VT_2: 0x1008FE02, - XF86XK_Switch_VT_3: 0x1008FE03, - XF86XK_Switch_VT_4: 0x1008FE04, - XF86XK_Switch_VT_5: 0x1008FE05, - XF86XK_Switch_VT_6: 0x1008FE06, - XF86XK_Switch_VT_7: 0x1008FE07, - XF86XK_Switch_VT_8: 0x1008FE08, - XF86XK_Switch_VT_9: 0x1008FE09, - XF86XK_Switch_VT_10: 0x1008FE0A, - XF86XK_Switch_VT_11: 0x1008FE0B, - XF86XK_Switch_VT_12: 0x1008FE0C, - XF86XK_Ungrab: 0x1008FE20, - XF86XK_ClearGrab: 0x1008FE21, - XF86XK_Next_VMode: 0x1008FE22, - XF86XK_Prev_VMode: 0x1008FE23, - XF86XK_LogWindowTree: 0x1008FE24, - XF86XK_LogGrabInfo: 0x1008FE25, -}; diff --git a/base/app/novnc/core/input/keysymdef.js b/base/app/novnc/core/input/keysymdef.js deleted file mode 100644 index 951caca..0000000 --- a/base/app/novnc/core/input/keysymdef.js +++ /dev/null @@ -1,688 +0,0 @@ -/* - * Mapping from Unicode codepoints to X11/RFB keysyms - * - * This file was automatically generated from keysymdef.h - * DO NOT EDIT! - */ - -/* Functions at the bottom */ - -const codepoints = { - 0x0100: 0x03c0, // XK_Amacron - 0x0101: 0x03e0, // XK_amacron - 0x0102: 0x01c3, // XK_Abreve - 0x0103: 0x01e3, // XK_abreve - 0x0104: 0x01a1, // XK_Aogonek - 0x0105: 0x01b1, // XK_aogonek - 0x0106: 0x01c6, // XK_Cacute - 0x0107: 0x01e6, // XK_cacute - 0x0108: 0x02c6, // XK_Ccircumflex - 0x0109: 0x02e6, // XK_ccircumflex - 0x010a: 0x02c5, // XK_Cabovedot - 0x010b: 0x02e5, // XK_cabovedot - 0x010c: 0x01c8, // XK_Ccaron - 0x010d: 0x01e8, // XK_ccaron - 0x010e: 0x01cf, // XK_Dcaron - 0x010f: 0x01ef, // XK_dcaron - 0x0110: 0x01d0, // XK_Dstroke - 0x0111: 0x01f0, // XK_dstroke - 0x0112: 0x03aa, // XK_Emacron - 0x0113: 0x03ba, // XK_emacron - 0x0116: 0x03cc, // XK_Eabovedot - 0x0117: 0x03ec, // XK_eabovedot - 0x0118: 0x01ca, // XK_Eogonek - 0x0119: 0x01ea, // XK_eogonek - 0x011a: 0x01cc, // XK_Ecaron - 0x011b: 0x01ec, // XK_ecaron - 0x011c: 0x02d8, // XK_Gcircumflex - 0x011d: 0x02f8, // XK_gcircumflex - 0x011e: 0x02ab, // XK_Gbreve - 0x011f: 0x02bb, // XK_gbreve - 0x0120: 0x02d5, // XK_Gabovedot - 0x0121: 0x02f5, // XK_gabovedot - 0x0122: 0x03ab, // XK_Gcedilla - 0x0123: 0x03bb, // XK_gcedilla - 0x0124: 0x02a6, // XK_Hcircumflex - 0x0125: 0x02b6, // XK_hcircumflex - 0x0126: 0x02a1, // XK_Hstroke - 0x0127: 0x02b1, // XK_hstroke - 0x0128: 0x03a5, // XK_Itilde - 0x0129: 0x03b5, // XK_itilde - 0x012a: 0x03cf, // XK_Imacron - 0x012b: 0x03ef, // XK_imacron - 0x012e: 0x03c7, // XK_Iogonek - 0x012f: 0x03e7, // XK_iogonek - 0x0130: 0x02a9, // XK_Iabovedot - 0x0131: 0x02b9, // XK_idotless - 0x0134: 0x02ac, // XK_Jcircumflex - 0x0135: 0x02bc, // XK_jcircumflex - 0x0136: 0x03d3, // XK_Kcedilla - 0x0137: 0x03f3, // XK_kcedilla - 0x0138: 0x03a2, // XK_kra - 0x0139: 0x01c5, // XK_Lacute - 0x013a: 0x01e5, // XK_lacute - 0x013b: 0x03a6, // XK_Lcedilla - 0x013c: 0x03b6, // XK_lcedilla - 0x013d: 0x01a5, // XK_Lcaron - 0x013e: 0x01b5, // XK_lcaron - 0x0141: 0x01a3, // XK_Lstroke - 0x0142: 0x01b3, // XK_lstroke - 0x0143: 0x01d1, // XK_Nacute - 0x0144: 0x01f1, // XK_nacute - 0x0145: 0x03d1, // XK_Ncedilla - 0x0146: 0x03f1, // XK_ncedilla - 0x0147: 0x01d2, // XK_Ncaron - 0x0148: 0x01f2, // XK_ncaron - 0x014a: 0x03bd, // XK_ENG - 0x014b: 0x03bf, // XK_eng - 0x014c: 0x03d2, // XK_Omacron - 0x014d: 0x03f2, // XK_omacron - 0x0150: 0x01d5, // XK_Odoubleacute - 0x0151: 0x01f5, // XK_odoubleacute - 0x0152: 0x13bc, // XK_OE - 0x0153: 0x13bd, // XK_oe - 0x0154: 0x01c0, // XK_Racute - 0x0155: 0x01e0, // XK_racute - 0x0156: 0x03a3, // XK_Rcedilla - 0x0157: 0x03b3, // XK_rcedilla - 0x0158: 0x01d8, // XK_Rcaron - 0x0159: 0x01f8, // XK_rcaron - 0x015a: 0x01a6, // XK_Sacute - 0x015b: 0x01b6, // XK_sacute - 0x015c: 0x02de, // XK_Scircumflex - 0x015d: 0x02fe, // XK_scircumflex - 0x015e: 0x01aa, // XK_Scedilla - 0x015f: 0x01ba, // XK_scedilla - 0x0160: 0x01a9, // XK_Scaron - 0x0161: 0x01b9, // XK_scaron - 0x0162: 0x01de, // XK_Tcedilla - 0x0163: 0x01fe, // XK_tcedilla - 0x0164: 0x01ab, // XK_Tcaron - 0x0165: 0x01bb, // XK_tcaron - 0x0166: 0x03ac, // XK_Tslash - 0x0167: 0x03bc, // XK_tslash - 0x0168: 0x03dd, // XK_Utilde - 0x0169: 0x03fd, // XK_utilde - 0x016a: 0x03de, // XK_Umacron - 0x016b: 0x03fe, // XK_umacron - 0x016c: 0x02dd, // XK_Ubreve - 0x016d: 0x02fd, // XK_ubreve - 0x016e: 0x01d9, // XK_Uring - 0x016f: 0x01f9, // XK_uring - 0x0170: 0x01db, // XK_Udoubleacute - 0x0171: 0x01fb, // XK_udoubleacute - 0x0172: 0x03d9, // XK_Uogonek - 0x0173: 0x03f9, // XK_uogonek - 0x0178: 0x13be, // XK_Ydiaeresis - 0x0179: 0x01ac, // XK_Zacute - 0x017a: 0x01bc, // XK_zacute - 0x017b: 0x01af, // XK_Zabovedot - 0x017c: 0x01bf, // XK_zabovedot - 0x017d: 0x01ae, // XK_Zcaron - 0x017e: 0x01be, // XK_zcaron - 0x0192: 0x08f6, // XK_function - 0x01d2: 0x10001d1, // XK_Ocaron - 0x02c7: 0x01b7, // XK_caron - 0x02d8: 0x01a2, // XK_breve - 0x02d9: 0x01ff, // XK_abovedot - 0x02db: 0x01b2, // XK_ogonek - 0x02dd: 0x01bd, // XK_doubleacute - 0x0385: 0x07ae, // XK_Greek_accentdieresis - 0x0386: 0x07a1, // XK_Greek_ALPHAaccent - 0x0388: 0x07a2, // XK_Greek_EPSILONaccent - 0x0389: 0x07a3, // XK_Greek_ETAaccent - 0x038a: 0x07a4, // XK_Greek_IOTAaccent - 0x038c: 0x07a7, // XK_Greek_OMICRONaccent - 0x038e: 0x07a8, // XK_Greek_UPSILONaccent - 0x038f: 0x07ab, // XK_Greek_OMEGAaccent - 0x0390: 0x07b6, // XK_Greek_iotaaccentdieresis - 0x0391: 0x07c1, // XK_Greek_ALPHA - 0x0392: 0x07c2, // XK_Greek_BETA - 0x0393: 0x07c3, // XK_Greek_GAMMA - 0x0394: 0x07c4, // XK_Greek_DELTA - 0x0395: 0x07c5, // XK_Greek_EPSILON - 0x0396: 0x07c6, // XK_Greek_ZETA - 0x0397: 0x07c7, // XK_Greek_ETA - 0x0398: 0x07c8, // XK_Greek_THETA - 0x0399: 0x07c9, // XK_Greek_IOTA - 0x039a: 0x07ca, // XK_Greek_KAPPA - 0x039b: 0x07cb, // XK_Greek_LAMDA - 0x039c: 0x07cc, // XK_Greek_MU - 0x039d: 0x07cd, // XK_Greek_NU - 0x039e: 0x07ce, // XK_Greek_XI - 0x039f: 0x07cf, // XK_Greek_OMICRON - 0x03a0: 0x07d0, // XK_Greek_PI - 0x03a1: 0x07d1, // XK_Greek_RHO - 0x03a3: 0x07d2, // XK_Greek_SIGMA - 0x03a4: 0x07d4, // XK_Greek_TAU - 0x03a5: 0x07d5, // XK_Greek_UPSILON - 0x03a6: 0x07d6, // XK_Greek_PHI - 0x03a7: 0x07d7, // XK_Greek_CHI - 0x03a8: 0x07d8, // XK_Greek_PSI - 0x03a9: 0x07d9, // XK_Greek_OMEGA - 0x03aa: 0x07a5, // XK_Greek_IOTAdieresis - 0x03ab: 0x07a9, // XK_Greek_UPSILONdieresis - 0x03ac: 0x07b1, // XK_Greek_alphaaccent - 0x03ad: 0x07b2, // XK_Greek_epsilonaccent - 0x03ae: 0x07b3, // XK_Greek_etaaccent - 0x03af: 0x07b4, // XK_Greek_iotaaccent - 0x03b0: 0x07ba, // XK_Greek_upsilonaccentdieresis - 0x03b1: 0x07e1, // XK_Greek_alpha - 0x03b2: 0x07e2, // XK_Greek_beta - 0x03b3: 0x07e3, // XK_Greek_gamma - 0x03b4: 0x07e4, // XK_Greek_delta - 0x03b5: 0x07e5, // XK_Greek_epsilon - 0x03b6: 0x07e6, // XK_Greek_zeta - 0x03b7: 0x07e7, // XK_Greek_eta - 0x03b8: 0x07e8, // XK_Greek_theta - 0x03b9: 0x07e9, // XK_Greek_iota - 0x03ba: 0x07ea, // XK_Greek_kappa - 0x03bb: 0x07eb, // XK_Greek_lamda - 0x03bc: 0x07ec, // XK_Greek_mu - 0x03bd: 0x07ed, // XK_Greek_nu - 0x03be: 0x07ee, // XK_Greek_xi - 0x03bf: 0x07ef, // XK_Greek_omicron - 0x03c0: 0x07f0, // XK_Greek_pi - 0x03c1: 0x07f1, // XK_Greek_rho - 0x03c2: 0x07f3, // XK_Greek_finalsmallsigma - 0x03c3: 0x07f2, // XK_Greek_sigma - 0x03c4: 0x07f4, // XK_Greek_tau - 0x03c5: 0x07f5, // XK_Greek_upsilon - 0x03c6: 0x07f6, // XK_Greek_phi - 0x03c7: 0x07f7, // XK_Greek_chi - 0x03c8: 0x07f8, // XK_Greek_psi - 0x03c9: 0x07f9, // XK_Greek_omega - 0x03ca: 0x07b5, // XK_Greek_iotadieresis - 0x03cb: 0x07b9, // XK_Greek_upsilondieresis - 0x03cc: 0x07b7, // XK_Greek_omicronaccent - 0x03cd: 0x07b8, // XK_Greek_upsilonaccent - 0x03ce: 0x07bb, // XK_Greek_omegaaccent - 0x0401: 0x06b3, // XK_Cyrillic_IO - 0x0402: 0x06b1, // XK_Serbian_DJE - 0x0403: 0x06b2, // XK_Macedonia_GJE - 0x0404: 0x06b4, // XK_Ukrainian_IE - 0x0405: 0x06b5, // XK_Macedonia_DSE - 0x0406: 0x06b6, // XK_Ukrainian_I - 0x0407: 0x06b7, // XK_Ukrainian_YI - 0x0408: 0x06b8, // XK_Cyrillic_JE - 0x0409: 0x06b9, // XK_Cyrillic_LJE - 0x040a: 0x06ba, // XK_Cyrillic_NJE - 0x040b: 0x06bb, // XK_Serbian_TSHE - 0x040c: 0x06bc, // XK_Macedonia_KJE - 0x040e: 0x06be, // XK_Byelorussian_SHORTU - 0x040f: 0x06bf, // XK_Cyrillic_DZHE - 0x0410: 0x06e1, // XK_Cyrillic_A - 0x0411: 0x06e2, // XK_Cyrillic_BE - 0x0412: 0x06f7, // XK_Cyrillic_VE - 0x0413: 0x06e7, // XK_Cyrillic_GHE - 0x0414: 0x06e4, // XK_Cyrillic_DE - 0x0415: 0x06e5, // XK_Cyrillic_IE - 0x0416: 0x06f6, // XK_Cyrillic_ZHE - 0x0417: 0x06fa, // XK_Cyrillic_ZE - 0x0418: 0x06e9, // XK_Cyrillic_I - 0x0419: 0x06ea, // XK_Cyrillic_SHORTI - 0x041a: 0x06eb, // XK_Cyrillic_KA - 0x041b: 0x06ec, // XK_Cyrillic_EL - 0x041c: 0x06ed, // XK_Cyrillic_EM - 0x041d: 0x06ee, // XK_Cyrillic_EN - 0x041e: 0x06ef, // XK_Cyrillic_O - 0x041f: 0x06f0, // XK_Cyrillic_PE - 0x0420: 0x06f2, // XK_Cyrillic_ER - 0x0421: 0x06f3, // XK_Cyrillic_ES - 0x0422: 0x06f4, // XK_Cyrillic_TE - 0x0423: 0x06f5, // XK_Cyrillic_U - 0x0424: 0x06e6, // XK_Cyrillic_EF - 0x0425: 0x06e8, // XK_Cyrillic_HA - 0x0426: 0x06e3, // XK_Cyrillic_TSE - 0x0427: 0x06fe, // XK_Cyrillic_CHE - 0x0428: 0x06fb, // XK_Cyrillic_SHA - 0x0429: 0x06fd, // XK_Cyrillic_SHCHA - 0x042a: 0x06ff, // XK_Cyrillic_HARDSIGN - 0x042b: 0x06f9, // XK_Cyrillic_YERU - 0x042c: 0x06f8, // XK_Cyrillic_SOFTSIGN - 0x042d: 0x06fc, // XK_Cyrillic_E - 0x042e: 0x06e0, // XK_Cyrillic_YU - 0x042f: 0x06f1, // XK_Cyrillic_YA - 0x0430: 0x06c1, // XK_Cyrillic_a - 0x0431: 0x06c2, // XK_Cyrillic_be - 0x0432: 0x06d7, // XK_Cyrillic_ve - 0x0433: 0x06c7, // XK_Cyrillic_ghe - 0x0434: 0x06c4, // XK_Cyrillic_de - 0x0435: 0x06c5, // XK_Cyrillic_ie - 0x0436: 0x06d6, // XK_Cyrillic_zhe - 0x0437: 0x06da, // XK_Cyrillic_ze - 0x0438: 0x06c9, // XK_Cyrillic_i - 0x0439: 0x06ca, // XK_Cyrillic_shorti - 0x043a: 0x06cb, // XK_Cyrillic_ka - 0x043b: 0x06cc, // XK_Cyrillic_el - 0x043c: 0x06cd, // XK_Cyrillic_em - 0x043d: 0x06ce, // XK_Cyrillic_en - 0x043e: 0x06cf, // XK_Cyrillic_o - 0x043f: 0x06d0, // XK_Cyrillic_pe - 0x0440: 0x06d2, // XK_Cyrillic_er - 0x0441: 0x06d3, // XK_Cyrillic_es - 0x0442: 0x06d4, // XK_Cyrillic_te - 0x0443: 0x06d5, // XK_Cyrillic_u - 0x0444: 0x06c6, // XK_Cyrillic_ef - 0x0445: 0x06c8, // XK_Cyrillic_ha - 0x0446: 0x06c3, // XK_Cyrillic_tse - 0x0447: 0x06de, // XK_Cyrillic_che - 0x0448: 0x06db, // XK_Cyrillic_sha - 0x0449: 0x06dd, // XK_Cyrillic_shcha - 0x044a: 0x06df, // XK_Cyrillic_hardsign - 0x044b: 0x06d9, // XK_Cyrillic_yeru - 0x044c: 0x06d8, // XK_Cyrillic_softsign - 0x044d: 0x06dc, // XK_Cyrillic_e - 0x044e: 0x06c0, // XK_Cyrillic_yu - 0x044f: 0x06d1, // XK_Cyrillic_ya - 0x0451: 0x06a3, // XK_Cyrillic_io - 0x0452: 0x06a1, // XK_Serbian_dje - 0x0453: 0x06a2, // XK_Macedonia_gje - 0x0454: 0x06a4, // XK_Ukrainian_ie - 0x0455: 0x06a5, // XK_Macedonia_dse - 0x0456: 0x06a6, // XK_Ukrainian_i - 0x0457: 0x06a7, // XK_Ukrainian_yi - 0x0458: 0x06a8, // XK_Cyrillic_je - 0x0459: 0x06a9, // XK_Cyrillic_lje - 0x045a: 0x06aa, // XK_Cyrillic_nje - 0x045b: 0x06ab, // XK_Serbian_tshe - 0x045c: 0x06ac, // XK_Macedonia_kje - 0x045e: 0x06ae, // XK_Byelorussian_shortu - 0x045f: 0x06af, // XK_Cyrillic_dzhe - 0x0490: 0x06bd, // XK_Ukrainian_GHE_WITH_UPTURN - 0x0491: 0x06ad, // XK_Ukrainian_ghe_with_upturn - 0x05d0: 0x0ce0, // XK_hebrew_aleph - 0x05d1: 0x0ce1, // XK_hebrew_bet - 0x05d2: 0x0ce2, // XK_hebrew_gimel - 0x05d3: 0x0ce3, // XK_hebrew_dalet - 0x05d4: 0x0ce4, // XK_hebrew_he - 0x05d5: 0x0ce5, // XK_hebrew_waw - 0x05d6: 0x0ce6, // XK_hebrew_zain - 0x05d7: 0x0ce7, // XK_hebrew_chet - 0x05d8: 0x0ce8, // XK_hebrew_tet - 0x05d9: 0x0ce9, // XK_hebrew_yod - 0x05da: 0x0cea, // XK_hebrew_finalkaph - 0x05db: 0x0ceb, // XK_hebrew_kaph - 0x05dc: 0x0cec, // XK_hebrew_lamed - 0x05dd: 0x0ced, // XK_hebrew_finalmem - 0x05de: 0x0cee, // XK_hebrew_mem - 0x05df: 0x0cef, // XK_hebrew_finalnun - 0x05e0: 0x0cf0, // XK_hebrew_nun - 0x05e1: 0x0cf1, // XK_hebrew_samech - 0x05e2: 0x0cf2, // XK_hebrew_ayin - 0x05e3: 0x0cf3, // XK_hebrew_finalpe - 0x05e4: 0x0cf4, // XK_hebrew_pe - 0x05e5: 0x0cf5, // XK_hebrew_finalzade - 0x05e6: 0x0cf6, // XK_hebrew_zade - 0x05e7: 0x0cf7, // XK_hebrew_qoph - 0x05e8: 0x0cf8, // XK_hebrew_resh - 0x05e9: 0x0cf9, // XK_hebrew_shin - 0x05ea: 0x0cfa, // XK_hebrew_taw - 0x060c: 0x05ac, // XK_Arabic_comma - 0x061b: 0x05bb, // XK_Arabic_semicolon - 0x061f: 0x05bf, // XK_Arabic_question_mark - 0x0621: 0x05c1, // XK_Arabic_hamza - 0x0622: 0x05c2, // XK_Arabic_maddaonalef - 0x0623: 0x05c3, // XK_Arabic_hamzaonalef - 0x0624: 0x05c4, // XK_Arabic_hamzaonwaw - 0x0625: 0x05c5, // XK_Arabic_hamzaunderalef - 0x0626: 0x05c6, // XK_Arabic_hamzaonyeh - 0x0627: 0x05c7, // XK_Arabic_alef - 0x0628: 0x05c8, // XK_Arabic_beh - 0x0629: 0x05c9, // XK_Arabic_tehmarbuta - 0x062a: 0x05ca, // XK_Arabic_teh - 0x062b: 0x05cb, // XK_Arabic_theh - 0x062c: 0x05cc, // XK_Arabic_jeem - 0x062d: 0x05cd, // XK_Arabic_hah - 0x062e: 0x05ce, // XK_Arabic_khah - 0x062f: 0x05cf, // XK_Arabic_dal - 0x0630: 0x05d0, // XK_Arabic_thal - 0x0631: 0x05d1, // XK_Arabic_ra - 0x0632: 0x05d2, // XK_Arabic_zain - 0x0633: 0x05d3, // XK_Arabic_seen - 0x0634: 0x05d4, // XK_Arabic_sheen - 0x0635: 0x05d5, // XK_Arabic_sad - 0x0636: 0x05d6, // XK_Arabic_dad - 0x0637: 0x05d7, // XK_Arabic_tah - 0x0638: 0x05d8, // XK_Arabic_zah - 0x0639: 0x05d9, // XK_Arabic_ain - 0x063a: 0x05da, // XK_Arabic_ghain - 0x0640: 0x05e0, // XK_Arabic_tatweel - 0x0641: 0x05e1, // XK_Arabic_feh - 0x0642: 0x05e2, // XK_Arabic_qaf - 0x0643: 0x05e3, // XK_Arabic_kaf - 0x0644: 0x05e4, // XK_Arabic_lam - 0x0645: 0x05e5, // XK_Arabic_meem - 0x0646: 0x05e6, // XK_Arabic_noon - 0x0647: 0x05e7, // XK_Arabic_ha - 0x0648: 0x05e8, // XK_Arabic_waw - 0x0649: 0x05e9, // XK_Arabic_alefmaksura - 0x064a: 0x05ea, // XK_Arabic_yeh - 0x064b: 0x05eb, // XK_Arabic_fathatan - 0x064c: 0x05ec, // XK_Arabic_dammatan - 0x064d: 0x05ed, // XK_Arabic_kasratan - 0x064e: 0x05ee, // XK_Arabic_fatha - 0x064f: 0x05ef, // XK_Arabic_damma - 0x0650: 0x05f0, // XK_Arabic_kasra - 0x0651: 0x05f1, // XK_Arabic_shadda - 0x0652: 0x05f2, // XK_Arabic_sukun - 0x0e01: 0x0da1, // XK_Thai_kokai - 0x0e02: 0x0da2, // XK_Thai_khokhai - 0x0e03: 0x0da3, // XK_Thai_khokhuat - 0x0e04: 0x0da4, // XK_Thai_khokhwai - 0x0e05: 0x0da5, // XK_Thai_khokhon - 0x0e06: 0x0da6, // XK_Thai_khorakhang - 0x0e07: 0x0da7, // XK_Thai_ngongu - 0x0e08: 0x0da8, // XK_Thai_chochan - 0x0e09: 0x0da9, // XK_Thai_choching - 0x0e0a: 0x0daa, // XK_Thai_chochang - 0x0e0b: 0x0dab, // XK_Thai_soso - 0x0e0c: 0x0dac, // XK_Thai_chochoe - 0x0e0d: 0x0dad, // XK_Thai_yoying - 0x0e0e: 0x0dae, // XK_Thai_dochada - 0x0e0f: 0x0daf, // XK_Thai_topatak - 0x0e10: 0x0db0, // XK_Thai_thothan - 0x0e11: 0x0db1, // XK_Thai_thonangmontho - 0x0e12: 0x0db2, // XK_Thai_thophuthao - 0x0e13: 0x0db3, // XK_Thai_nonen - 0x0e14: 0x0db4, // XK_Thai_dodek - 0x0e15: 0x0db5, // XK_Thai_totao - 0x0e16: 0x0db6, // XK_Thai_thothung - 0x0e17: 0x0db7, // XK_Thai_thothahan - 0x0e18: 0x0db8, // XK_Thai_thothong - 0x0e19: 0x0db9, // XK_Thai_nonu - 0x0e1a: 0x0dba, // XK_Thai_bobaimai - 0x0e1b: 0x0dbb, // XK_Thai_popla - 0x0e1c: 0x0dbc, // XK_Thai_phophung - 0x0e1d: 0x0dbd, // XK_Thai_fofa - 0x0e1e: 0x0dbe, // XK_Thai_phophan - 0x0e1f: 0x0dbf, // XK_Thai_fofan - 0x0e20: 0x0dc0, // XK_Thai_phosamphao - 0x0e21: 0x0dc1, // XK_Thai_moma - 0x0e22: 0x0dc2, // XK_Thai_yoyak - 0x0e23: 0x0dc3, // XK_Thai_rorua - 0x0e24: 0x0dc4, // XK_Thai_ru - 0x0e25: 0x0dc5, // XK_Thai_loling - 0x0e26: 0x0dc6, // XK_Thai_lu - 0x0e27: 0x0dc7, // XK_Thai_wowaen - 0x0e28: 0x0dc8, // XK_Thai_sosala - 0x0e29: 0x0dc9, // XK_Thai_sorusi - 0x0e2a: 0x0dca, // XK_Thai_sosua - 0x0e2b: 0x0dcb, // XK_Thai_hohip - 0x0e2c: 0x0dcc, // XK_Thai_lochula - 0x0e2d: 0x0dcd, // XK_Thai_oang - 0x0e2e: 0x0dce, // XK_Thai_honokhuk - 0x0e2f: 0x0dcf, // XK_Thai_paiyannoi - 0x0e30: 0x0dd0, // XK_Thai_saraa - 0x0e31: 0x0dd1, // XK_Thai_maihanakat - 0x0e32: 0x0dd2, // XK_Thai_saraaa - 0x0e33: 0x0dd3, // XK_Thai_saraam - 0x0e34: 0x0dd4, // XK_Thai_sarai - 0x0e35: 0x0dd5, // XK_Thai_saraii - 0x0e36: 0x0dd6, // XK_Thai_saraue - 0x0e37: 0x0dd7, // XK_Thai_sarauee - 0x0e38: 0x0dd8, // XK_Thai_sarau - 0x0e39: 0x0dd9, // XK_Thai_sarauu - 0x0e3a: 0x0dda, // XK_Thai_phinthu - 0x0e3f: 0x0ddf, // XK_Thai_baht - 0x0e40: 0x0de0, // XK_Thai_sarae - 0x0e41: 0x0de1, // XK_Thai_saraae - 0x0e42: 0x0de2, // XK_Thai_sarao - 0x0e43: 0x0de3, // XK_Thai_saraaimaimuan - 0x0e44: 0x0de4, // XK_Thai_saraaimaimalai - 0x0e45: 0x0de5, // XK_Thai_lakkhangyao - 0x0e46: 0x0de6, // XK_Thai_maiyamok - 0x0e47: 0x0de7, // XK_Thai_maitaikhu - 0x0e48: 0x0de8, // XK_Thai_maiek - 0x0e49: 0x0de9, // XK_Thai_maitho - 0x0e4a: 0x0dea, // XK_Thai_maitri - 0x0e4b: 0x0deb, // XK_Thai_maichattawa - 0x0e4c: 0x0dec, // XK_Thai_thanthakhat - 0x0e4d: 0x0ded, // XK_Thai_nikhahit - 0x0e50: 0x0df0, // XK_Thai_leksun - 0x0e51: 0x0df1, // XK_Thai_leknung - 0x0e52: 0x0df2, // XK_Thai_leksong - 0x0e53: 0x0df3, // XK_Thai_leksam - 0x0e54: 0x0df4, // XK_Thai_leksi - 0x0e55: 0x0df5, // XK_Thai_lekha - 0x0e56: 0x0df6, // XK_Thai_lekhok - 0x0e57: 0x0df7, // XK_Thai_lekchet - 0x0e58: 0x0df8, // XK_Thai_lekpaet - 0x0e59: 0x0df9, // XK_Thai_lekkao - 0x2002: 0x0aa2, // XK_enspace - 0x2003: 0x0aa1, // XK_emspace - 0x2004: 0x0aa3, // XK_em3space - 0x2005: 0x0aa4, // XK_em4space - 0x2007: 0x0aa5, // XK_digitspace - 0x2008: 0x0aa6, // XK_punctspace - 0x2009: 0x0aa7, // XK_thinspace - 0x200a: 0x0aa8, // XK_hairspace - 0x2012: 0x0abb, // XK_figdash - 0x2013: 0x0aaa, // XK_endash - 0x2014: 0x0aa9, // XK_emdash - 0x2015: 0x07af, // XK_Greek_horizbar - 0x2017: 0x0cdf, // XK_hebrew_doublelowline - 0x2018: 0x0ad0, // XK_leftsinglequotemark - 0x2019: 0x0ad1, // XK_rightsinglequotemark - 0x201a: 0x0afd, // XK_singlelowquotemark - 0x201c: 0x0ad2, // XK_leftdoublequotemark - 0x201d: 0x0ad3, // XK_rightdoublequotemark - 0x201e: 0x0afe, // XK_doublelowquotemark - 0x2020: 0x0af1, // XK_dagger - 0x2021: 0x0af2, // XK_doubledagger - 0x2022: 0x0ae6, // XK_enfilledcircbullet - 0x2025: 0x0aaf, // XK_doubbaselinedot - 0x2026: 0x0aae, // XK_ellipsis - 0x2030: 0x0ad5, // XK_permille - 0x2032: 0x0ad6, // XK_minutes - 0x2033: 0x0ad7, // XK_seconds - 0x2038: 0x0afc, // XK_caret - 0x203e: 0x047e, // XK_overline - 0x20a9: 0x0eff, // XK_Korean_Won - 0x20ac: 0x20ac, // XK_EuroSign - 0x2105: 0x0ab8, // XK_careof - 0x2116: 0x06b0, // XK_numerosign - 0x2117: 0x0afb, // XK_phonographcopyright - 0x211e: 0x0ad4, // XK_prescription - 0x2122: 0x0ac9, // XK_trademark - 0x2153: 0x0ab0, // XK_onethird - 0x2154: 0x0ab1, // XK_twothirds - 0x2155: 0x0ab2, // XK_onefifth - 0x2156: 0x0ab3, // XK_twofifths - 0x2157: 0x0ab4, // XK_threefifths - 0x2158: 0x0ab5, // XK_fourfifths - 0x2159: 0x0ab6, // XK_onesixth - 0x215a: 0x0ab7, // XK_fivesixths - 0x215b: 0x0ac3, // XK_oneeighth - 0x215c: 0x0ac4, // XK_threeeighths - 0x215d: 0x0ac5, // XK_fiveeighths - 0x215e: 0x0ac6, // XK_seveneighths - 0x2190: 0x08fb, // XK_leftarrow - 0x2191: 0x08fc, // XK_uparrow - 0x2192: 0x08fd, // XK_rightarrow - 0x2193: 0x08fe, // XK_downarrow - 0x21d2: 0x08ce, // XK_implies - 0x21d4: 0x08cd, // XK_ifonlyif - 0x2202: 0x08ef, // XK_partialderivative - 0x2207: 0x08c5, // XK_nabla - 0x2218: 0x0bca, // XK_jot - 0x221a: 0x08d6, // XK_radical - 0x221d: 0x08c1, // XK_variation - 0x221e: 0x08c2, // XK_infinity - 0x2227: 0x08de, // XK_logicaland - 0x2228: 0x08df, // XK_logicalor - 0x2229: 0x08dc, // XK_intersection - 0x222a: 0x08dd, // XK_union - 0x222b: 0x08bf, // XK_integral - 0x2234: 0x08c0, // XK_therefore - 0x223c: 0x08c8, // XK_approximate - 0x2243: 0x08c9, // XK_similarequal - 0x2245: 0x1002248, // XK_approxeq - 0x2260: 0x08bd, // XK_notequal - 0x2261: 0x08cf, // XK_identical - 0x2264: 0x08bc, // XK_lessthanequal - 0x2265: 0x08be, // XK_greaterthanequal - 0x2282: 0x08da, // XK_includedin - 0x2283: 0x08db, // XK_includes - 0x22a2: 0x0bfc, // XK_righttack - 0x22a3: 0x0bdc, // XK_lefttack - 0x22a4: 0x0bc2, // XK_downtack - 0x22a5: 0x0bce, // XK_uptack - 0x2308: 0x0bd3, // XK_upstile - 0x230a: 0x0bc4, // XK_downstile - 0x2315: 0x0afa, // XK_telephonerecorder - 0x2320: 0x08a4, // XK_topintegral - 0x2321: 0x08a5, // XK_botintegral - 0x2395: 0x0bcc, // XK_quad - 0x239b: 0x08ab, // XK_topleftparens - 0x239d: 0x08ac, // XK_botleftparens - 0x239e: 0x08ad, // XK_toprightparens - 0x23a0: 0x08ae, // XK_botrightparens - 0x23a1: 0x08a7, // XK_topleftsqbracket - 0x23a3: 0x08a8, // XK_botleftsqbracket - 0x23a4: 0x08a9, // XK_toprightsqbracket - 0x23a6: 0x08aa, // XK_botrightsqbracket - 0x23a8: 0x08af, // XK_leftmiddlecurlybrace - 0x23ac: 0x08b0, // XK_rightmiddlecurlybrace - 0x23b7: 0x08a1, // XK_leftradical - 0x23ba: 0x09ef, // XK_horizlinescan1 - 0x23bb: 0x09f0, // XK_horizlinescan3 - 0x23bc: 0x09f2, // XK_horizlinescan7 - 0x23bd: 0x09f3, // XK_horizlinescan9 - 0x2409: 0x09e2, // XK_ht - 0x240a: 0x09e5, // XK_lf - 0x240b: 0x09e9, // XK_vt - 0x240c: 0x09e3, // XK_ff - 0x240d: 0x09e4, // XK_cr - 0x2423: 0x0aac, // XK_signifblank - 0x2424: 0x09e8, // XK_nl - 0x2500: 0x08a3, // XK_horizconnector - 0x2502: 0x08a6, // XK_vertconnector - 0x250c: 0x08a2, // XK_topleftradical - 0x2510: 0x09eb, // XK_uprightcorner - 0x2514: 0x09ed, // XK_lowleftcorner - 0x2518: 0x09ea, // XK_lowrightcorner - 0x251c: 0x09f4, // XK_leftt - 0x2524: 0x09f5, // XK_rightt - 0x252c: 0x09f7, // XK_topt - 0x2534: 0x09f6, // XK_bott - 0x253c: 0x09ee, // XK_crossinglines - 0x2592: 0x09e1, // XK_checkerboard - 0x25aa: 0x0ae7, // XK_enfilledsqbullet - 0x25ab: 0x0ae1, // XK_enopensquarebullet - 0x25ac: 0x0adb, // XK_filledrectbullet - 0x25ad: 0x0ae2, // XK_openrectbullet - 0x25ae: 0x0adf, // XK_emfilledrect - 0x25af: 0x0acf, // XK_emopenrectangle - 0x25b2: 0x0ae8, // XK_filledtribulletup - 0x25b3: 0x0ae3, // XK_opentribulletup - 0x25b6: 0x0add, // XK_filledrighttribullet - 0x25b7: 0x0acd, // XK_rightopentriangle - 0x25bc: 0x0ae9, // XK_filledtribulletdown - 0x25bd: 0x0ae4, // XK_opentribulletdown - 0x25c0: 0x0adc, // XK_filledlefttribullet - 0x25c1: 0x0acc, // XK_leftopentriangle - 0x25c6: 0x09e0, // XK_soliddiamond - 0x25cb: 0x0ace, // XK_emopencircle - 0x25cf: 0x0ade, // XK_emfilledcircle - 0x25e6: 0x0ae0, // XK_enopencircbullet - 0x2606: 0x0ae5, // XK_openstar - 0x260e: 0x0af9, // XK_telephone - 0x2613: 0x0aca, // XK_signaturemark - 0x261c: 0x0aea, // XK_leftpointer - 0x261e: 0x0aeb, // XK_rightpointer - 0x2640: 0x0af8, // XK_femalesymbol - 0x2642: 0x0af7, // XK_malesymbol - 0x2663: 0x0aec, // XK_club - 0x2665: 0x0aee, // XK_heart - 0x2666: 0x0aed, // XK_diamond - 0x266d: 0x0af6, // XK_musicalflat - 0x266f: 0x0af5, // XK_musicalsharp - 0x2713: 0x0af3, // XK_checkmark - 0x2717: 0x0af4, // XK_ballotcross - 0x271d: 0x0ad9, // XK_latincross - 0x2720: 0x0af0, // XK_maltesecross - 0x27e8: 0x0abc, // XK_leftanglebracket - 0x27e9: 0x0abe, // XK_rightanglebracket - 0x3001: 0x04a4, // XK_kana_comma - 0x3002: 0x04a1, // XK_kana_fullstop - 0x300c: 0x04a2, // XK_kana_openingbracket - 0x300d: 0x04a3, // XK_kana_closingbracket - 0x309b: 0x04de, // XK_voicedsound - 0x309c: 0x04df, // XK_semivoicedsound - 0x30a1: 0x04a7, // XK_kana_a - 0x30a2: 0x04b1, // XK_kana_A - 0x30a3: 0x04a8, // XK_kana_i - 0x30a4: 0x04b2, // XK_kana_I - 0x30a5: 0x04a9, // XK_kana_u - 0x30a6: 0x04b3, // XK_kana_U - 0x30a7: 0x04aa, // XK_kana_e - 0x30a8: 0x04b4, // XK_kana_E - 0x30a9: 0x04ab, // XK_kana_o - 0x30aa: 0x04b5, // XK_kana_O - 0x30ab: 0x04b6, // XK_kana_KA - 0x30ad: 0x04b7, // XK_kana_KI - 0x30af: 0x04b8, // XK_kana_KU - 0x30b1: 0x04b9, // XK_kana_KE - 0x30b3: 0x04ba, // XK_kana_KO - 0x30b5: 0x04bb, // XK_kana_SA - 0x30b7: 0x04bc, // XK_kana_SHI - 0x30b9: 0x04bd, // XK_kana_SU - 0x30bb: 0x04be, // XK_kana_SE - 0x30bd: 0x04bf, // XK_kana_SO - 0x30bf: 0x04c0, // XK_kana_TA - 0x30c1: 0x04c1, // XK_kana_CHI - 0x30c3: 0x04af, // XK_kana_tsu - 0x30c4: 0x04c2, // XK_kana_TSU - 0x30c6: 0x04c3, // XK_kana_TE - 0x30c8: 0x04c4, // XK_kana_TO - 0x30ca: 0x04c5, // XK_kana_NA - 0x30cb: 0x04c6, // XK_kana_NI - 0x30cc: 0x04c7, // XK_kana_NU - 0x30cd: 0x04c8, // XK_kana_NE - 0x30ce: 0x04c9, // XK_kana_NO - 0x30cf: 0x04ca, // XK_kana_HA - 0x30d2: 0x04cb, // XK_kana_HI - 0x30d5: 0x04cc, // XK_kana_FU - 0x30d8: 0x04cd, // XK_kana_HE - 0x30db: 0x04ce, // XK_kana_HO - 0x30de: 0x04cf, // XK_kana_MA - 0x30df: 0x04d0, // XK_kana_MI - 0x30e0: 0x04d1, // XK_kana_MU - 0x30e1: 0x04d2, // XK_kana_ME - 0x30e2: 0x04d3, // XK_kana_MO - 0x30e3: 0x04ac, // XK_kana_ya - 0x30e4: 0x04d4, // XK_kana_YA - 0x30e5: 0x04ad, // XK_kana_yu - 0x30e6: 0x04d5, // XK_kana_YU - 0x30e7: 0x04ae, // XK_kana_yo - 0x30e8: 0x04d6, // XK_kana_YO - 0x30e9: 0x04d7, // XK_kana_RA - 0x30ea: 0x04d8, // XK_kana_RI - 0x30eb: 0x04d9, // XK_kana_RU - 0x30ec: 0x04da, // XK_kana_RE - 0x30ed: 0x04db, // XK_kana_RO - 0x30ef: 0x04dc, // XK_kana_WA - 0x30f2: 0x04a6, // XK_kana_WO - 0x30f3: 0x04dd, // XK_kana_N - 0x30fb: 0x04a5, // XK_kana_conjunctive - 0x30fc: 0x04b0, // XK_prolongedsound -}; - -export default { - lookup(u) { - // Latin-1 is one-to-one mapping - if ((u >= 0x20) && (u <= 0xff)) { - return u; - } - - // Lookup table (fairly random) - const keysym = codepoints[u]; - if (keysym !== undefined) { - return keysym; - } - - // General mapping as final fallback - return 0x01000000 | u; - }, -}; diff --git a/base/app/novnc/core/input/util.js b/base/app/novnc/core/input/util.js deleted file mode 100644 index 36b6981..0000000 --- a/base/app/novnc/core/input/util.js +++ /dev/null @@ -1,191 +0,0 @@ -import KeyTable from "./keysym.js"; -import keysyms from "./keysymdef.js"; -import vkeys from "./vkeys.js"; -import fixedkeys from "./fixedkeys.js"; -import DOMKeyTable from "./domkeytable.js"; -import * as browser from "../util/browser.js"; - -// Get 'KeyboardEvent.code', handling legacy browsers -export function getKeycode(evt) { - // Are we getting proper key identifiers? - // (unfortunately Firefox and Chrome are crappy here and gives - // us an empty string on some platforms, rather than leaving it - // undefined) - if (evt.code) { - // Mozilla isn't fully in sync with the spec yet - switch (evt.code) { - case 'OSLeft': return 'MetaLeft'; - case 'OSRight': return 'MetaRight'; - } - - return evt.code; - } - - // The de-facto standard is to use Windows Virtual-Key codes - // in the 'keyCode' field for non-printable characters - if (evt.keyCode in vkeys) { - let code = vkeys[evt.keyCode]; - - // macOS has messed up this code for some reason - if (browser.isMac() && (code === 'ContextMenu')) { - code = 'MetaRight'; - } - - // The keyCode doesn't distinguish between left and right - // for the standard modifiers - if (evt.location === 2) { - switch (code) { - case 'ShiftLeft': return 'ShiftRight'; - case 'ControlLeft': return 'ControlRight'; - case 'AltLeft': return 'AltRight'; - } - } - - // Nor a bunch of the numpad keys - if (evt.location === 3) { - switch (code) { - case 'Delete': return 'NumpadDecimal'; - case 'Insert': return 'Numpad0'; - case 'End': return 'Numpad1'; - case 'ArrowDown': return 'Numpad2'; - case 'PageDown': return 'Numpad3'; - case 'ArrowLeft': return 'Numpad4'; - case 'ArrowRight': return 'Numpad6'; - case 'Home': return 'Numpad7'; - case 'ArrowUp': return 'Numpad8'; - case 'PageUp': return 'Numpad9'; - case 'Enter': return 'NumpadEnter'; - } - } - - return code; - } - - return 'Unidentified'; -} - -// Get 'KeyboardEvent.key', handling legacy browsers -export function getKey(evt) { - // Are we getting a proper key value? - if ((evt.key !== undefined) && (evt.key !== 'Unidentified')) { - // Mozilla isn't fully in sync with the spec yet - switch (evt.key) { - case 'OS': return 'Meta'; - case 'LaunchMyComputer': return 'LaunchApplication1'; - case 'LaunchCalculator': return 'LaunchApplication2'; - } - - // iOS leaks some OS names - switch (evt.key) { - case 'UIKeyInputUpArrow': return 'ArrowUp'; - case 'UIKeyInputDownArrow': return 'ArrowDown'; - case 'UIKeyInputLeftArrow': return 'ArrowLeft'; - case 'UIKeyInputRightArrow': return 'ArrowRight'; - case 'UIKeyInputEscape': return 'Escape'; - } - - // Broken behaviour in Chrome - if ((evt.key === '\x00') && (evt.code === 'NumpadDecimal')) { - return 'Delete'; - } - - return evt.key; - } - - // Try to deduce it based on the physical key - const code = getKeycode(evt); - if (code in fixedkeys) { - return fixedkeys[code]; - } - - // If that failed, then see if we have a printable character - if (evt.charCode) { - return String.fromCharCode(evt.charCode); - } - - // At this point we have nothing left to go on - return 'Unidentified'; -} - -// Get the most reliable keysym value we can get from a key event -export function getKeysym(evt) { - const key = getKey(evt); - - if (key === 'Unidentified') { - return null; - } - - // First look up special keys - if (key in DOMKeyTable) { - let location = evt.location; - - // Safari screws up location for the right cmd key - if ((key === 'Meta') && (location === 0)) { - location = 2; - } - - // And for Clear - if ((key === 'Clear') && (location === 3)) { - let code = getKeycode(evt); - if (code === 'NumLock') { - location = 0; - } - } - - if ((location === undefined) || (location > 3)) { - location = 0; - } - - // The original Meta key now gets confused with the Windows key - // https://bugs.chromium.org/p/chromium/issues/detail?id=1020141 - // https://bugzilla.mozilla.org/show_bug.cgi?id=1232918 - if (key === 'Meta') { - let code = getKeycode(evt); - if (code === 'AltLeft') { - return KeyTable.XK_Meta_L; - } else if (code === 'AltRight') { - return KeyTable.XK_Meta_R; - } - } - - // macOS has Clear instead of NumLock, but the remote system is - // probably not macOS, so lying here is probably best... - if (key === 'Clear') { - let code = getKeycode(evt); - if (code === 'NumLock') { - return KeyTable.XK_Num_Lock; - } - } - - // Windows sends alternating symbols for some keys when using a - // Japanese layout. We have no way of synchronising with the IM - // running on the remote system, so we send some combined keysym - // instead and hope for the best. - if (browser.isWindows()) { - switch (key) { - case 'Zenkaku': - case 'Hankaku': - return KeyTable.XK_Zenkaku_Hankaku; - case 'Romaji': - case 'KanaMode': - return KeyTable.XK_Romaji; - } - } - - return DOMKeyTable[key][location]; - } - - // Now we need to look at the Unicode symbol instead - - // Special key? (FIXME: Should have been caught earlier) - if (key.length !== 1) { - return null; - } - - const codepoint = key.charCodeAt(); - if (codepoint) { - return keysyms.lookup(codepoint); - } - - return null; -} diff --git a/base/app/novnc/core/input/vkeys.js b/base/app/novnc/core/input/vkeys.js deleted file mode 100644 index dacc358..0000000 --- a/base/app/novnc/core/input/vkeys.js +++ /dev/null @@ -1,116 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2018 The noVNC Authors - * Licensed under MPL 2.0 or any later version (see LICENSE.txt) - */ - -/* - * Mapping between Microsoft® Windows® Virtual-Key codes and - * HTML key codes. - */ - -export default { - 0x08: 'Backspace', - 0x09: 'Tab', - 0x0a: 'NumpadClear', - 0x0d: 'Enter', - 0x10: 'ShiftLeft', - 0x11: 'ControlLeft', - 0x12: 'AltLeft', - 0x13: 'Pause', - 0x14: 'CapsLock', - 0x15: 'Lang1', - 0x19: 'Lang2', - 0x1b: 'Escape', - 0x1c: 'Convert', - 0x1d: 'NonConvert', - 0x20: 'Space', - 0x21: 'PageUp', - 0x22: 'PageDown', - 0x23: 'End', - 0x24: 'Home', - 0x25: 'ArrowLeft', - 0x26: 'ArrowUp', - 0x27: 'ArrowRight', - 0x28: 'ArrowDown', - 0x29: 'Select', - 0x2c: 'PrintScreen', - 0x2d: 'Insert', - 0x2e: 'Delete', - 0x2f: 'Help', - 0x30: 'Digit0', - 0x31: 'Digit1', - 0x32: 'Digit2', - 0x33: 'Digit3', - 0x34: 'Digit4', - 0x35: 'Digit5', - 0x36: 'Digit6', - 0x37: 'Digit7', - 0x38: 'Digit8', - 0x39: 'Digit9', - 0x5b: 'MetaLeft', - 0x5c: 'MetaRight', - 0x5d: 'ContextMenu', - 0x5f: 'Sleep', - 0x60: 'Numpad0', - 0x61: 'Numpad1', - 0x62: 'Numpad2', - 0x63: 'Numpad3', - 0x64: 'Numpad4', - 0x65: 'Numpad5', - 0x66: 'Numpad6', - 0x67: 'Numpad7', - 0x68: 'Numpad8', - 0x69: 'Numpad9', - 0x6a: 'NumpadMultiply', - 0x6b: 'NumpadAdd', - 0x6c: 'NumpadDecimal', - 0x6d: 'NumpadSubtract', - 0x6e: 'NumpadDecimal', // Duplicate, because buggy on Windows - 0x6f: 'NumpadDivide', - 0x70: 'F1', - 0x71: 'F2', - 0x72: 'F3', - 0x73: 'F4', - 0x74: 'F5', - 0x75: 'F6', - 0x76: 'F7', - 0x77: 'F8', - 0x78: 'F9', - 0x79: 'F10', - 0x7a: 'F11', - 0x7b: 'F12', - 0x7c: 'F13', - 0x7d: 'F14', - 0x7e: 'F15', - 0x7f: 'F16', - 0x80: 'F17', - 0x81: 'F18', - 0x82: 'F19', - 0x83: 'F20', - 0x84: 'F21', - 0x85: 'F22', - 0x86: 'F23', - 0x87: 'F24', - 0x90: 'NumLock', - 0x91: 'ScrollLock', - 0xa6: 'BrowserBack', - 0xa7: 'BrowserForward', - 0xa8: 'BrowserRefresh', - 0xa9: 'BrowserStop', - 0xaa: 'BrowserSearch', - 0xab: 'BrowserFavorites', - 0xac: 'BrowserHome', - 0xad: 'AudioVolumeMute', - 0xae: 'AudioVolumeDown', - 0xaf: 'AudioVolumeUp', - 0xb0: 'MediaTrackNext', - 0xb1: 'MediaTrackPrevious', - 0xb2: 'MediaStop', - 0xb3: 'MediaPlayPause', - 0xb4: 'LaunchMail', - 0xb5: 'MediaSelect', - 0xb6: 'LaunchApp1', - 0xb7: 'LaunchApp2', - 0xe1: 'AltRight', // Only when it is AltGraph -}; diff --git a/base/app/novnc/core/input/xtscancodes.js b/base/app/novnc/core/input/xtscancodes.js deleted file mode 100644 index 8ab9c17..0000000 --- a/base/app/novnc/core/input/xtscancodes.js +++ /dev/null @@ -1,173 +0,0 @@ -/* - * This file is auto-generated from keymaps.csv - * Database checksum sha256(76d68c10e97d37fe2ea459e210125ae41796253fb217e900bf2983ade13a7920) - * To re-generate, run: - * keymap-gen code-map --lang=js keymaps.csv html atset1 -*/ -export default { - "Again": 0xe005, /* html:Again (Again) -> linux:129 (KEY_AGAIN) -> atset1:57349 */ - "AltLeft": 0x38, /* html:AltLeft (AltLeft) -> linux:56 (KEY_LEFTALT) -> atset1:56 */ - "AltRight": 0xe038, /* html:AltRight (AltRight) -> linux:100 (KEY_RIGHTALT) -> atset1:57400 */ - "ArrowDown": 0xe050, /* html:ArrowDown (ArrowDown) -> linux:108 (KEY_DOWN) -> atset1:57424 */ - "ArrowLeft": 0xe04b, /* html:ArrowLeft (ArrowLeft) -> linux:105 (KEY_LEFT) -> atset1:57419 */ - "ArrowRight": 0xe04d, /* html:ArrowRight (ArrowRight) -> linux:106 (KEY_RIGHT) -> atset1:57421 */ - "ArrowUp": 0xe048, /* html:ArrowUp (ArrowUp) -> linux:103 (KEY_UP) -> atset1:57416 */ - "AudioVolumeDown": 0xe02e, /* html:AudioVolumeDown (AudioVolumeDown) -> linux:114 (KEY_VOLUMEDOWN) -> atset1:57390 */ - "AudioVolumeMute": 0xe020, /* html:AudioVolumeMute (AudioVolumeMute) -> linux:113 (KEY_MUTE) -> atset1:57376 */ - "AudioVolumeUp": 0xe030, /* html:AudioVolumeUp (AudioVolumeUp) -> linux:115 (KEY_VOLUMEUP) -> atset1:57392 */ - "Backquote": 0x29, /* html:Backquote (Backquote) -> linux:41 (KEY_GRAVE) -> atset1:41 */ - "Backslash": 0x2b, /* html:Backslash (Backslash) -> linux:43 (KEY_BACKSLASH) -> atset1:43 */ - "Backspace": 0xe, /* html:Backspace (Backspace) -> linux:14 (KEY_BACKSPACE) -> atset1:14 */ - "BracketLeft": 0x1a, /* html:BracketLeft (BracketLeft) -> linux:26 (KEY_LEFTBRACE) -> atset1:26 */ - "BracketRight": 0x1b, /* html:BracketRight (BracketRight) -> linux:27 (KEY_RIGHTBRACE) -> atset1:27 */ - "BrowserBack": 0xe06a, /* html:BrowserBack (BrowserBack) -> linux:158 (KEY_BACK) -> atset1:57450 */ - "BrowserFavorites": 0xe066, /* html:BrowserFavorites (BrowserFavorites) -> linux:156 (KEY_BOOKMARKS) -> atset1:57446 */ - "BrowserForward": 0xe069, /* html:BrowserForward (BrowserForward) -> linux:159 (KEY_FORWARD) -> atset1:57449 */ - "BrowserHome": 0xe032, /* html:BrowserHome (BrowserHome) -> linux:172 (KEY_HOMEPAGE) -> atset1:57394 */ - "BrowserRefresh": 0xe067, /* html:BrowserRefresh (BrowserRefresh) -> linux:173 (KEY_REFRESH) -> atset1:57447 */ - "BrowserSearch": 0xe065, /* html:BrowserSearch (BrowserSearch) -> linux:217 (KEY_SEARCH) -> atset1:57445 */ - "BrowserStop": 0xe068, /* html:BrowserStop (BrowserStop) -> linux:128 (KEY_STOP) -> atset1:57448 */ - "CapsLock": 0x3a, /* html:CapsLock (CapsLock) -> linux:58 (KEY_CAPSLOCK) -> atset1:58 */ - "Comma": 0x33, /* html:Comma (Comma) -> linux:51 (KEY_COMMA) -> atset1:51 */ - "ContextMenu": 0xe05d, /* html:ContextMenu (ContextMenu) -> linux:127 (KEY_COMPOSE) -> atset1:57437 */ - "ControlLeft": 0x1d, /* html:ControlLeft (ControlLeft) -> linux:29 (KEY_LEFTCTRL) -> atset1:29 */ - "ControlRight": 0xe01d, /* html:ControlRight (ControlRight) -> linux:97 (KEY_RIGHTCTRL) -> atset1:57373 */ - "Convert": 0x79, /* html:Convert (Convert) -> linux:92 (KEY_HENKAN) -> atset1:121 */ - "Copy": 0xe078, /* html:Copy (Copy) -> linux:133 (KEY_COPY) -> atset1:57464 */ - "Cut": 0xe03c, /* html:Cut (Cut) -> linux:137 (KEY_CUT) -> atset1:57404 */ - "Delete": 0xe053, /* html:Delete (Delete) -> linux:111 (KEY_DELETE) -> atset1:57427 */ - "Digit0": 0xb, /* html:Digit0 (Digit0) -> linux:11 (KEY_0) -> atset1:11 */ - "Digit1": 0x2, /* html:Digit1 (Digit1) -> linux:2 (KEY_1) -> atset1:2 */ - "Digit2": 0x3, /* html:Digit2 (Digit2) -> linux:3 (KEY_2) -> atset1:3 */ - "Digit3": 0x4, /* html:Digit3 (Digit3) -> linux:4 (KEY_3) -> atset1:4 */ - "Digit4": 0x5, /* html:Digit4 (Digit4) -> linux:5 (KEY_4) -> atset1:5 */ - "Digit5": 0x6, /* html:Digit5 (Digit5) -> linux:6 (KEY_5) -> atset1:6 */ - "Digit6": 0x7, /* html:Digit6 (Digit6) -> linux:7 (KEY_6) -> atset1:7 */ - "Digit7": 0x8, /* html:Digit7 (Digit7) -> linux:8 (KEY_7) -> atset1:8 */ - "Digit8": 0x9, /* html:Digit8 (Digit8) -> linux:9 (KEY_8) -> atset1:9 */ - "Digit9": 0xa, /* html:Digit9 (Digit9) -> linux:10 (KEY_9) -> atset1:10 */ - "Eject": 0xe07d, /* html:Eject (Eject) -> linux:162 (KEY_EJECTCLOSECD) -> atset1:57469 */ - "End": 0xe04f, /* html:End (End) -> linux:107 (KEY_END) -> atset1:57423 */ - "Enter": 0x1c, /* html:Enter (Enter) -> linux:28 (KEY_ENTER) -> atset1:28 */ - "Equal": 0xd, /* html:Equal (Equal) -> linux:13 (KEY_EQUAL) -> atset1:13 */ - "Escape": 0x1, /* html:Escape (Escape) -> linux:1 (KEY_ESC) -> atset1:1 */ - "F1": 0x3b, /* html:F1 (F1) -> linux:59 (KEY_F1) -> atset1:59 */ - "F10": 0x44, /* html:F10 (F10) -> linux:68 (KEY_F10) -> atset1:68 */ - "F11": 0x57, /* html:F11 (F11) -> linux:87 (KEY_F11) -> atset1:87 */ - "F12": 0x58, /* html:F12 (F12) -> linux:88 (KEY_F12) -> atset1:88 */ - "F13": 0x5d, /* html:F13 (F13) -> linux:183 (KEY_F13) -> atset1:93 */ - "F14": 0x5e, /* html:F14 (F14) -> linux:184 (KEY_F14) -> atset1:94 */ - "F15": 0x5f, /* html:F15 (F15) -> linux:185 (KEY_F15) -> atset1:95 */ - "F16": 0x55, /* html:F16 (F16) -> linux:186 (KEY_F16) -> atset1:85 */ - "F17": 0xe003, /* html:F17 (F17) -> linux:187 (KEY_F17) -> atset1:57347 */ - "F18": 0xe077, /* html:F18 (F18) -> linux:188 (KEY_F18) -> atset1:57463 */ - "F19": 0xe004, /* html:F19 (F19) -> linux:189 (KEY_F19) -> atset1:57348 */ - "F2": 0x3c, /* html:F2 (F2) -> linux:60 (KEY_F2) -> atset1:60 */ - "F20": 0x5a, /* html:F20 (F20) -> linux:190 (KEY_F20) -> atset1:90 */ - "F21": 0x74, /* html:F21 (F21) -> linux:191 (KEY_F21) -> atset1:116 */ - "F22": 0xe079, /* html:F22 (F22) -> linux:192 (KEY_F22) -> atset1:57465 */ - "F23": 0x6d, /* html:F23 (F23) -> linux:193 (KEY_F23) -> atset1:109 */ - "F24": 0x6f, /* html:F24 (F24) -> linux:194 (KEY_F24) -> atset1:111 */ - "F3": 0x3d, /* html:F3 (F3) -> linux:61 (KEY_F3) -> atset1:61 */ - "F4": 0x3e, /* html:F4 (F4) -> linux:62 (KEY_F4) -> atset1:62 */ - "F5": 0x3f, /* html:F5 (F5) -> linux:63 (KEY_F5) -> atset1:63 */ - "F6": 0x40, /* html:F6 (F6) -> linux:64 (KEY_F6) -> atset1:64 */ - "F7": 0x41, /* html:F7 (F7) -> linux:65 (KEY_F7) -> atset1:65 */ - "F8": 0x42, /* html:F8 (F8) -> linux:66 (KEY_F8) -> atset1:66 */ - "F9": 0x43, /* html:F9 (F9) -> linux:67 (KEY_F9) -> atset1:67 */ - "Find": 0xe041, /* html:Find (Find) -> linux:136 (KEY_FIND) -> atset1:57409 */ - "Help": 0xe075, /* html:Help (Help) -> linux:138 (KEY_HELP) -> atset1:57461 */ - "Hiragana": 0x77, /* html:Hiragana (Lang4) -> linux:91 (KEY_HIRAGANA) -> atset1:119 */ - "Home": 0xe047, /* html:Home (Home) -> linux:102 (KEY_HOME) -> atset1:57415 */ - "Insert": 0xe052, /* html:Insert (Insert) -> linux:110 (KEY_INSERT) -> atset1:57426 */ - "IntlBackslash": 0x56, /* html:IntlBackslash (IntlBackslash) -> linux:86 (KEY_102ND) -> atset1:86 */ - "IntlRo": 0x73, /* html:IntlRo (IntlRo) -> linux:89 (KEY_RO) -> atset1:115 */ - "IntlYen": 0x7d, /* html:IntlYen (IntlYen) -> linux:124 (KEY_YEN) -> atset1:125 */ - "KanaMode": 0x70, /* html:KanaMode (KanaMode) -> linux:93 (KEY_KATAKANAHIRAGANA) -> atset1:112 */ - "Katakana": 0x78, /* html:Katakana (Lang3) -> linux:90 (KEY_KATAKANA) -> atset1:120 */ - "KeyA": 0x1e, /* html:KeyA (KeyA) -> linux:30 (KEY_A) -> atset1:30 */ - "KeyB": 0x30, /* html:KeyB (KeyB) -> linux:48 (KEY_B) -> atset1:48 */ - "KeyC": 0x2e, /* html:KeyC (KeyC) -> linux:46 (KEY_C) -> atset1:46 */ - "KeyD": 0x20, /* html:KeyD (KeyD) -> linux:32 (KEY_D) -> atset1:32 */ - "KeyE": 0x12, /* html:KeyE (KeyE) -> linux:18 (KEY_E) -> atset1:18 */ - "KeyF": 0x21, /* html:KeyF (KeyF) -> linux:33 (KEY_F) -> atset1:33 */ - "KeyG": 0x22, /* html:KeyG (KeyG) -> linux:34 (KEY_G) -> atset1:34 */ - "KeyH": 0x23, /* html:KeyH (KeyH) -> linux:35 (KEY_H) -> atset1:35 */ - "KeyI": 0x17, /* html:KeyI (KeyI) -> linux:23 (KEY_I) -> atset1:23 */ - "KeyJ": 0x24, /* html:KeyJ (KeyJ) -> linux:36 (KEY_J) -> atset1:36 */ - "KeyK": 0x25, /* html:KeyK (KeyK) -> linux:37 (KEY_K) -> atset1:37 */ - "KeyL": 0x26, /* html:KeyL (KeyL) -> linux:38 (KEY_L) -> atset1:38 */ - "KeyM": 0x32, /* html:KeyM (KeyM) -> linux:50 (KEY_M) -> atset1:50 */ - "KeyN": 0x31, /* html:KeyN (KeyN) -> linux:49 (KEY_N) -> atset1:49 */ - "KeyO": 0x18, /* html:KeyO (KeyO) -> linux:24 (KEY_O) -> atset1:24 */ - "KeyP": 0x19, /* html:KeyP (KeyP) -> linux:25 (KEY_P) -> atset1:25 */ - "KeyQ": 0x10, /* html:KeyQ (KeyQ) -> linux:16 (KEY_Q) -> atset1:16 */ - "KeyR": 0x13, /* html:KeyR (KeyR) -> linux:19 (KEY_R) -> atset1:19 */ - "KeyS": 0x1f, /* html:KeyS (KeyS) -> linux:31 (KEY_S) -> atset1:31 */ - "KeyT": 0x14, /* html:KeyT (KeyT) -> linux:20 (KEY_T) -> atset1:20 */ - "KeyU": 0x16, /* html:KeyU (KeyU) -> linux:22 (KEY_U) -> atset1:22 */ - "KeyV": 0x2f, /* html:KeyV (KeyV) -> linux:47 (KEY_V) -> atset1:47 */ - "KeyW": 0x11, /* html:KeyW (KeyW) -> linux:17 (KEY_W) -> atset1:17 */ - "KeyX": 0x2d, /* html:KeyX (KeyX) -> linux:45 (KEY_X) -> atset1:45 */ - "KeyY": 0x15, /* html:KeyY (KeyY) -> linux:21 (KEY_Y) -> atset1:21 */ - "KeyZ": 0x2c, /* html:KeyZ (KeyZ) -> linux:44 (KEY_Z) -> atset1:44 */ - "Lang1": 0x72, /* html:Lang1 (Lang1) -> linux:122 (KEY_HANGEUL) -> atset1:114 */ - "Lang2": 0x71, /* html:Lang2 (Lang2) -> linux:123 (KEY_HANJA) -> atset1:113 */ - "Lang3": 0x78, /* html:Lang3 (Lang3) -> linux:90 (KEY_KATAKANA) -> atset1:120 */ - "Lang4": 0x77, /* html:Lang4 (Lang4) -> linux:91 (KEY_HIRAGANA) -> atset1:119 */ - "Lang5": 0x76, /* html:Lang5 (Lang5) -> linux:85 (KEY_ZENKAKUHANKAKU) -> atset1:118 */ - "LaunchApp1": 0xe06b, /* html:LaunchApp1 (LaunchApp1) -> linux:157 (KEY_COMPUTER) -> atset1:57451 */ - "LaunchApp2": 0xe021, /* html:LaunchApp2 (LaunchApp2) -> linux:140 (KEY_CALC) -> atset1:57377 */ - "LaunchMail": 0xe06c, /* html:LaunchMail (LaunchMail) -> linux:155 (KEY_MAIL) -> atset1:57452 */ - "MediaPlayPause": 0xe022, /* html:MediaPlayPause (MediaPlayPause) -> linux:164 (KEY_PLAYPAUSE) -> atset1:57378 */ - "MediaSelect": 0xe06d, /* html:MediaSelect (MediaSelect) -> linux:226 (KEY_MEDIA) -> atset1:57453 */ - "MediaStop": 0xe024, /* html:MediaStop (MediaStop) -> linux:166 (KEY_STOPCD) -> atset1:57380 */ - "MediaTrackNext": 0xe019, /* html:MediaTrackNext (MediaTrackNext) -> linux:163 (KEY_NEXTSONG) -> atset1:57369 */ - "MediaTrackPrevious": 0xe010, /* html:MediaTrackPrevious (MediaTrackPrevious) -> linux:165 (KEY_PREVIOUSSONG) -> atset1:57360 */ - "MetaLeft": 0xe05b, /* html:MetaLeft (MetaLeft) -> linux:125 (KEY_LEFTMETA) -> atset1:57435 */ - "MetaRight": 0xe05c, /* html:MetaRight (MetaRight) -> linux:126 (KEY_RIGHTMETA) -> atset1:57436 */ - "Minus": 0xc, /* html:Minus (Minus) -> linux:12 (KEY_MINUS) -> atset1:12 */ - "NonConvert": 0x7b, /* html:NonConvert (NonConvert) -> linux:94 (KEY_MUHENKAN) -> atset1:123 */ - "NumLock": 0x45, /* html:NumLock (NumLock) -> linux:69 (KEY_NUMLOCK) -> atset1:69 */ - "Numpad0": 0x52, /* html:Numpad0 (Numpad0) -> linux:82 (KEY_KP0) -> atset1:82 */ - "Numpad1": 0x4f, /* html:Numpad1 (Numpad1) -> linux:79 (KEY_KP1) -> atset1:79 */ - "Numpad2": 0x50, /* html:Numpad2 (Numpad2) -> linux:80 (KEY_KP2) -> atset1:80 */ - "Numpad3": 0x51, /* html:Numpad3 (Numpad3) -> linux:81 (KEY_KP3) -> atset1:81 */ - "Numpad4": 0x4b, /* html:Numpad4 (Numpad4) -> linux:75 (KEY_KP4) -> atset1:75 */ - "Numpad5": 0x4c, /* html:Numpad5 (Numpad5) -> linux:76 (KEY_KP5) -> atset1:76 */ - "Numpad6": 0x4d, /* html:Numpad6 (Numpad6) -> linux:77 (KEY_KP6) -> atset1:77 */ - "Numpad7": 0x47, /* html:Numpad7 (Numpad7) -> linux:71 (KEY_KP7) -> atset1:71 */ - "Numpad8": 0x48, /* html:Numpad8 (Numpad8) -> linux:72 (KEY_KP8) -> atset1:72 */ - "Numpad9": 0x49, /* html:Numpad9 (Numpad9) -> linux:73 (KEY_KP9) -> atset1:73 */ - "NumpadAdd": 0x4e, /* html:NumpadAdd (NumpadAdd) -> linux:78 (KEY_KPPLUS) -> atset1:78 */ - "NumpadComma": 0x7e, /* html:NumpadComma (NumpadComma) -> linux:121 (KEY_KPCOMMA) -> atset1:126 */ - "NumpadDecimal": 0x53, /* html:NumpadDecimal (NumpadDecimal) -> linux:83 (KEY_KPDOT) -> atset1:83 */ - "NumpadDivide": 0xe035, /* html:NumpadDivide (NumpadDivide) -> linux:98 (KEY_KPSLASH) -> atset1:57397 */ - "NumpadEnter": 0xe01c, /* html:NumpadEnter (NumpadEnter) -> linux:96 (KEY_KPENTER) -> atset1:57372 */ - "NumpadEqual": 0x59, /* html:NumpadEqual (NumpadEqual) -> linux:117 (KEY_KPEQUAL) -> atset1:89 */ - "NumpadMultiply": 0x37, /* html:NumpadMultiply (NumpadMultiply) -> linux:55 (KEY_KPASTERISK) -> atset1:55 */ - "NumpadParenLeft": 0xe076, /* html:NumpadParenLeft (NumpadParenLeft) -> linux:179 (KEY_KPLEFTPAREN) -> atset1:57462 */ - "NumpadParenRight": 0xe07b, /* html:NumpadParenRight (NumpadParenRight) -> linux:180 (KEY_KPRIGHTPAREN) -> atset1:57467 */ - "NumpadSubtract": 0x4a, /* html:NumpadSubtract (NumpadSubtract) -> linux:74 (KEY_KPMINUS) -> atset1:74 */ - "Open": 0x64, /* html:Open (Open) -> linux:134 (KEY_OPEN) -> atset1:100 */ - "PageDown": 0xe051, /* html:PageDown (PageDown) -> linux:109 (KEY_PAGEDOWN) -> atset1:57425 */ - "PageUp": 0xe049, /* html:PageUp (PageUp) -> linux:104 (KEY_PAGEUP) -> atset1:57417 */ - "Paste": 0x65, /* html:Paste (Paste) -> linux:135 (KEY_PASTE) -> atset1:101 */ - "Pause": 0xe046, /* html:Pause (Pause) -> linux:119 (KEY_PAUSE) -> atset1:57414 */ - "Period": 0x34, /* html:Period (Period) -> linux:52 (KEY_DOT) -> atset1:52 */ - "Power": 0xe05e, /* html:Power (Power) -> linux:116 (KEY_POWER) -> atset1:57438 */ - "PrintScreen": 0x54, /* html:PrintScreen (PrintScreen) -> linux:99 (KEY_SYSRQ) -> atset1:84 */ - "Props": 0xe006, /* html:Props (Props) -> linux:130 (KEY_PROPS) -> atset1:57350 */ - "Quote": 0x28, /* html:Quote (Quote) -> linux:40 (KEY_APOSTROPHE) -> atset1:40 */ - "ScrollLock": 0x46, /* html:ScrollLock (ScrollLock) -> linux:70 (KEY_SCROLLLOCK) -> atset1:70 */ - "Semicolon": 0x27, /* html:Semicolon (Semicolon) -> linux:39 (KEY_SEMICOLON) -> atset1:39 */ - "ShiftLeft": 0x2a, /* html:ShiftLeft (ShiftLeft) -> linux:42 (KEY_LEFTSHIFT) -> atset1:42 */ - "ShiftRight": 0x36, /* html:ShiftRight (ShiftRight) -> linux:54 (KEY_RIGHTSHIFT) -> atset1:54 */ - "Slash": 0x35, /* html:Slash (Slash) -> linux:53 (KEY_SLASH) -> atset1:53 */ - "Sleep": 0xe05f, /* html:Sleep (Sleep) -> linux:142 (KEY_SLEEP) -> atset1:57439 */ - "Space": 0x39, /* html:Space (Space) -> linux:57 (KEY_SPACE) -> atset1:57 */ - "Suspend": 0xe025, /* html:Suspend (Suspend) -> linux:205 (KEY_SUSPEND) -> atset1:57381 */ - "Tab": 0xf, /* html:Tab (Tab) -> linux:15 (KEY_TAB) -> atset1:15 */ - "Undo": 0xe007, /* html:Undo (Undo) -> linux:131 (KEY_UNDO) -> atset1:57351 */ - "WakeUp": 0xe063, /* html:WakeUp (WakeUp) -> linux:143 (KEY_WAKEUP) -> atset1:57443 */ -}; diff --git a/base/app/novnc/core/ra2.js b/base/app/novnc/core/ra2.js deleted file mode 100644 index d330b84..0000000 --- a/base/app/novnc/core/ra2.js +++ /dev/null @@ -1,312 +0,0 @@ -import { encodeUTF8 } from './util/strings.js'; -import EventTargetMixin from './util/eventtarget.js'; -import legacyCrypto from './crypto/crypto.js'; - -class RA2Cipher { - constructor() { - this._cipher = null; - this._counter = new Uint8Array(16); - } - - async setKey(key) { - this._cipher = await legacyCrypto.importKey( - "raw", key, { name: "AES-EAX" }, false, ["encrypt, decrypt"]); - } - - async makeMessage(message) { - const ad = new Uint8Array([(message.length & 0xff00) >>> 8, message.length & 0xff]); - const encrypted = await legacyCrypto.encrypt({ - name: "AES-EAX", - iv: this._counter, - additionalData: ad, - }, this._cipher, message); - for (let i = 0; i < 16 && this._counter[i]++ === 255; i++); - const res = new Uint8Array(message.length + 2 + 16); - res.set(ad); - res.set(encrypted, 2); - return res; - } - - async receiveMessage(length, encrypted) { - const ad = new Uint8Array([(length & 0xff00) >>> 8, length & 0xff]); - const res = await legacyCrypto.decrypt({ - name: "AES-EAX", - iv: this._counter, - additionalData: ad, - }, this._cipher, encrypted); - for (let i = 0; i < 16 && this._counter[i]++ === 255; i++); - return res; - } -} - -export default class RSAAESAuthenticationState extends EventTargetMixin { - constructor(sock, getCredentials) { - super(); - this._hasStarted = false; - this._checkSock = null; - this._checkCredentials = null; - this._approveServerResolve = null; - this._sockReject = null; - this._credentialsReject = null; - this._approveServerReject = null; - this._sock = sock; - this._getCredentials = getCredentials; - } - - _waitSockAsync(len) { - return new Promise((resolve, reject) => { - const hasData = () => !this._sock.rQwait('RA2', len); - if (hasData()) { - resolve(); - } else { - this._checkSock = () => { - if (hasData()) { - resolve(); - this._checkSock = null; - this._sockReject = null; - } - }; - this._sockReject = reject; - } - }); - } - - _waitApproveKeyAsync() { - return new Promise((resolve, reject) => { - this._approveServerResolve = resolve; - this._approveServerReject = reject; - }); - } - - _waitCredentialsAsync(subtype) { - const hasCredentials = () => { - if (subtype === 1 && this._getCredentials().username !== undefined && - this._getCredentials().password !== undefined) { - return true; - } else if (subtype === 2 && this._getCredentials().password !== undefined) { - return true; - } - return false; - }; - return new Promise((resolve, reject) => { - if (hasCredentials()) { - resolve(); - } else { - this._checkCredentials = () => { - if (hasCredentials()) { - resolve(); - this._checkCredentials = null; - this._credentialsReject = null; - } - }; - this._credentialsReject = reject; - } - }); - } - - checkInternalEvents() { - if (this._checkSock !== null) { - this._checkSock(); - } - if (this._checkCredentials !== null) { - this._checkCredentials(); - } - } - - approveServer() { - if (this._approveServerResolve !== null) { - this._approveServerResolve(); - this._approveServerResolve = null; - } - } - - disconnect() { - if (this._sockReject !== null) { - this._sockReject(new Error("disconnect normally")); - this._sockReject = null; - } - if (this._credentialsReject !== null) { - this._credentialsReject(new Error("disconnect normally")); - this._credentialsReject = null; - } - if (this._approveServerReject !== null) { - this._approveServerReject(new Error("disconnect normally")); - this._approveServerReject = null; - } - } - - async negotiateRA2neAuthAsync() { - this._hasStarted = true; - // 1: Receive server public key - await this._waitSockAsync(4); - const serverKeyLengthBuffer = this._sock.rQpeekBytes(4); - const serverKeyLength = this._sock.rQshift32(); - if (serverKeyLength < 1024) { - throw new Error("RA2: server public key is too short: " + serverKeyLength); - } else if (serverKeyLength > 8192) { - throw new Error("RA2: server public key is too long: " + serverKeyLength); - } - const serverKeyBytes = Math.ceil(serverKeyLength / 8); - await this._waitSockAsync(serverKeyBytes * 2); - const serverN = this._sock.rQshiftBytes(serverKeyBytes); - const serverE = this._sock.rQshiftBytes(serverKeyBytes); - const serverRSACipher = await legacyCrypto.importKey( - "raw", { n: serverN, e: serverE }, { name: "RSA-PKCS1-v1_5" }, false, ["encrypt"]); - const serverPublickey = new Uint8Array(4 + serverKeyBytes * 2); - serverPublickey.set(serverKeyLengthBuffer); - serverPublickey.set(serverN, 4); - serverPublickey.set(serverE, 4 + serverKeyBytes); - - // verify server public key - let approveKey = this._waitApproveKeyAsync(); - this.dispatchEvent(new CustomEvent("serververification", { - detail: { type: "RSA", publickey: serverPublickey } - })); - await approveKey; - - // 2: Send client public key - const clientKeyLength = 2048; - const clientKeyBytes = Math.ceil(clientKeyLength / 8); - const clientRSACipher = (await legacyCrypto.generateKey({ - name: "RSA-PKCS1-v1_5", - modulusLength: clientKeyLength, - publicExponent: new Uint8Array([1, 0, 1]), - }, true, ["encrypt"])).privateKey; - const clientExportedRSAKey = await legacyCrypto.exportKey("raw", clientRSACipher); - const clientN = clientExportedRSAKey.n; - const clientE = clientExportedRSAKey.e; - const clientPublicKey = new Uint8Array(4 + clientKeyBytes * 2); - clientPublicKey[0] = (clientKeyLength & 0xff000000) >>> 24; - clientPublicKey[1] = (clientKeyLength & 0xff0000) >>> 16; - clientPublicKey[2] = (clientKeyLength & 0xff00) >>> 8; - clientPublicKey[3] = clientKeyLength & 0xff; - clientPublicKey.set(clientN, 4); - clientPublicKey.set(clientE, 4 + clientKeyBytes); - this._sock.sQpushBytes(clientPublicKey); - this._sock.flush(); - - // 3: Send client random - const clientRandom = new Uint8Array(16); - window.crypto.getRandomValues(clientRandom); - const clientEncryptedRandom = await legacyCrypto.encrypt( - { name: "RSA-PKCS1-v1_5" }, serverRSACipher, clientRandom); - const clientRandomMessage = new Uint8Array(2 + serverKeyBytes); - clientRandomMessage[0] = (serverKeyBytes & 0xff00) >>> 8; - clientRandomMessage[1] = serverKeyBytes & 0xff; - clientRandomMessage.set(clientEncryptedRandom, 2); - this._sock.sQpushBytes(clientRandomMessage); - this._sock.flush(); - - // 4: Receive server random - await this._waitSockAsync(2); - if (this._sock.rQshift16() !== clientKeyBytes) { - throw new Error("RA2: wrong encrypted message length"); - } - const serverEncryptedRandom = this._sock.rQshiftBytes(clientKeyBytes); - const serverRandom = await legacyCrypto.decrypt( - { name: "RSA-PKCS1-v1_5" }, clientRSACipher, serverEncryptedRandom); - if (serverRandom === null || serverRandom.length !== 16) { - throw new Error("RA2: corrupted server encrypted random"); - } - - // 5: Compute session keys and set ciphers - let clientSessionKey = new Uint8Array(32); - let serverSessionKey = new Uint8Array(32); - clientSessionKey.set(serverRandom); - clientSessionKey.set(clientRandom, 16); - serverSessionKey.set(clientRandom); - serverSessionKey.set(serverRandom, 16); - clientSessionKey = await window.crypto.subtle.digest("SHA-1", clientSessionKey); - clientSessionKey = new Uint8Array(clientSessionKey).slice(0, 16); - serverSessionKey = await window.crypto.subtle.digest("SHA-1", serverSessionKey); - serverSessionKey = new Uint8Array(serverSessionKey).slice(0, 16); - const clientCipher = new RA2Cipher(); - await clientCipher.setKey(clientSessionKey); - const serverCipher = new RA2Cipher(); - await serverCipher.setKey(serverSessionKey); - - // 6: Compute and exchange hashes - let serverHash = new Uint8Array(8 + serverKeyBytes * 2 + clientKeyBytes * 2); - let clientHash = new Uint8Array(8 + serverKeyBytes * 2 + clientKeyBytes * 2); - serverHash.set(serverPublickey); - serverHash.set(clientPublicKey, 4 + serverKeyBytes * 2); - clientHash.set(clientPublicKey); - clientHash.set(serverPublickey, 4 + clientKeyBytes * 2); - serverHash = await window.crypto.subtle.digest("SHA-1", serverHash); - clientHash = await window.crypto.subtle.digest("SHA-1", clientHash); - serverHash = new Uint8Array(serverHash); - clientHash = new Uint8Array(clientHash); - this._sock.sQpushBytes(await clientCipher.makeMessage(clientHash)); - this._sock.flush(); - await this._waitSockAsync(2 + 20 + 16); - if (this._sock.rQshift16() !== 20) { - throw new Error("RA2: wrong server hash"); - } - const serverHashReceived = await serverCipher.receiveMessage( - 20, this._sock.rQshiftBytes(20 + 16)); - if (serverHashReceived === null) { - throw new Error("RA2: failed to authenticate the message"); - } - for (let i = 0; i < 20; i++) { - if (serverHashReceived[i] !== serverHash[i]) { - throw new Error("RA2: wrong server hash"); - } - } - - // 7: Receive subtype - await this._waitSockAsync(2 + 1 + 16); - if (this._sock.rQshift16() !== 1) { - throw new Error("RA2: wrong subtype"); - } - let subtype = (await serverCipher.receiveMessage( - 1, this._sock.rQshiftBytes(1 + 16))); - if (subtype === null) { - throw new Error("RA2: failed to authenticate the message"); - } - subtype = subtype[0]; - let waitCredentials = this._waitCredentialsAsync(subtype); - if (subtype === 1) { - if (this._getCredentials().username === undefined || - this._getCredentials().password === undefined) { - this.dispatchEvent(new CustomEvent( - "credentialsrequired", - { detail: { types: ["username", "password"] } })); - } - } else if (subtype === 2) { - if (this._getCredentials().password === undefined) { - this.dispatchEvent(new CustomEvent( - "credentialsrequired", - { detail: { types: ["password"] } })); - } - } else { - throw new Error("RA2: wrong subtype"); - } - await waitCredentials; - let username; - if (subtype === 1) { - username = encodeUTF8(this._getCredentials().username).slice(0, 255); - } else { - username = ""; - } - const password = encodeUTF8(this._getCredentials().password).slice(0, 255); - const credentials = new Uint8Array(username.length + password.length + 2); - credentials[0] = username.length; - credentials[username.length + 1] = password.length; - for (let i = 0; i < username.length; i++) { - credentials[i + 1] = username.charCodeAt(i); - } - for (let i = 0; i < password.length; i++) { - credentials[username.length + 2 + i] = password.charCodeAt(i); - } - this._sock.sQpushBytes(await clientCipher.makeMessage(credentials)); - this._sock.flush(); - } - - get hasStarted() { - return this._hasStarted; - } - - set hasStarted(s) { - this._hasStarted = s; - } -} diff --git a/base/app/novnc/core/rfb.js b/base/app/novnc/core/rfb.js deleted file mode 100644 index f2deb0e..0000000 --- a/base/app/novnc/core/rfb.js +++ /dev/null @@ -1,3249 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2020 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - * - */ - -import { toUnsigned32bit, toSigned32bit } from './util/int.js'; -import * as Log from './util/logging.js'; -import { encodeUTF8, decodeUTF8 } from './util/strings.js'; -import { dragThreshold } from './util/browser.js'; -import { clientToElement } from './util/element.js'; -import { setCapture } from './util/events.js'; -import EventTargetMixin from './util/eventtarget.js'; -import Display from "./display.js"; -import Inflator from "./inflator.js"; -import Deflator from "./deflator.js"; -import Keyboard from "./input/keyboard.js"; -import GestureHandler from "./input/gesturehandler.js"; -import Cursor from "./util/cursor.js"; -import Websock from "./websock.js"; -import KeyTable from "./input/keysym.js"; -import XtScancode from "./input/xtscancodes.js"; -import { encodings } from "./encodings.js"; -import RSAAESAuthenticationState from "./ra2.js"; -import legacyCrypto from "./crypto/crypto.js"; - -import RawDecoder from "./decoders/raw.js"; -import CopyRectDecoder from "./decoders/copyrect.js"; -import RREDecoder from "./decoders/rre.js"; -import HextileDecoder from "./decoders/hextile.js"; -import TightDecoder from "./decoders/tight.js"; -import TightPNGDecoder from "./decoders/tightpng.js"; -import ZRLEDecoder from "./decoders/zrle.js"; -import JPEGDecoder from "./decoders/jpeg.js"; - -// How many seconds to wait for a disconnect to finish -const DISCONNECT_TIMEOUT = 3; -const DEFAULT_BACKGROUND = 'rgb(40, 40, 40)'; - -// Minimum wait (ms) between two mouse moves -const MOUSE_MOVE_DELAY = 17; - -// Wheel thresholds -const WHEEL_STEP = 50; // Pixels needed for one step -const WHEEL_LINE_HEIGHT = 19; // Assumed pixels for one line step - -// Gesture thresholds -const GESTURE_ZOOMSENS = 75; -const GESTURE_SCRLSENS = 50; -const DOUBLE_TAP_TIMEOUT = 1000; -const DOUBLE_TAP_THRESHOLD = 50; - -// Security types -const securityTypeNone = 1; -const securityTypeVNCAuth = 2; -const securityTypeRA2ne = 6; -const securityTypeTight = 16; -const securityTypeVeNCrypt = 19; -const securityTypeXVP = 22; -const securityTypeARD = 30; -const securityTypeMSLogonII = 113; - -// Special Tight security types -const securityTypeUnixLogon = 129; - -// VeNCrypt security types -const securityTypePlain = 256; - -// Extended clipboard pseudo-encoding formats -const extendedClipboardFormatText = 1; -/*eslint-disable no-unused-vars */ -const extendedClipboardFormatRtf = 1 << 1; -const extendedClipboardFormatHtml = 1 << 2; -const extendedClipboardFormatDib = 1 << 3; -const extendedClipboardFormatFiles = 1 << 4; -/*eslint-enable */ - -// Extended clipboard pseudo-encoding actions -const extendedClipboardActionCaps = 1 << 24; -const extendedClipboardActionRequest = 1 << 25; -const extendedClipboardActionPeek = 1 << 26; -const extendedClipboardActionNotify = 1 << 27; -const extendedClipboardActionProvide = 1 << 28; - -export default class RFB extends EventTargetMixin { - constructor(target, urlOrChannel, options) { - if (!target) { - throw new Error("Must specify target"); - } - if (!urlOrChannel) { - throw new Error("Must specify URL, WebSocket or RTCDataChannel"); - } - - // We rely on modern APIs which might not be available in an - // insecure context - if (!window.isSecureContext) { - Log.Error("noVNC requires a secure context (TLS). Expect crashes!"); - } - - super(); - - this._target = target; - - if (typeof urlOrChannel === "string") { - this._url = urlOrChannel; - } else { - this._url = null; - this._rawChannel = urlOrChannel; - } - - // Connection details - options = options || {}; - this._rfbCredentials = options.credentials || {}; - this._shared = 'shared' in options ? !!options.shared : true; - this._repeaterID = options.repeaterID || ''; - this._wsProtocols = options.wsProtocols || []; - - // Internal state - this._rfbConnectionState = ''; - this._rfbInitState = ''; - this._rfbAuthScheme = -1; - this._rfbCleanDisconnect = true; - this._rfbRSAAESAuthenticationState = null; - - // Server capabilities - this._rfbVersion = 0; - this._rfbMaxVersion = 3.8; - this._rfbTightVNC = false; - this._rfbVeNCryptState = 0; - this._rfbXvpVer = 0; - - this._fbWidth = 0; - this._fbHeight = 0; - - this._fbName = ""; - - this._capabilities = { power: false }; - - this._supportsFence = false; - - this._supportsContinuousUpdates = false; - this._enabledContinuousUpdates = false; - - this._supportsSetDesktopSize = false; - this._screenID = 0; - this._screenFlags = 0; - - this._qemuExtKeyEventSupported = false; - - this._clipboardText = null; - this._clipboardServerCapabilitiesActions = {}; - this._clipboardServerCapabilitiesFormats = {}; - - // Internal objects - this._sock = null; // Websock object - this._display = null; // Display object - this._flushing = false; // Display flushing state - this._keyboard = null; // Keyboard input handler object - this._gestures = null; // Gesture input handler object - this._resizeObserver = null; // Resize observer object - - // Timers - this._disconnTimer = null; // disconnection timer - this._resizeTimeout = null; // resize rate limiting - this._mouseMoveTimer = null; - - // Decoder states - this._decoders = {}; - - this._FBU = { - rects: 0, - x: 0, - y: 0, - width: 0, - height: 0, - encoding: null, - }; - - // Mouse state - this._mousePos = {}; - this._mouseButtonMask = 0; - this._mouseLastMoveTime = 0; - this._viewportDragging = false; - this._viewportDragPos = {}; - this._viewportHasMoved = false; - this._accumulatedWheelDeltaX = 0; - this._accumulatedWheelDeltaY = 0; - - // Gesture state - this._gestureLastTapTime = null; - this._gestureFirstDoubleTapEv = null; - this._gestureLastMagnitudeX = 0; - this._gestureLastMagnitudeY = 0; - - // Bound event handlers - this._eventHandlers = { - focusCanvas: this._focusCanvas.bind(this), - handleResize: this._handleResize.bind(this), - handleMouse: this._handleMouse.bind(this), - handleWheel: this._handleWheel.bind(this), - handleGesture: this._handleGesture.bind(this), - handleRSAAESCredentialsRequired: this._handleRSAAESCredentialsRequired.bind(this), - handleRSAAESServerVerification: this._handleRSAAESServerVerification.bind(this), - }; - - // main setup - Log.Debug(">> RFB.constructor"); - - // Create DOM elements - this._screen = document.createElement('div'); - this._screen.style.display = 'flex'; - this._screen.style.width = '100%'; - this._screen.style.height = '100%'; - this._screen.style.overflow = 'auto'; - this._screen.style.background = DEFAULT_BACKGROUND; - this._canvas = document.createElement('canvas'); - this._canvas.style.margin = 'auto'; - // Some browsers add an outline on focus - this._canvas.style.outline = 'none'; - this._canvas.width = 0; - this._canvas.height = 0; - this._canvas.tabIndex = -1; - this._screen.appendChild(this._canvas); - - // Cursor - this._cursor = new Cursor(); - - // XXX: TightVNC 2.8.11 sends no cursor at all until Windows changes - // it. Result: no cursor at all until a window border or an edit field - // is hit blindly. But there are also VNC servers that draw the cursor - // in the framebuffer and don't send the empty local cursor. There is - // no way to satisfy both sides. - // - // The spec is unclear on this "initial cursor" issue. Many other - // viewers (TigerVNC, RealVNC, Remmina) display an arrow as the - // initial cursor instead. - this._cursorImage = RFB.cursors.none; - - // populate decoder array with objects - this._decoders[encodings.encodingRaw] = new RawDecoder(); - this._decoders[encodings.encodingCopyRect] = new CopyRectDecoder(); - this._decoders[encodings.encodingRRE] = new RREDecoder(); - this._decoders[encodings.encodingHextile] = new HextileDecoder(); - this._decoders[encodings.encodingTight] = new TightDecoder(); - this._decoders[encodings.encodingTightPNG] = new TightPNGDecoder(); - this._decoders[encodings.encodingZRLE] = new ZRLEDecoder(); - this._decoders[encodings.encodingJPEG] = new JPEGDecoder(); - - // NB: nothing that needs explicit teardown should be done - // before this point, since this can throw an exception - try { - this._display = new Display(this._canvas); - } catch (exc) { - Log.Error("Display exception: " + exc); - throw exc; - } - - this._keyboard = new Keyboard(this._canvas); - this._keyboard.onkeyevent = this._handleKeyEvent.bind(this); - this._remoteCapsLock = null; // Null indicates unknown or irrelevant - this._remoteNumLock = null; - - this._gestures = new GestureHandler(); - - this._sock = new Websock(); - this._sock.on('open', this._socketOpen.bind(this)); - this._sock.on('close', this._socketClose.bind(this)); - this._sock.on('message', this._handleMessage.bind(this)); - this._sock.on('error', this._socketError.bind(this)); - - this._expectedClientWidth = null; - this._expectedClientHeight = null; - this._resizeObserver = new ResizeObserver(this._eventHandlers.handleResize); - - // All prepared, kick off the connection - this._updateConnectionState('connecting'); - - Log.Debug("<< RFB.constructor"); - - // ===== PROPERTIES ===== - - this.dragViewport = false; - this.focusOnClick = true; - - this._viewOnly = false; - this._clipViewport = false; - this._clippingViewport = false; - this._scaleViewport = false; - this._resizeSession = false; - - this._showDotCursor = false; - if (options.showDotCursor !== undefined) { - Log.Warn("Specifying showDotCursor as a RFB constructor argument is deprecated"); - this._showDotCursor = options.showDotCursor; - } - - this._qualityLevel = 6; - this._compressionLevel = 2; - } - - // ===== PROPERTIES ===== - - get viewOnly() { return this._viewOnly; } - set viewOnly(viewOnly) { - this._viewOnly = viewOnly; - - if (this._rfbConnectionState === "connecting" || - this._rfbConnectionState === "connected") { - if (viewOnly) { - this._keyboard.ungrab(); - } else { - this._keyboard.grab(); - } - } - } - - get capabilities() { return this._capabilities; } - - get clippingViewport() { return this._clippingViewport; } - _setClippingViewport(on) { - if (on === this._clippingViewport) { - return; - } - this._clippingViewport = on; - this.dispatchEvent(new CustomEvent("clippingviewport", - { detail: this._clippingViewport })); - } - - get touchButton() { return 0; } - set touchButton(button) { Log.Warn("Using old API!"); } - - get clipViewport() { return this._clipViewport; } - set clipViewport(viewport) { - this._clipViewport = viewport; - this._updateClip(); - } - - get scaleViewport() { return this._scaleViewport; } - set scaleViewport(scale) { - this._scaleViewport = scale; - // Scaling trumps clipping, so we may need to adjust - // clipping when enabling or disabling scaling - if (scale && this._clipViewport) { - this._updateClip(); - } - this._updateScale(); - if (!scale && this._clipViewport) { - this._updateClip(); - } - } - - get resizeSession() { return this._resizeSession; } - set resizeSession(resize) { - this._resizeSession = resize; - if (resize) { - this._requestRemoteResize(); - } - } - - get showDotCursor() { return this._showDotCursor; } - set showDotCursor(show) { - this._showDotCursor = show; - this._refreshCursor(); - } - - get background() { return this._screen.style.background; } - set background(cssValue) { this._screen.style.background = cssValue; } - - get qualityLevel() { - return this._qualityLevel; - } - set qualityLevel(qualityLevel) { - if (!Number.isInteger(qualityLevel) || qualityLevel < 0 || qualityLevel > 9) { - Log.Error("qualityLevel must be an integer between 0 and 9"); - return; - } - - if (this._qualityLevel === qualityLevel) { - return; - } - - this._qualityLevel = qualityLevel; - - if (this._rfbConnectionState === 'connected') { - this._sendEncodings(); - } - } - - get compressionLevel() { - return this._compressionLevel; - } - set compressionLevel(compressionLevel) { - if (!Number.isInteger(compressionLevel) || compressionLevel < 0 || compressionLevel > 9) { - Log.Error("compressionLevel must be an integer between 0 and 9"); - return; - } - - if (this._compressionLevel === compressionLevel) { - return; - } - - this._compressionLevel = compressionLevel; - - if (this._rfbConnectionState === 'connected') { - this._sendEncodings(); - } - } - - // ===== PUBLIC METHODS ===== - - disconnect() { - this._updateConnectionState('disconnecting'); - this._sock.off('error'); - this._sock.off('message'); - this._sock.off('open'); - if (this._rfbRSAAESAuthenticationState !== null) { - this._rfbRSAAESAuthenticationState.disconnect(); - } - } - - approveServer() { - if (this._rfbRSAAESAuthenticationState !== null) { - this._rfbRSAAESAuthenticationState.approveServer(); - } - } - - sendCredentials(creds) { - this._rfbCredentials = creds; - this._resumeAuthentication(); - } - - sendCtrlAltDel() { - if (this._rfbConnectionState !== 'connected' || this._viewOnly) { return; } - Log.Info("Sending Ctrl-Alt-Del"); - - this.sendKey(KeyTable.XK_Control_L, "ControlLeft", true); - this.sendKey(KeyTable.XK_Alt_L, "AltLeft", true); - this.sendKey(KeyTable.XK_Delete, "Delete", true); - this.sendKey(KeyTable.XK_Delete, "Delete", false); - this.sendKey(KeyTable.XK_Alt_L, "AltLeft", false); - this.sendKey(KeyTable.XK_Control_L, "ControlLeft", false); - } - - machineShutdown() { - this._xvpOp(1, 2); - } - - machineReboot() { - this._xvpOp(1, 3); - } - - machineReset() { - this._xvpOp(1, 4); - } - - // Send a key press. If 'down' is not specified then send a down key - // followed by an up key. - sendKey(keysym, code, down) { - if (this._rfbConnectionState !== 'connected' || this._viewOnly) { return; } - - if (down === undefined) { - this.sendKey(keysym, code, true); - this.sendKey(keysym, code, false); - return; - } - - const scancode = XtScancode[code]; - - if (this._qemuExtKeyEventSupported && scancode) { - // 0 is NoSymbol - keysym = keysym || 0; - - Log.Info("Sending key (" + (down ? "down" : "up") + "): keysym " + keysym + ", scancode " + scancode); - - RFB.messages.QEMUExtendedKeyEvent(this._sock, keysym, down, scancode); - } else { - if (!keysym) { - return; - } - Log.Info("Sending keysym (" + (down ? "down" : "up") + "): " + keysym); - RFB.messages.keyEvent(this._sock, keysym, down ? 1 : 0); - } - } - - focus(options) { - this._canvas.focus(options); - } - - blur() { - this._canvas.blur(); - } - - clipboardPasteFrom(text) { - if (this._rfbConnectionState !== 'connected' || this._viewOnly) { return; } - - if (this._clipboardServerCapabilitiesFormats[extendedClipboardFormatText] && - this._clipboardServerCapabilitiesActions[extendedClipboardActionNotify]) { - - this._clipboardText = text; - RFB.messages.extendedClipboardNotify(this._sock, [extendedClipboardFormatText]); - } else { - let length, i; - let data; - - length = 0; - // eslint-disable-next-line no-unused-vars - for (let codePoint of text) { - length++; - } - - data = new Uint8Array(length); - - i = 0; - for (let codePoint of text) { - let code = codePoint.codePointAt(0); - - /* Only ISO 8859-1 is supported */ - if (code > 0xff) { - code = 0x3f; // '?' - } - - data[i++] = code; - } - - RFB.messages.clientCutText(this._sock, data); - } - } - - getImageData() { - return this._display.getImageData(); - } - - toDataURL(type, encoderOptions) { - return this._display.toDataURL(type, encoderOptions); - } - - toBlob(callback, type, quality) { - return this._display.toBlob(callback, type, quality); - } - - // ===== PRIVATE METHODS ===== - - _connect() { - Log.Debug(">> RFB.connect"); - - if (this._url) { - Log.Info(`connecting to ${this._url}`); - this._sock.open(this._url, this._wsProtocols); - } else { - Log.Info(`attaching ${this._rawChannel} to Websock`); - this._sock.attach(this._rawChannel); - - if (this._sock.readyState === 'closed') { - throw Error("Cannot use already closed WebSocket/RTCDataChannel"); - } - - if (this._sock.readyState === 'open') { - // FIXME: _socketOpen() can in theory call _fail(), which - // isn't allowed this early, but I'm not sure that can - // happen without a bug messing up our state variables - this._socketOpen(); - } - } - - // Make our elements part of the page - this._target.appendChild(this._screen); - - this._gestures.attach(this._canvas); - - this._cursor.attach(this._canvas); - this._refreshCursor(); - - // Monitor size changes of the screen element - this._resizeObserver.observe(this._screen); - - // Always grab focus on some kind of click event - this._canvas.addEventListener("mousedown", this._eventHandlers.focusCanvas); - this._canvas.addEventListener("touchstart", this._eventHandlers.focusCanvas); - - // Mouse events - this._canvas.addEventListener('mousedown', this._eventHandlers.handleMouse); - this._canvas.addEventListener('mouseup', this._eventHandlers.handleMouse); - this._canvas.addEventListener('mousemove', this._eventHandlers.handleMouse); - // Prevent middle-click pasting (see handler for why we bind to document) - this._canvas.addEventListener('click', this._eventHandlers.handleMouse); - // preventDefault() on mousedown doesn't stop this event for some - // reason so we have to explicitly block it - this._canvas.addEventListener('contextmenu', this._eventHandlers.handleMouse); - - // Wheel events - this._canvas.addEventListener("wheel", this._eventHandlers.handleWheel); - - // Gesture events - this._canvas.addEventListener("gesturestart", this._eventHandlers.handleGesture); - this._canvas.addEventListener("gesturemove", this._eventHandlers.handleGesture); - this._canvas.addEventListener("gestureend", this._eventHandlers.handleGesture); - - Log.Debug("<< RFB.connect"); - } - - _disconnect() { - Log.Debug(">> RFB.disconnect"); - this._cursor.detach(); - this._canvas.removeEventListener("gesturestart", this._eventHandlers.handleGesture); - this._canvas.removeEventListener("gesturemove", this._eventHandlers.handleGesture); - this._canvas.removeEventListener("gestureend", this._eventHandlers.handleGesture); - this._canvas.removeEventListener("wheel", this._eventHandlers.handleWheel); - this._canvas.removeEventListener('mousedown', this._eventHandlers.handleMouse); - this._canvas.removeEventListener('mouseup', this._eventHandlers.handleMouse); - this._canvas.removeEventListener('mousemove', this._eventHandlers.handleMouse); - this._canvas.removeEventListener('click', this._eventHandlers.handleMouse); - this._canvas.removeEventListener('contextmenu', this._eventHandlers.handleMouse); - this._canvas.removeEventListener("mousedown", this._eventHandlers.focusCanvas); - this._canvas.removeEventListener("touchstart", this._eventHandlers.focusCanvas); - this._resizeObserver.disconnect(); - this._keyboard.ungrab(); - this._gestures.detach(); - this._sock.close(); - try { - this._target.removeChild(this._screen); - } catch (e) { - if (e.name === 'NotFoundError') { - // Some cases where the initial connection fails - // can disconnect before the _screen is created - } else { - throw e; - } - } - clearTimeout(this._resizeTimeout); - clearTimeout(this._mouseMoveTimer); - Log.Debug("<< RFB.disconnect"); - } - - _socketOpen() { - if ((this._rfbConnectionState === 'connecting') && - (this._rfbInitState === '')) { - this._rfbInitState = 'ProtocolVersion'; - Log.Debug("Starting VNC handshake"); - } else { - this._fail("Unexpected server connection while " + - this._rfbConnectionState); - } - } - - _socketClose(e) { - Log.Debug("WebSocket on-close event"); - let msg = ""; - if (e.code) { - msg = "(code: " + e.code; - if (e.reason) { - msg += ", reason: " + e.reason; - } - msg += ")"; - } - switch (this._rfbConnectionState) { - case 'connecting': - this._fail("Connection closed " + msg); - break; - case 'connected': - // Handle disconnects that were initiated server-side - this._updateConnectionState('disconnecting'); - this._updateConnectionState('disconnected'); - break; - case 'disconnecting': - // Normal disconnection path - this._updateConnectionState('disconnected'); - break; - case 'disconnected': - this._fail("Unexpected server disconnect " + - "when already disconnected " + msg); - break; - default: - this._fail("Unexpected server disconnect before connecting " + - msg); - break; - } - this._sock.off('close'); - // Delete reference to raw channel to allow cleanup. - this._rawChannel = null; - } - - _socketError(e) { - Log.Warn("WebSocket on-error event"); - } - - _focusCanvas(event) { - if (!this.focusOnClick) { - return; - } - - this.focus({ preventScroll: true }); - } - - _setDesktopName(name) { - this._fbName = name; - this.dispatchEvent(new CustomEvent( - "desktopname", - { detail: { name: this._fbName } })); - } - - _saveExpectedClientSize() { - this._expectedClientWidth = this._screen.clientWidth; - this._expectedClientHeight = this._screen.clientHeight; - } - - _currentClientSize() { - return [this._screen.clientWidth, this._screen.clientHeight]; - } - - _clientHasExpectedSize() { - const [currentWidth, currentHeight] = this._currentClientSize(); - return currentWidth == this._expectedClientWidth && - currentHeight == this._expectedClientHeight; - } - - _handleResize() { - // Don't change anything if the client size is already as expected - if (this._clientHasExpectedSize()) { - return; - } - // If the window resized then our screen element might have - // as well. Update the viewport dimensions. - window.requestAnimationFrame(() => { - this._updateClip(); - this._updateScale(); - }); - - if (this._resizeSession) { - // Request changing the resolution of the remote display to - // the size of the local browser viewport. - - // In order to not send multiple requests before the browser-resize - // is finished we wait 0.5 seconds before sending the request. - clearTimeout(this._resizeTimeout); - this._resizeTimeout = setTimeout(this._requestRemoteResize.bind(this), 500); - } - } - - // Update state of clipping in Display object, and make sure the - // configured viewport matches the current screen size - _updateClip() { - const curClip = this._display.clipViewport; - let newClip = this._clipViewport; - - if (this._scaleViewport) { - // Disable viewport clipping if we are scaling - newClip = false; - } - - if (curClip !== newClip) { - this._display.clipViewport = newClip; - } - - if (newClip) { - // When clipping is enabled, the screen is limited to - // the size of the container. - const size = this._screenSize(); - this._display.viewportChangeSize(size.w, size.h); - this._fixScrollbars(); - this._setClippingViewport(size.w < this._display.width || - size.h < this._display.height); - } else { - this._setClippingViewport(false); - } - - // When changing clipping we might show or hide scrollbars. - // This causes the expected client dimensions to change. - if (curClip !== newClip) { - this._saveExpectedClientSize(); - } - } - - _updateScale() { - if (!this._scaleViewport) { - this._display.scale = 1.0; - } else { - const size = this._screenSize(); - this._display.autoscale(size.w, size.h); - } - this._fixScrollbars(); - } - - // Requests a change of remote desktop size. This message is an extension - // and may only be sent if we have received an ExtendedDesktopSize message - _requestRemoteResize() { - clearTimeout(this._resizeTimeout); - this._resizeTimeout = null; - - if (!this._resizeSession || this._viewOnly || - !this._supportsSetDesktopSize) { - return; - } - - const size = this._screenSize(); - - RFB.messages.setDesktopSize(this._sock, - Math.floor(size.w), Math.floor(size.h), - this._screenID, this._screenFlags); - - Log.Debug('Requested new desktop size: ' + - size.w + 'x' + size.h); - } - - // Gets the the size of the available screen - _screenSize() { - let r = this._screen.getBoundingClientRect(); - return { w: r.width, h: r.height }; - } - - _fixScrollbars() { - // This is a hack because Safari on macOS screws up the calculation - // for when scrollbars are needed. We get scrollbars when making the - // browser smaller, despite remote resize being enabled. So to fix it - // we temporarily toggle them off and on. - const orig = this._screen.style.overflow; - this._screen.style.overflow = 'hidden'; - // Force Safari to recalculate the layout by asking for - // an element's dimensions - this._screen.getBoundingClientRect(); - this._screen.style.overflow = orig; - } - - /* - * Connection states: - * connecting - * connected - * disconnecting - * disconnected - permanent state - */ - _updateConnectionState(state) { - const oldstate = this._rfbConnectionState; - - if (state === oldstate) { - Log.Debug("Already in state '" + state + "', ignoring"); - return; - } - - // The 'disconnected' state is permanent for each RFB object - if (oldstate === 'disconnected') { - Log.Error("Tried changing state of a disconnected RFB object"); - return; - } - - // Ensure proper transitions before doing anything - switch (state) { - case 'connected': - if (oldstate !== 'connecting') { - Log.Error("Bad transition to connected state, " + - "previous connection state: " + oldstate); - return; - } - break; - - case 'disconnected': - if (oldstate !== 'disconnecting') { - Log.Error("Bad transition to disconnected state, " + - "previous connection state: " + oldstate); - return; - } - break; - - case 'connecting': - if (oldstate !== '') { - Log.Error("Bad transition to connecting state, " + - "previous connection state: " + oldstate); - return; - } - break; - - case 'disconnecting': - if (oldstate !== 'connected' && oldstate !== 'connecting') { - Log.Error("Bad transition to disconnecting state, " + - "previous connection state: " + oldstate); - return; - } - break; - - default: - Log.Error("Unknown connection state: " + state); - return; - } - - // State change actions - - this._rfbConnectionState = state; - - Log.Debug("New state '" + state + "', was '" + oldstate + "'."); - - if (this._disconnTimer && state !== 'disconnecting') { - Log.Debug("Clearing disconnect timer"); - clearTimeout(this._disconnTimer); - this._disconnTimer = null; - - // make sure we don't get a double event - this._sock.off('close'); - } - - switch (state) { - case 'connecting': - this._connect(); - break; - - case 'connected': - this.dispatchEvent(new CustomEvent("connect", { detail: {} })); - break; - - case 'disconnecting': - this._disconnect(); - - this._disconnTimer = setTimeout(() => { - Log.Error("Disconnection timed out."); - this._updateConnectionState('disconnected'); - }, DISCONNECT_TIMEOUT * 1000); - break; - - case 'disconnected': - this.dispatchEvent(new CustomEvent( - "disconnect", { detail: - { clean: this._rfbCleanDisconnect } })); - break; - } - } - - /* Print errors and disconnect - * - * The parameter 'details' is used for information that - * should be logged but not sent to the user interface. - */ - _fail(details) { - switch (this._rfbConnectionState) { - case 'disconnecting': - Log.Error("Failed when disconnecting: " + details); - break; - case 'connected': - Log.Error("Failed while connected: " + details); - break; - case 'connecting': - Log.Error("Failed when connecting: " + details); - break; - default: - Log.Error("RFB failure: " + details); - break; - } - this._rfbCleanDisconnect = false; //This is sent to the UI - - // Transition to disconnected without waiting for socket to close - this._updateConnectionState('disconnecting'); - this._updateConnectionState('disconnected'); - - return false; - } - - _setCapability(cap, val) { - this._capabilities[cap] = val; - this.dispatchEvent(new CustomEvent("capabilities", - { detail: { capabilities: this._capabilities } })); - } - - _handleMessage() { - if (this._sock.rQwait("message", 1)) { - Log.Warn("handleMessage called on an empty receive queue"); - return; - } - - switch (this._rfbConnectionState) { - case 'disconnected': - Log.Error("Got data while disconnected"); - break; - case 'connected': - while (true) { - if (this._flushing) { - break; - } - if (!this._normalMsg()) { - break; - } - if (this._sock.rQwait("message", 1)) { - break; - } - } - break; - case 'connecting': - while (this._rfbConnectionState === 'connecting') { - if (!this._initMsg()) { - break; - } - } - break; - default: - Log.Error("Got data while in an invalid state"); - break; - } - } - - _handleKeyEvent(keysym, code, down, numlock, capslock) { - // If remote state of capslock is known, and it doesn't match the local led state of - // the keyboard, we send a capslock keypress first to bring it into sync. - // If we just pressed CapsLock, or we toggled it remotely due to it being out of sync - // we clear the remote state so that we don't send duplicate or spurious fixes, - // since it may take some time to receive the new remote CapsLock state. - if (code == 'CapsLock' && down) { - this._remoteCapsLock = null; - } - if (this._remoteCapsLock !== null && capslock !== null && this._remoteCapsLock !== capslock && down) { - Log.Debug("Fixing remote caps lock"); - - this.sendKey(KeyTable.XK_Caps_Lock, 'CapsLock', true); - this.sendKey(KeyTable.XK_Caps_Lock, 'CapsLock', false); - // We clear the remote capsLock state when we do this to prevent issues with doing this twice - // before we receive an update of the the remote state. - this._remoteCapsLock = null; - } - - // Logic for numlock is exactly the same. - if (code == 'NumLock' && down) { - this._remoteNumLock = null; - } - if (this._remoteNumLock !== null && numlock !== null && this._remoteNumLock !== numlock && down) { - Log.Debug("Fixing remote num lock"); - this.sendKey(KeyTable.XK_Num_Lock, 'NumLock', true); - this.sendKey(KeyTable.XK_Num_Lock, 'NumLock', false); - this._remoteNumLock = null; - } - this.sendKey(keysym, code, down); - } - - _handleMouse(ev) { - /* - * We don't check connection status or viewOnly here as the - * mouse events might be used to control the viewport - */ - - if (ev.type === 'click') { - /* - * Note: This is only needed for the 'click' event as it fails - * to fire properly for the target element so we have - * to listen on the document element instead. - */ - if (ev.target !== this._canvas) { - return; - } - } - - // FIXME: if we're in view-only and not dragging, - // should we stop events? - ev.stopPropagation(); - ev.preventDefault(); - - if ((ev.type === 'click') || (ev.type === 'contextmenu')) { - return; - } - - let pos = clientToElement(ev.clientX, ev.clientY, - this._canvas); - - switch (ev.type) { - case 'mousedown': - setCapture(this._canvas); - this._handleMouseButton(pos.x, pos.y, - true, 1 << ev.button); - break; - case 'mouseup': - this._handleMouseButton(pos.x, pos.y, - false, 1 << ev.button); - break; - case 'mousemove': - this._handleMouseMove(pos.x, pos.y); - break; - } - } - - _handleMouseButton(x, y, down, bmask) { - if (this.dragViewport) { - if (down && !this._viewportDragging) { - this._viewportDragging = true; - this._viewportDragPos = {'x': x, 'y': y}; - this._viewportHasMoved = false; - - // Skip sending mouse events - return; - } else { - this._viewportDragging = false; - - // If we actually performed a drag then we are done - // here and should not send any mouse events - if (this._viewportHasMoved) { - return; - } - - // Otherwise we treat this as a mouse click event. - // Send the button down event here, as the button up - // event is sent at the end of this function. - this._sendMouse(x, y, bmask); - } - } - - // Flush waiting move event first - if (this._mouseMoveTimer !== null) { - clearTimeout(this._mouseMoveTimer); - this._mouseMoveTimer = null; - this._sendMouse(x, y, this._mouseButtonMask); - } - - if (down) { - this._mouseButtonMask |= bmask; - } else { - this._mouseButtonMask &= ~bmask; - } - - this._sendMouse(x, y, this._mouseButtonMask); - } - - _handleMouseMove(x, y) { - if (this._viewportDragging) { - const deltaX = this._viewportDragPos.x - x; - const deltaY = this._viewportDragPos.y - y; - - if (this._viewportHasMoved || (Math.abs(deltaX) > dragThreshold || - Math.abs(deltaY) > dragThreshold)) { - this._viewportHasMoved = true; - - this._viewportDragPos = {'x': x, 'y': y}; - this._display.viewportChangePos(deltaX, deltaY); - } - - // Skip sending mouse events - return; - } - - this._mousePos = { 'x': x, 'y': y }; - - // Limit many mouse move events to one every MOUSE_MOVE_DELAY ms - if (this._mouseMoveTimer == null) { - - const timeSinceLastMove = Date.now() - this._mouseLastMoveTime; - if (timeSinceLastMove > MOUSE_MOVE_DELAY) { - this._sendMouse(x, y, this._mouseButtonMask); - this._mouseLastMoveTime = Date.now(); - } else { - // Too soon since the latest move, wait the remaining time - this._mouseMoveTimer = setTimeout(() => { - this._handleDelayedMouseMove(); - }, MOUSE_MOVE_DELAY - timeSinceLastMove); - } - } - } - - _handleDelayedMouseMove() { - this._mouseMoveTimer = null; - this._sendMouse(this._mousePos.x, this._mousePos.y, - this._mouseButtonMask); - this._mouseLastMoveTime = Date.now(); - } - - _sendMouse(x, y, mask) { - if (this._rfbConnectionState !== 'connected') { return; } - if (this._viewOnly) { return; } // View only, skip mouse events - - RFB.messages.pointerEvent(this._sock, this._display.absX(x), - this._display.absY(y), mask); - } - - _handleWheel(ev) { - if (this._rfbConnectionState !== 'connected') { return; } - if (this._viewOnly) { return; } // View only, skip mouse events - - ev.stopPropagation(); - ev.preventDefault(); - - let pos = clientToElement(ev.clientX, ev.clientY, - this._canvas); - - let dX = ev.deltaX; - let dY = ev.deltaY; - - // Pixel units unless it's non-zero. - // Note that if deltamode is line or page won't matter since we aren't - // sending the mouse wheel delta to the server anyway. - // The difference between pixel and line can be important however since - // we have a threshold that can be smaller than the line height. - if (ev.deltaMode !== 0) { - dX *= WHEEL_LINE_HEIGHT; - dY *= WHEEL_LINE_HEIGHT; - } - - // Mouse wheel events are sent in steps over VNC. This means that the VNC - // protocol can't handle a wheel event with specific distance or speed. - // Therefor, if we get a lot of small mouse wheel events we combine them. - this._accumulatedWheelDeltaX += dX; - this._accumulatedWheelDeltaY += dY; - - // Generate a mouse wheel step event when the accumulated delta - // for one of the axes is large enough. - if (Math.abs(this._accumulatedWheelDeltaX) >= WHEEL_STEP) { - if (this._accumulatedWheelDeltaX < 0) { - this._handleMouseButton(pos.x, pos.y, true, 1 << 5); - this._handleMouseButton(pos.x, pos.y, false, 1 << 5); - } else if (this._accumulatedWheelDeltaX > 0) { - this._handleMouseButton(pos.x, pos.y, true, 1 << 6); - this._handleMouseButton(pos.x, pos.y, false, 1 << 6); - } - - this._accumulatedWheelDeltaX = 0; - } - if (Math.abs(this._accumulatedWheelDeltaY) >= WHEEL_STEP) { - if (this._accumulatedWheelDeltaY < 0) { - this._handleMouseButton(pos.x, pos.y, true, 1 << 3); - this._handleMouseButton(pos.x, pos.y, false, 1 << 3); - } else if (this._accumulatedWheelDeltaY > 0) { - this._handleMouseButton(pos.x, pos.y, true, 1 << 4); - this._handleMouseButton(pos.x, pos.y, false, 1 << 4); - } - - this._accumulatedWheelDeltaY = 0; - } - } - - _fakeMouseMove(ev, elementX, elementY) { - this._handleMouseMove(elementX, elementY); - this._cursor.move(ev.detail.clientX, ev.detail.clientY); - } - - _handleTapEvent(ev, bmask) { - let pos = clientToElement(ev.detail.clientX, ev.detail.clientY, - this._canvas); - - // If the user quickly taps multiple times we assume they meant to - // hit the same spot, so slightly adjust coordinates - - if ((this._gestureLastTapTime !== null) && - ((Date.now() - this._gestureLastTapTime) < DOUBLE_TAP_TIMEOUT) && - (this._gestureFirstDoubleTapEv.detail.type === ev.detail.type)) { - let dx = this._gestureFirstDoubleTapEv.detail.clientX - ev.detail.clientX; - let dy = this._gestureFirstDoubleTapEv.detail.clientY - ev.detail.clientY; - let distance = Math.hypot(dx, dy); - - if (distance < DOUBLE_TAP_THRESHOLD) { - pos = clientToElement(this._gestureFirstDoubleTapEv.detail.clientX, - this._gestureFirstDoubleTapEv.detail.clientY, - this._canvas); - } else { - this._gestureFirstDoubleTapEv = ev; - } - } else { - this._gestureFirstDoubleTapEv = ev; - } - this._gestureLastTapTime = Date.now(); - - this._fakeMouseMove(this._gestureFirstDoubleTapEv, pos.x, pos.y); - this._handleMouseButton(pos.x, pos.y, true, bmask); - this._handleMouseButton(pos.x, pos.y, false, bmask); - } - - _handleGesture(ev) { - let magnitude; - - let pos = clientToElement(ev.detail.clientX, ev.detail.clientY, - this._canvas); - switch (ev.type) { - case 'gesturestart': - switch (ev.detail.type) { - case 'onetap': - this._handleTapEvent(ev, 0x1); - break; - case 'twotap': - this._handleTapEvent(ev, 0x4); - break; - case 'threetap': - this._handleTapEvent(ev, 0x2); - break; - case 'drag': - this._fakeMouseMove(ev, pos.x, pos.y); - this._handleMouseButton(pos.x, pos.y, true, 0x1); - break; - case 'longpress': - this._fakeMouseMove(ev, pos.x, pos.y); - this._handleMouseButton(pos.x, pos.y, true, 0x4); - break; - - case 'twodrag': - this._gestureLastMagnitudeX = ev.detail.magnitudeX; - this._gestureLastMagnitudeY = ev.detail.magnitudeY; - this._fakeMouseMove(ev, pos.x, pos.y); - break; - case 'pinch': - this._gestureLastMagnitudeX = Math.hypot(ev.detail.magnitudeX, - ev.detail.magnitudeY); - this._fakeMouseMove(ev, pos.x, pos.y); - break; - } - break; - - case 'gesturemove': - switch (ev.detail.type) { - case 'onetap': - case 'twotap': - case 'threetap': - break; - case 'drag': - case 'longpress': - this._fakeMouseMove(ev, pos.x, pos.y); - break; - case 'twodrag': - // Always scroll in the same position. - // We don't know if the mouse was moved so we need to move it - // every update. - this._fakeMouseMove(ev, pos.x, pos.y); - while ((ev.detail.magnitudeY - this._gestureLastMagnitudeY) > GESTURE_SCRLSENS) { - this._handleMouseButton(pos.x, pos.y, true, 0x8); - this._handleMouseButton(pos.x, pos.y, false, 0x8); - this._gestureLastMagnitudeY += GESTURE_SCRLSENS; - } - while ((ev.detail.magnitudeY - this._gestureLastMagnitudeY) < -GESTURE_SCRLSENS) { - this._handleMouseButton(pos.x, pos.y, true, 0x10); - this._handleMouseButton(pos.x, pos.y, false, 0x10); - this._gestureLastMagnitudeY -= GESTURE_SCRLSENS; - } - while ((ev.detail.magnitudeX - this._gestureLastMagnitudeX) > GESTURE_SCRLSENS) { - this._handleMouseButton(pos.x, pos.y, true, 0x20); - this._handleMouseButton(pos.x, pos.y, false, 0x20); - this._gestureLastMagnitudeX += GESTURE_SCRLSENS; - } - while ((ev.detail.magnitudeX - this._gestureLastMagnitudeX) < -GESTURE_SCRLSENS) { - this._handleMouseButton(pos.x, pos.y, true, 0x40); - this._handleMouseButton(pos.x, pos.y, false, 0x40); - this._gestureLastMagnitudeX -= GESTURE_SCRLSENS; - } - break; - case 'pinch': - // Always scroll in the same position. - // We don't know if the mouse was moved so we need to move it - // every update. - this._fakeMouseMove(ev, pos.x, pos.y); - magnitude = Math.hypot(ev.detail.magnitudeX, ev.detail.magnitudeY); - if (Math.abs(magnitude - this._gestureLastMagnitudeX) > GESTURE_ZOOMSENS) { - this._handleKeyEvent(KeyTable.XK_Control_L, "ControlLeft", true); - while ((magnitude - this._gestureLastMagnitudeX) > GESTURE_ZOOMSENS) { - this._handleMouseButton(pos.x, pos.y, true, 0x8); - this._handleMouseButton(pos.x, pos.y, false, 0x8); - this._gestureLastMagnitudeX += GESTURE_ZOOMSENS; - } - while ((magnitude - this._gestureLastMagnitudeX) < -GESTURE_ZOOMSENS) { - this._handleMouseButton(pos.x, pos.y, true, 0x10); - this._handleMouseButton(pos.x, pos.y, false, 0x10); - this._gestureLastMagnitudeX -= GESTURE_ZOOMSENS; - } - } - this._handleKeyEvent(KeyTable.XK_Control_L, "ControlLeft", false); - break; - } - break; - - case 'gestureend': - switch (ev.detail.type) { - case 'onetap': - case 'twotap': - case 'threetap': - case 'pinch': - case 'twodrag': - break; - case 'drag': - this._fakeMouseMove(ev, pos.x, pos.y); - this._handleMouseButton(pos.x, pos.y, false, 0x1); - break; - case 'longpress': - this._fakeMouseMove(ev, pos.x, pos.y); - this._handleMouseButton(pos.x, pos.y, false, 0x4); - break; - } - break; - } - } - - // Message Handlers - - _negotiateProtocolVersion() { - if (this._sock.rQwait("version", 12)) { - return false; - } - - const sversion = this._sock.rQshiftStr(12).substr(4, 7); - Log.Info("Server ProtocolVersion: " + sversion); - let isRepeater = 0; - switch (sversion) { - case "000.000": // UltraVNC repeater - isRepeater = 1; - break; - case "003.003": - case "003.006": // UltraVNC - this._rfbVersion = 3.3; - break; - case "003.007": - this._rfbVersion = 3.7; - break; - case "003.008": - case "003.889": // Apple Remote Desktop - case "004.000": // Intel AMT KVM - case "004.001": // RealVNC 4.6 - case "005.000": // RealVNC 5.3 - this._rfbVersion = 3.8; - break; - default: - return this._fail("Invalid server version " + sversion); - } - - if (isRepeater) { - let repeaterID = "ID:" + this._repeaterID; - while (repeaterID.length < 250) { - repeaterID += "\0"; - } - this._sock.sQpushString(repeaterID); - this._sock.flush(); - return true; - } - - if (this._rfbVersion > this._rfbMaxVersion) { - this._rfbVersion = this._rfbMaxVersion; - } - - const cversion = "00" + parseInt(this._rfbVersion, 10) + - ".00" + ((this._rfbVersion * 10) % 10); - this._sock.sQpushString("RFB " + cversion + "\n"); - this._sock.flush(); - Log.Debug('Sent ProtocolVersion: ' + cversion); - - this._rfbInitState = 'Security'; - } - - _isSupportedSecurityType(type) { - const clientTypes = [ - securityTypeNone, - securityTypeVNCAuth, - securityTypeRA2ne, - securityTypeTight, - securityTypeVeNCrypt, - securityTypeXVP, - securityTypeARD, - securityTypeMSLogonII, - securityTypePlain, - ]; - - return clientTypes.includes(type); - } - - _negotiateSecurity() { - if (this._rfbVersion >= 3.7) { - // Server sends supported list, client decides - const numTypes = this._sock.rQshift8(); - if (this._sock.rQwait("security type", numTypes, 1)) { return false; } - - if (numTypes === 0) { - this._rfbInitState = "SecurityReason"; - this._securityContext = "no security types"; - this._securityStatus = 1; - return true; - } - - const types = this._sock.rQshiftBytes(numTypes); - Log.Debug("Server security types: " + types); - - // Look for a matching security type in the order that the - // server prefers - this._rfbAuthScheme = -1; - for (let type of types) { - if (this._isSupportedSecurityType(type)) { - this._rfbAuthScheme = type; - break; - } - } - - if (this._rfbAuthScheme === -1) { - return this._fail("Unsupported security types (types: " + types + ")"); - } - - this._sock.sQpush8(this._rfbAuthScheme); - this._sock.flush(); - } else { - // Server decides - if (this._sock.rQwait("security scheme", 4)) { return false; } - this._rfbAuthScheme = this._sock.rQshift32(); - - if (this._rfbAuthScheme == 0) { - this._rfbInitState = "SecurityReason"; - this._securityContext = "authentication scheme"; - this._securityStatus = 1; - return true; - } - } - - this._rfbInitState = 'Authentication'; - Log.Debug('Authenticating using scheme: ' + this._rfbAuthScheme); - - return true; - } - - _handleSecurityReason() { - if (this._sock.rQwait("reason length", 4)) { - return false; - } - const strlen = this._sock.rQshift32(); - let reason = ""; - - if (strlen > 0) { - if (this._sock.rQwait("reason", strlen, 4)) { return false; } - reason = this._sock.rQshiftStr(strlen); - } - - if (reason !== "") { - this.dispatchEvent(new CustomEvent( - "securityfailure", - { detail: { status: this._securityStatus, - reason: reason } })); - - return this._fail("Security negotiation failed on " + - this._securityContext + - " (reason: " + reason + ")"); - } else { - this.dispatchEvent(new CustomEvent( - "securityfailure", - { detail: { status: this._securityStatus } })); - - return this._fail("Security negotiation failed on " + - this._securityContext); - } - } - - // authentication - _negotiateXvpAuth() { - if (this._rfbCredentials.username === undefined || - this._rfbCredentials.password === undefined || - this._rfbCredentials.target === undefined) { - this.dispatchEvent(new CustomEvent( - "credentialsrequired", - { detail: { types: ["username", "password", "target"] } })); - return false; - } - - this._sock.sQpush8(this._rfbCredentials.username.length); - this._sock.sQpush8(this._rfbCredentials.target.length); - this._sock.sQpushString(this._rfbCredentials.username); - this._sock.sQpushString(this._rfbCredentials.target); - - this._sock.flush(); - - this._rfbAuthScheme = securityTypeVNCAuth; - - return this._negotiateAuthentication(); - } - - // VeNCrypt authentication, currently only supports version 0.2 and only Plain subtype - _negotiateVeNCryptAuth() { - - // waiting for VeNCrypt version - if (this._rfbVeNCryptState == 0) { - if (this._sock.rQwait("vencrypt version", 2)) { return false; } - - const major = this._sock.rQshift8(); - const minor = this._sock.rQshift8(); - - if (!(major == 0 && minor == 2)) { - return this._fail("Unsupported VeNCrypt version " + major + "." + minor); - } - - this._sock.sQpush8(0); - this._sock.sQpush8(2); - this._sock.flush(); - this._rfbVeNCryptState = 1; - } - - // waiting for ACK - if (this._rfbVeNCryptState == 1) { - if (this._sock.rQwait("vencrypt ack", 1)) { return false; } - - const res = this._sock.rQshift8(); - - if (res != 0) { - return this._fail("VeNCrypt failure " + res); - } - - this._rfbVeNCryptState = 2; - } - // must fall through here (i.e. no "else if"), beacause we may have already received - // the subtypes length and won't be called again - - if (this._rfbVeNCryptState == 2) { // waiting for subtypes length - if (this._sock.rQwait("vencrypt subtypes length", 1)) { return false; } - - const subtypesLength = this._sock.rQshift8(); - if (subtypesLength < 1) { - return this._fail("VeNCrypt subtypes empty"); - } - - this._rfbVeNCryptSubtypesLength = subtypesLength; - this._rfbVeNCryptState = 3; - } - - // waiting for subtypes list - if (this._rfbVeNCryptState == 3) { - if (this._sock.rQwait("vencrypt subtypes", 4 * this._rfbVeNCryptSubtypesLength)) { return false; } - - const subtypes = []; - for (let i = 0; i < this._rfbVeNCryptSubtypesLength; i++) { - subtypes.push(this._sock.rQshift32()); - } - - // Look for a matching security type in the order that the - // server prefers - this._rfbAuthScheme = -1; - for (let type of subtypes) { - // Avoid getting in to a loop - if (type === securityTypeVeNCrypt) { - continue; - } - - if (this._isSupportedSecurityType(type)) { - this._rfbAuthScheme = type; - break; - } - } - - if (this._rfbAuthScheme === -1) { - return this._fail("Unsupported security types (types: " + subtypes + ")"); - } - - this._sock.sQpush32(this._rfbAuthScheme); - this._sock.flush(); - - this._rfbVeNCryptState = 4; - return true; - } - } - - _negotiatePlainAuth() { - if (this._rfbCredentials.username === undefined || - this._rfbCredentials.password === undefined) { - this.dispatchEvent(new CustomEvent( - "credentialsrequired", - { detail: { types: ["username", "password"] } })); - return false; - } - - const user = encodeUTF8(this._rfbCredentials.username); - const pass = encodeUTF8(this._rfbCredentials.password); - - this._sock.sQpush32(user.length); - this._sock.sQpush32(pass.length); - this._sock.sQpushString(user); - this._sock.sQpushString(pass); - this._sock.flush(); - - this._rfbInitState = "SecurityResult"; - return true; - } - - _negotiateStdVNCAuth() { - if (this._sock.rQwait("auth challenge", 16)) { return false; } - - if (this._rfbCredentials.password === undefined) { - this.dispatchEvent(new CustomEvent( - "credentialsrequired", - { detail: { types: ["password"] } })); - return false; - } - - // TODO(directxman12): make genDES not require an Array - const challenge = Array.prototype.slice.call(this._sock.rQshiftBytes(16)); - const response = RFB.genDES(this._rfbCredentials.password, challenge); - this._sock.sQpushBytes(response); - this._sock.flush(); - this._rfbInitState = "SecurityResult"; - return true; - } - - _negotiateARDAuth() { - - if (this._rfbCredentials.username === undefined || - this._rfbCredentials.password === undefined) { - this.dispatchEvent(new CustomEvent( - "credentialsrequired", - { detail: { types: ["username", "password"] } })); - return false; - } - - if (this._rfbCredentials.ardPublicKey != undefined && - this._rfbCredentials.ardCredentials != undefined) { - // if the async web crypto is done return the results - this._sock.sQpushBytes(this._rfbCredentials.ardCredentials); - this._sock.sQpushBytes(this._rfbCredentials.ardPublicKey); - this._sock.flush(); - this._rfbCredentials.ardCredentials = null; - this._rfbCredentials.ardPublicKey = null; - this._rfbInitState = "SecurityResult"; - return true; - } - - if (this._sock.rQwait("read ard", 4)) { return false; } - - let generator = this._sock.rQshiftBytes(2); // DH base generator value - - let keyLength = this._sock.rQshift16(); - - if (this._sock.rQwait("read ard keylength", keyLength*2, 4)) { return false; } - - // read the server values - let prime = this._sock.rQshiftBytes(keyLength); // predetermined prime modulus - let serverPublicKey = this._sock.rQshiftBytes(keyLength); // other party's public key - - let clientKey = legacyCrypto.generateKey( - { name: "DH", g: generator, p: prime }, false, ["deriveBits"]); - this._negotiateARDAuthAsync(keyLength, serverPublicKey, clientKey); - - return false; - } - - async _negotiateARDAuthAsync(keyLength, serverPublicKey, clientKey) { - const clientPublicKey = legacyCrypto.exportKey("raw", clientKey.publicKey); - const sharedKey = legacyCrypto.deriveBits( - { name: "DH", public: serverPublicKey }, clientKey.privateKey, keyLength * 8); - - const username = encodeUTF8(this._rfbCredentials.username).substring(0, 63); - const password = encodeUTF8(this._rfbCredentials.password).substring(0, 63); - - const credentials = window.crypto.getRandomValues(new Uint8Array(128)); - for (let i = 0; i < username.length; i++) { - credentials[i] = username.charCodeAt(i); - } - credentials[username.length] = 0; - for (let i = 0; i < password.length; i++) { - credentials[64 + i] = password.charCodeAt(i); - } - credentials[64 + password.length] = 0; - - const key = await legacyCrypto.digest("MD5", sharedKey); - const cipher = await legacyCrypto.importKey( - "raw", key, { name: "AES-ECB" }, false, ["encrypt"]); - const encrypted = await legacyCrypto.encrypt({ name: "AES-ECB" }, cipher, credentials); - - this._rfbCredentials.ardCredentials = encrypted; - this._rfbCredentials.ardPublicKey = clientPublicKey; - - this._resumeAuthentication(); - } - - _negotiateTightUnixAuth() { - if (this._rfbCredentials.username === undefined || - this._rfbCredentials.password === undefined) { - this.dispatchEvent(new CustomEvent( - "credentialsrequired", - { detail: { types: ["username", "password"] } })); - return false; - } - - this._sock.sQpush32(this._rfbCredentials.username.length); - this._sock.sQpush32(this._rfbCredentials.password.length); - this._sock.sQpushString(this._rfbCredentials.username); - this._sock.sQpushString(this._rfbCredentials.password); - this._sock.flush(); - - this._rfbInitState = "SecurityResult"; - return true; - } - - _negotiateTightTunnels(numTunnels) { - const clientSupportedTunnelTypes = { - 0: { vendor: 'TGHT', signature: 'NOTUNNEL' } - }; - const serverSupportedTunnelTypes = {}; - // receive tunnel capabilities - for (let i = 0; i < numTunnels; i++) { - const capCode = this._sock.rQshift32(); - const capVendor = this._sock.rQshiftStr(4); - const capSignature = this._sock.rQshiftStr(8); - serverSupportedTunnelTypes[capCode] = { vendor: capVendor, signature: capSignature }; - } - - Log.Debug("Server Tight tunnel types: " + serverSupportedTunnelTypes); - - // Siemens touch panels have a VNC server that supports NOTUNNEL, - // but forgets to advertise it. Try to detect such servers by - // looking for their custom tunnel type. - if (serverSupportedTunnelTypes[1] && - (serverSupportedTunnelTypes[1].vendor === "SICR") && - (serverSupportedTunnelTypes[1].signature === "SCHANNEL")) { - Log.Debug("Detected Siemens server. Assuming NOTUNNEL support."); - serverSupportedTunnelTypes[0] = { vendor: 'TGHT', signature: 'NOTUNNEL' }; - } - - // choose the notunnel type - if (serverSupportedTunnelTypes[0]) { - if (serverSupportedTunnelTypes[0].vendor != clientSupportedTunnelTypes[0].vendor || - serverSupportedTunnelTypes[0].signature != clientSupportedTunnelTypes[0].signature) { - return this._fail("Client's tunnel type had the incorrect " + - "vendor or signature"); - } - Log.Debug("Selected tunnel type: " + clientSupportedTunnelTypes[0]); - this._sock.sQpush32(0); // use NOTUNNEL - this._sock.flush(); - return false; // wait until we receive the sub auth count to continue - } else { - return this._fail("Server wanted tunnels, but doesn't support " + - "the notunnel type"); - } - } - - _negotiateTightAuth() { - if (!this._rfbTightVNC) { // first pass, do the tunnel negotiation - if (this._sock.rQwait("num tunnels", 4)) { return false; } - const numTunnels = this._sock.rQshift32(); - if (numTunnels > 0 && this._sock.rQwait("tunnel capabilities", 16 * numTunnels, 4)) { return false; } - - this._rfbTightVNC = true; - - if (numTunnels > 0) { - this._negotiateTightTunnels(numTunnels); - return false; // wait until we receive the sub auth to continue - } - } - - // second pass, do the sub-auth negotiation - if (this._sock.rQwait("sub auth count", 4)) { return false; } - const subAuthCount = this._sock.rQshift32(); - if (subAuthCount === 0) { // empty sub-auth list received means 'no auth' subtype selected - this._rfbInitState = 'SecurityResult'; - return true; - } - - if (this._sock.rQwait("sub auth capabilities", 16 * subAuthCount, 4)) { return false; } - - const clientSupportedTypes = { - 'STDVNOAUTH__': 1, - 'STDVVNCAUTH_': 2, - 'TGHTULGNAUTH': 129 - }; - - const serverSupportedTypes = []; - - for (let i = 0; i < subAuthCount; i++) { - this._sock.rQshift32(); // capNum - const capabilities = this._sock.rQshiftStr(12); - serverSupportedTypes.push(capabilities); - } - - Log.Debug("Server Tight authentication types: " + serverSupportedTypes); - - for (let authType in clientSupportedTypes) { - if (serverSupportedTypes.indexOf(authType) != -1) { - this._sock.sQpush32(clientSupportedTypes[authType]); - this._sock.flush(); - Log.Debug("Selected authentication type: " + authType); - - switch (authType) { - case 'STDVNOAUTH__': // no auth - this._rfbInitState = 'SecurityResult'; - return true; - case 'STDVVNCAUTH_': - this._rfbAuthScheme = securityTypeVNCAuth; - return true; - case 'TGHTULGNAUTH': - this._rfbAuthScheme = securityTypeUnixLogon; - return true; - default: - return this._fail("Unsupported tiny auth scheme " + - "(scheme: " + authType + ")"); - } - } - } - - return this._fail("No supported sub-auth types!"); - } - - _handleRSAAESCredentialsRequired(event) { - this.dispatchEvent(event); - } - - _handleRSAAESServerVerification(event) { - this.dispatchEvent(event); - } - - _negotiateRA2neAuth() { - if (this._rfbRSAAESAuthenticationState === null) { - this._rfbRSAAESAuthenticationState = new RSAAESAuthenticationState(this._sock, () => this._rfbCredentials); - this._rfbRSAAESAuthenticationState.addEventListener( - "serververification", this._eventHandlers.handleRSAAESServerVerification); - this._rfbRSAAESAuthenticationState.addEventListener( - "credentialsrequired", this._eventHandlers.handleRSAAESCredentialsRequired); - } - this._rfbRSAAESAuthenticationState.checkInternalEvents(); - if (!this._rfbRSAAESAuthenticationState.hasStarted) { - this._rfbRSAAESAuthenticationState.negotiateRA2neAuthAsync() - .catch((e) => { - if (e.message !== "disconnect normally") { - this._fail(e.message); - } - }) - .then(() => { - this._rfbInitState = "SecurityResult"; - return true; - }).finally(() => { - this._rfbRSAAESAuthenticationState.removeEventListener( - "serververification", this._eventHandlers.handleRSAAESServerVerification); - this._rfbRSAAESAuthenticationState.removeEventListener( - "credentialsrequired", this._eventHandlers.handleRSAAESCredentialsRequired); - this._rfbRSAAESAuthenticationState = null; - }); - } - return false; - } - - _negotiateMSLogonIIAuth() { - if (this._sock.rQwait("mslogonii dh param", 24)) { return false; } - - if (this._rfbCredentials.username === undefined || - this._rfbCredentials.password === undefined) { - this.dispatchEvent(new CustomEvent( - "credentialsrequired", - { detail: { types: ["username", "password"] } })); - return false; - } - - const g = this._sock.rQshiftBytes(8); - const p = this._sock.rQshiftBytes(8); - const A = this._sock.rQshiftBytes(8); - const dhKey = legacyCrypto.generateKey({ name: "DH", g: g, p: p }, true, ["deriveBits"]); - const B = legacyCrypto.exportKey("raw", dhKey.publicKey); - const secret = legacyCrypto.deriveBits({ name: "DH", public: A }, dhKey.privateKey, 64); - - const key = legacyCrypto.importKey("raw", secret, { name: "DES-CBC" }, false, ["encrypt"]); - const username = encodeUTF8(this._rfbCredentials.username).substring(0, 255); - const password = encodeUTF8(this._rfbCredentials.password).substring(0, 63); - let usernameBytes = new Uint8Array(256); - let passwordBytes = new Uint8Array(64); - window.crypto.getRandomValues(usernameBytes); - window.crypto.getRandomValues(passwordBytes); - for (let i = 0; i < username.length; i++) { - usernameBytes[i] = username.charCodeAt(i); - } - usernameBytes[username.length] = 0; - for (let i = 0; i < password.length; i++) { - passwordBytes[i] = password.charCodeAt(i); - } - passwordBytes[password.length] = 0; - usernameBytes = legacyCrypto.encrypt({ name: "DES-CBC", iv: secret }, key, usernameBytes); - passwordBytes = legacyCrypto.encrypt({ name: "DES-CBC", iv: secret }, key, passwordBytes); - this._sock.sQpushBytes(B); - this._sock.sQpushBytes(usernameBytes); - this._sock.sQpushBytes(passwordBytes); - this._sock.flush(); - this._rfbInitState = "SecurityResult"; - return true; - } - - _negotiateAuthentication() { - switch (this._rfbAuthScheme) { - case securityTypeNone: - if (this._rfbVersion >= 3.8) { - this._rfbInitState = 'SecurityResult'; - } else { - this._rfbInitState = 'ClientInitialisation'; - } - return true; - - case securityTypeXVP: - return this._negotiateXvpAuth(); - - case securityTypeARD: - return this._negotiateARDAuth(); - - case securityTypeVNCAuth: - return this._negotiateStdVNCAuth(); - - case securityTypeTight: - return this._negotiateTightAuth(); - - case securityTypeVeNCrypt: - return this._negotiateVeNCryptAuth(); - - case securityTypePlain: - return this._negotiatePlainAuth(); - - case securityTypeUnixLogon: - return this._negotiateTightUnixAuth(); - - case securityTypeRA2ne: - return this._negotiateRA2neAuth(); - - case securityTypeMSLogonII: - return this._negotiateMSLogonIIAuth(); - - default: - return this._fail("Unsupported auth scheme (scheme: " + - this._rfbAuthScheme + ")"); - } - } - - _handleSecurityResult() { - if (this._sock.rQwait('VNC auth response ', 4)) { return false; } - - const status = this._sock.rQshift32(); - - if (status === 0) { // OK - this._rfbInitState = 'ClientInitialisation'; - Log.Debug('Authentication OK'); - return true; - } else { - if (this._rfbVersion >= 3.8) { - this._rfbInitState = "SecurityReason"; - this._securityContext = "security result"; - this._securityStatus = status; - return true; - } else { - this.dispatchEvent(new CustomEvent( - "securityfailure", - { detail: { status: status } })); - - return this._fail("Security handshake failed"); - } - } - } - - _negotiateServerInit() { - if (this._sock.rQwait("server initialization", 24)) { return false; } - - /* Screen size */ - const width = this._sock.rQshift16(); - const height = this._sock.rQshift16(); - - /* PIXEL_FORMAT */ - const bpp = this._sock.rQshift8(); - const depth = this._sock.rQshift8(); - const bigEndian = this._sock.rQshift8(); - const trueColor = this._sock.rQshift8(); - - const redMax = this._sock.rQshift16(); - const greenMax = this._sock.rQshift16(); - const blueMax = this._sock.rQshift16(); - const redShift = this._sock.rQshift8(); - const greenShift = this._sock.rQshift8(); - const blueShift = this._sock.rQshift8(); - this._sock.rQskipBytes(3); // padding - - // NB(directxman12): we don't want to call any callbacks or print messages until - // *after* we're past the point where we could backtrack - - /* Connection name/title */ - const nameLength = this._sock.rQshift32(); - if (this._sock.rQwait('server init name', nameLength, 24)) { return false; } - let name = this._sock.rQshiftStr(nameLength); - name = decodeUTF8(name, true); - - if (this._rfbTightVNC) { - if (this._sock.rQwait('TightVNC extended server init header', 8, 24 + nameLength)) { return false; } - // In TightVNC mode, ServerInit message is extended - const numServerMessages = this._sock.rQshift16(); - const numClientMessages = this._sock.rQshift16(); - const numEncodings = this._sock.rQshift16(); - this._sock.rQskipBytes(2); // padding - - const totalMessagesLength = (numServerMessages + numClientMessages + numEncodings) * 16; - if (this._sock.rQwait('TightVNC extended server init header', totalMessagesLength, 32 + nameLength)) { return false; } - - // we don't actually do anything with the capability information that TIGHT sends, - // so we just skip the all of this. - - // TIGHT server message capabilities - this._sock.rQskipBytes(16 * numServerMessages); - - // TIGHT client message capabilities - this._sock.rQskipBytes(16 * numClientMessages); - - // TIGHT encoding capabilities - this._sock.rQskipBytes(16 * numEncodings); - } - - // NB(directxman12): these are down here so that we don't run them multiple times - // if we backtrack - Log.Info("Screen: " + width + "x" + height + - ", bpp: " + bpp + ", depth: " + depth + - ", bigEndian: " + bigEndian + - ", trueColor: " + trueColor + - ", redMax: " + redMax + - ", greenMax: " + greenMax + - ", blueMax: " + blueMax + - ", redShift: " + redShift + - ", greenShift: " + greenShift + - ", blueShift: " + blueShift); - - // we're past the point where we could backtrack, so it's safe to call this - this._setDesktopName(name); - this._resize(width, height); - - if (!this._viewOnly) { this._keyboard.grab(); } - - this._fbDepth = 24; - - if (this._fbName === "Intel(r) AMT KVM") { - Log.Warn("Intel AMT KVM only supports 8/16 bit depths. Using low color mode."); - this._fbDepth = 8; - } - - RFB.messages.pixelFormat(this._sock, this._fbDepth, true); - this._sendEncodings(); - RFB.messages.fbUpdateRequest(this._sock, false, 0, 0, this._fbWidth, this._fbHeight); - - this._updateConnectionState('connected'); - return true; - } - - _sendEncodings() { - const encs = []; - - // In preference order - encs.push(encodings.encodingCopyRect); - // Only supported with full depth support - if (this._fbDepth == 24) { - encs.push(encodings.encodingTight); - encs.push(encodings.encodingTightPNG); - encs.push(encodings.encodingZRLE); - encs.push(encodings.encodingJPEG); - encs.push(encodings.encodingHextile); - encs.push(encodings.encodingRRE); - } - encs.push(encodings.encodingRaw); - - // Psuedo-encoding settings - encs.push(encodings.pseudoEncodingQualityLevel0 + this._qualityLevel); - encs.push(encodings.pseudoEncodingCompressLevel0 + this._compressionLevel); - - encs.push(encodings.pseudoEncodingDesktopSize); - encs.push(encodings.pseudoEncodingLastRect); - encs.push(encodings.pseudoEncodingQEMUExtendedKeyEvent); - encs.push(encodings.pseudoEncodingQEMULedEvent); - encs.push(encodings.pseudoEncodingExtendedDesktopSize); - encs.push(encodings.pseudoEncodingXvp); - encs.push(encodings.pseudoEncodingFence); - encs.push(encodings.pseudoEncodingContinuousUpdates); - encs.push(encodings.pseudoEncodingDesktopName); - encs.push(encodings.pseudoEncodingExtendedClipboard); - - if (this._fbDepth == 24) { - encs.push(encodings.pseudoEncodingVMwareCursor); - encs.push(encodings.pseudoEncodingCursor); - } - - RFB.messages.clientEncodings(this._sock, encs); - } - - /* RFB protocol initialization states: - * ProtocolVersion - * Security - * Authentication - * SecurityResult - * ClientInitialization - not triggered by server message - * ServerInitialization - */ - _initMsg() { - switch (this._rfbInitState) { - case 'ProtocolVersion': - return this._negotiateProtocolVersion(); - - case 'Security': - return this._negotiateSecurity(); - - case 'Authentication': - return this._negotiateAuthentication(); - - case 'SecurityResult': - return this._handleSecurityResult(); - - case 'SecurityReason': - return this._handleSecurityReason(); - - case 'ClientInitialisation': - this._sock.sQpush8(this._shared ? 1 : 0); // ClientInitialisation - this._sock.flush(); - this._rfbInitState = 'ServerInitialisation'; - return true; - - case 'ServerInitialisation': - return this._negotiateServerInit(); - - default: - return this._fail("Unknown init state (state: " + - this._rfbInitState + ")"); - } - } - - // Resume authentication handshake after it was paused for some - // reason, e.g. waiting for a password from the user - _resumeAuthentication() { - // We use setTimeout() so it's run in its own context, just like - // it originally did via the WebSocket's event handler - setTimeout(this._initMsg.bind(this), 0); - } - - _handleSetColourMapMsg() { - Log.Debug("SetColorMapEntries"); - - return this._fail("Unexpected SetColorMapEntries message"); - } - - _handleServerCutText() { - Log.Debug("ServerCutText"); - - if (this._sock.rQwait("ServerCutText header", 7, 1)) { return false; } - - this._sock.rQskipBytes(3); // Padding - - let length = this._sock.rQshift32(); - length = toSigned32bit(length); - - if (this._sock.rQwait("ServerCutText content", Math.abs(length), 8)) { return false; } - - if (length >= 0) { - //Standard msg - const text = this._sock.rQshiftStr(length); - if (this._viewOnly) { - return true; - } - - this.dispatchEvent(new CustomEvent( - "clipboard", - { detail: { text: text } })); - - } else { - //Extended msg. - length = Math.abs(length); - const flags = this._sock.rQshift32(); - let formats = flags & 0x0000FFFF; - let actions = flags & 0xFF000000; - - let isCaps = (!!(actions & extendedClipboardActionCaps)); - if (isCaps) { - this._clipboardServerCapabilitiesFormats = {}; - this._clipboardServerCapabilitiesActions = {}; - - // Update our server capabilities for Formats - for (let i = 0; i <= 15; i++) { - let index = 1 << i; - - // Check if format flag is set. - if ((formats & index)) { - this._clipboardServerCapabilitiesFormats[index] = true; - // We don't send unsolicited clipboard, so we - // ignore the size - this._sock.rQshift32(); - } - } - - // Update our server capabilities for Actions - for (let i = 24; i <= 31; i++) { - let index = 1 << i; - this._clipboardServerCapabilitiesActions[index] = !!(actions & index); - } - - /* Caps handling done, send caps with the clients - capabilities set as a response */ - let clientActions = [ - extendedClipboardActionCaps, - extendedClipboardActionRequest, - extendedClipboardActionPeek, - extendedClipboardActionNotify, - extendedClipboardActionProvide - ]; - RFB.messages.extendedClipboardCaps(this._sock, clientActions, {extendedClipboardFormatText: 0}); - - } else if (actions === extendedClipboardActionRequest) { - if (this._viewOnly) { - return true; - } - - // Check if server has told us it can handle Provide and there is clipboard data to send. - if (this._clipboardText != null && - this._clipboardServerCapabilitiesActions[extendedClipboardActionProvide]) { - - if (formats & extendedClipboardFormatText) { - RFB.messages.extendedClipboardProvide(this._sock, [extendedClipboardFormatText], [this._clipboardText]); - } - } - - } else if (actions === extendedClipboardActionPeek) { - if (this._viewOnly) { - return true; - } - - if (this._clipboardServerCapabilitiesActions[extendedClipboardActionNotify]) { - - if (this._clipboardText != null) { - RFB.messages.extendedClipboardNotify(this._sock, [extendedClipboardFormatText]); - } else { - RFB.messages.extendedClipboardNotify(this._sock, []); - } - } - - } else if (actions === extendedClipboardActionNotify) { - if (this._viewOnly) { - return true; - } - - if (this._clipboardServerCapabilitiesActions[extendedClipboardActionRequest]) { - - if (formats & extendedClipboardFormatText) { - RFB.messages.extendedClipboardRequest(this._sock, [extendedClipboardFormatText]); - } - } - - } else if (actions === extendedClipboardActionProvide) { - if (this._viewOnly) { - return true; - } - - if (!(formats & extendedClipboardFormatText)) { - return true; - } - // Ignore what we had in our clipboard client side. - this._clipboardText = null; - - // FIXME: Should probably verify that this data was actually requested - let zlibStream = this._sock.rQshiftBytes(length - 4); - let streamInflator = new Inflator(); - let textData = null; - - streamInflator.setInput(zlibStream); - for (let i = 0; i <= 15; i++) { - let format = 1 << i; - - if (formats & format) { - - let size = 0x00; - let sizeArray = streamInflator.inflate(4); - - size |= (sizeArray[0] << 24); - size |= (sizeArray[1] << 16); - size |= (sizeArray[2] << 8); - size |= (sizeArray[3]); - let chunk = streamInflator.inflate(size); - - if (format === extendedClipboardFormatText) { - textData = chunk; - } - } - } - streamInflator.setInput(null); - - if (textData !== null) { - let tmpText = ""; - for (let i = 0; i < textData.length; i++) { - tmpText += String.fromCharCode(textData[i]); - } - textData = tmpText; - - textData = decodeUTF8(textData); - if ((textData.length > 0) && "\0" === textData.charAt(textData.length - 1)) { - textData = textData.slice(0, -1); - } - - textData = textData.replaceAll("\r\n", "\n"); - - this.dispatchEvent(new CustomEvent( - "clipboard", - { detail: { text: textData } })); - } - } else { - return this._fail("Unexpected action in extended clipboard message: " + actions); - } - } - return true; - } - - _handleServerFenceMsg() { - if (this._sock.rQwait("ServerFence header", 8, 1)) { return false; } - this._sock.rQskipBytes(3); // Padding - let flags = this._sock.rQshift32(); - let length = this._sock.rQshift8(); - - if (this._sock.rQwait("ServerFence payload", length, 9)) { return false; } - - if (length > 64) { - Log.Warn("Bad payload length (" + length + ") in fence response"); - length = 64; - } - - const payload = this._sock.rQshiftStr(length); - - this._supportsFence = true; - - /* - * Fence flags - * - * (1<<0) - BlockBefore - * (1<<1) - BlockAfter - * (1<<2) - SyncNext - * (1<<31) - Request - */ - - if (!(flags & (1<<31))) { - return this._fail("Unexpected fence response"); - } - - // Filter out unsupported flags - // FIXME: support syncNext - flags &= (1<<0) | (1<<1); - - // BlockBefore and BlockAfter are automatically handled by - // the fact that we process each incoming message - // synchronuosly. - RFB.messages.clientFence(this._sock, flags, payload); - - return true; - } - - _handleXvpMsg() { - if (this._sock.rQwait("XVP version and message", 3, 1)) { return false; } - this._sock.rQskipBytes(1); // Padding - const xvpVer = this._sock.rQshift8(); - const xvpMsg = this._sock.rQshift8(); - - switch (xvpMsg) { - case 0: // XVP_FAIL - Log.Error("XVP Operation Failed"); - break; - case 1: // XVP_INIT - this._rfbXvpVer = xvpVer; - Log.Info("XVP extensions enabled (version " + this._rfbXvpVer + ")"); - this._setCapability("power", true); - break; - default: - this._fail("Illegal server XVP message (msg: " + xvpMsg + ")"); - break; - } - - return true; - } - - _normalMsg() { - let msgType; - if (this._FBU.rects > 0) { - msgType = 0; - } else { - msgType = this._sock.rQshift8(); - } - - let first, ret; - switch (msgType) { - case 0: // FramebufferUpdate - ret = this._framebufferUpdate(); - if (ret && !this._enabledContinuousUpdates) { - RFB.messages.fbUpdateRequest(this._sock, true, 0, 0, - this._fbWidth, this._fbHeight); - } - return ret; - - case 1: // SetColorMapEntries - return this._handleSetColourMapMsg(); - - case 2: // Bell - Log.Debug("Bell"); - this.dispatchEvent(new CustomEvent( - "bell", - { detail: {} })); - return true; - - case 3: // ServerCutText - return this._handleServerCutText(); - - case 150: // EndOfContinuousUpdates - first = !this._supportsContinuousUpdates; - this._supportsContinuousUpdates = true; - this._enabledContinuousUpdates = false; - if (first) { - this._enabledContinuousUpdates = true; - this._updateContinuousUpdates(); - Log.Info("Enabling continuous updates."); - } else { - // FIXME: We need to send a framebufferupdaterequest here - // if we add support for turning off continuous updates - } - return true; - - case 248: // ServerFence - return this._handleServerFenceMsg(); - - case 250: // XVP - return this._handleXvpMsg(); - - default: - this._fail("Unexpected server message (type " + msgType + ")"); - Log.Debug("sock.rQpeekBytes(30): " + this._sock.rQpeekBytes(30)); - return true; - } - } - - _framebufferUpdate() { - if (this._FBU.rects === 0) { - if (this._sock.rQwait("FBU header", 3, 1)) { return false; } - this._sock.rQskipBytes(1); // Padding - this._FBU.rects = this._sock.rQshift16(); - - // Make sure the previous frame is fully rendered first - // to avoid building up an excessive queue - if (this._display.pending()) { - this._flushing = true; - this._display.flush() - .then(() => { - this._flushing = false; - // Resume processing - if (!this._sock.rQwait("message", 1)) { - this._handleMessage(); - } - }); - return false; - } - } - - while (this._FBU.rects > 0) { - if (this._FBU.encoding === null) { - if (this._sock.rQwait("rect header", 12)) { return false; } - /* New FramebufferUpdate */ - - this._FBU.x = this._sock.rQshift16(); - this._FBU.y = this._sock.rQshift16(); - this._FBU.width = this._sock.rQshift16(); - this._FBU.height = this._sock.rQshift16(); - this._FBU.encoding = this._sock.rQshift32(); - /* Encodings are signed */ - this._FBU.encoding >>= 0; - } - - if (!this._handleRect()) { - return false; - } - - this._FBU.rects--; - this._FBU.encoding = null; - } - - this._display.flip(); - - return true; // We finished this FBU - } - - _handleRect() { - switch (this._FBU.encoding) { - case encodings.pseudoEncodingLastRect: - this._FBU.rects = 1; // Will be decreased when we return - return true; - - case encodings.pseudoEncodingVMwareCursor: - return this._handleVMwareCursor(); - - case encodings.pseudoEncodingCursor: - return this._handleCursor(); - - case encodings.pseudoEncodingQEMUExtendedKeyEvent: - this._qemuExtKeyEventSupported = true; - return true; - - case encodings.pseudoEncodingDesktopName: - return this._handleDesktopName(); - - case encodings.pseudoEncodingDesktopSize: - this._resize(this._FBU.width, this._FBU.height); - return true; - - case encodings.pseudoEncodingExtendedDesktopSize: - return this._handleExtendedDesktopSize(); - - case encodings.pseudoEncodingQEMULedEvent: - return this._handleLedEvent(); - - default: - return this._handleDataRect(); - } - } - - _handleVMwareCursor() { - const hotx = this._FBU.x; // hotspot-x - const hoty = this._FBU.y; // hotspot-y - const w = this._FBU.width; - const h = this._FBU.height; - if (this._sock.rQwait("VMware cursor encoding", 1)) { - return false; - } - - const cursorType = this._sock.rQshift8(); - - this._sock.rQshift8(); //Padding - - let rgba; - const bytesPerPixel = 4; - - //Classic cursor - if (cursorType == 0) { - //Used to filter away unimportant bits. - //OR is used for correct conversion in js. - const PIXEL_MASK = 0xffffff00 | 0; - rgba = new Array(w * h * bytesPerPixel); - - if (this._sock.rQwait("VMware cursor classic encoding", - (w * h * bytesPerPixel) * 2, 2)) { - return false; - } - - let andMask = new Array(w * h); - for (let pixel = 0; pixel < (w * h); pixel++) { - andMask[pixel] = this._sock.rQshift32(); - } - - let xorMask = new Array(w * h); - for (let pixel = 0; pixel < (w * h); pixel++) { - xorMask[pixel] = this._sock.rQshift32(); - } - - for (let pixel = 0; pixel < (w * h); pixel++) { - if (andMask[pixel] == 0) { - //Fully opaque pixel - let bgr = xorMask[pixel]; - let r = bgr >> 8 & 0xff; - let g = bgr >> 16 & 0xff; - let b = bgr >> 24 & 0xff; - - rgba[(pixel * bytesPerPixel) ] = r; //r - rgba[(pixel * bytesPerPixel) + 1 ] = g; //g - rgba[(pixel * bytesPerPixel) + 2 ] = b; //b - rgba[(pixel * bytesPerPixel) + 3 ] = 0xff; //a - - } else if ((andMask[pixel] & PIXEL_MASK) == - PIXEL_MASK) { - //Only screen value matters, no mouse colouring - if (xorMask[pixel] == 0) { - //Transparent pixel - rgba[(pixel * bytesPerPixel) ] = 0x00; - rgba[(pixel * bytesPerPixel) + 1 ] = 0x00; - rgba[(pixel * bytesPerPixel) + 2 ] = 0x00; - rgba[(pixel * bytesPerPixel) + 3 ] = 0x00; - - } else if ((xorMask[pixel] & PIXEL_MASK) == - PIXEL_MASK) { - //Inverted pixel, not supported in browsers. - //Fully opaque instead. - rgba[(pixel * bytesPerPixel) ] = 0x00; - rgba[(pixel * bytesPerPixel) + 1 ] = 0x00; - rgba[(pixel * bytesPerPixel) + 2 ] = 0x00; - rgba[(pixel * bytesPerPixel) + 3 ] = 0xff; - - } else { - //Unhandled xorMask - rgba[(pixel * bytesPerPixel) ] = 0x00; - rgba[(pixel * bytesPerPixel) + 1 ] = 0x00; - rgba[(pixel * bytesPerPixel) + 2 ] = 0x00; - rgba[(pixel * bytesPerPixel) + 3 ] = 0xff; - } - - } else { - //Unhandled andMask - rgba[(pixel * bytesPerPixel) ] = 0x00; - rgba[(pixel * bytesPerPixel) + 1 ] = 0x00; - rgba[(pixel * bytesPerPixel) + 2 ] = 0x00; - rgba[(pixel * bytesPerPixel) + 3 ] = 0xff; - } - } - - //Alpha cursor. - } else if (cursorType == 1) { - if (this._sock.rQwait("VMware cursor alpha encoding", - (w * h * 4), 2)) { - return false; - } - - rgba = new Array(w * h * bytesPerPixel); - - for (let pixel = 0; pixel < (w * h); pixel++) { - let data = this._sock.rQshift32(); - - rgba[(pixel * 4) ] = data >> 24 & 0xff; //r - rgba[(pixel * 4) + 1 ] = data >> 16 & 0xff; //g - rgba[(pixel * 4) + 2 ] = data >> 8 & 0xff; //b - rgba[(pixel * 4) + 3 ] = data & 0xff; //a - } - - } else { - Log.Warn("The given cursor type is not supported: " - + cursorType + " given."); - return false; - } - - this._updateCursor(rgba, hotx, hoty, w, h); - - return true; - } - - _handleCursor() { - const hotx = this._FBU.x; // hotspot-x - const hoty = this._FBU.y; // hotspot-y - const w = this._FBU.width; - const h = this._FBU.height; - - const pixelslength = w * h * 4; - const masklength = Math.ceil(w / 8) * h; - - let bytes = pixelslength + masklength; - if (this._sock.rQwait("cursor encoding", bytes)) { - return false; - } - - // Decode from BGRX pixels + bit mask to RGBA - const pixels = this._sock.rQshiftBytes(pixelslength); - const mask = this._sock.rQshiftBytes(masklength); - let rgba = new Uint8Array(w * h * 4); - - let pixIdx = 0; - for (let y = 0; y < h; y++) { - for (let x = 0; x < w; x++) { - let maskIdx = y * Math.ceil(w / 8) + Math.floor(x / 8); - let alpha = (mask[maskIdx] << (x % 8)) & 0x80 ? 255 : 0; - rgba[pixIdx ] = pixels[pixIdx + 2]; - rgba[pixIdx + 1] = pixels[pixIdx + 1]; - rgba[pixIdx + 2] = pixels[pixIdx]; - rgba[pixIdx + 3] = alpha; - pixIdx += 4; - } - } - - this._updateCursor(rgba, hotx, hoty, w, h); - - return true; - } - - _handleDesktopName() { - if (this._sock.rQwait("DesktopName", 4)) { - return false; - } - - let length = this._sock.rQshift32(); - - if (this._sock.rQwait("DesktopName", length, 4)) { - return false; - } - - let name = this._sock.rQshiftStr(length); - name = decodeUTF8(name, true); - - this._setDesktopName(name); - - return true; - } - - _handleLedEvent() { - if (this._sock.rQwait("LED Status", 1)) { - return false; - } - - let data = this._sock.rQshift8(); - // ScrollLock state can be retrieved with data & 1. This is currently not needed. - let numLock = data & 2 ? true : false; - let capsLock = data & 4 ? true : false; - this._remoteCapsLock = capsLock; - this._remoteNumLock = numLock; - - return true; - } - - _handleExtendedDesktopSize() { - if (this._sock.rQwait("ExtendedDesktopSize", 4)) { - return false; - } - - const numberOfScreens = this._sock.rQpeek8(); - - let bytes = 4 + (numberOfScreens * 16); - if (this._sock.rQwait("ExtendedDesktopSize", bytes)) { - return false; - } - - const firstUpdate = !this._supportsSetDesktopSize; - this._supportsSetDesktopSize = true; - - this._sock.rQskipBytes(1); // number-of-screens - this._sock.rQskipBytes(3); // padding - - for (let i = 0; i < numberOfScreens; i += 1) { - // Save the id and flags of the first screen - if (i === 0) { - this._screenID = this._sock.rQshift32(); // id - this._sock.rQskipBytes(2); // x-position - this._sock.rQskipBytes(2); // y-position - this._sock.rQskipBytes(2); // width - this._sock.rQskipBytes(2); // height - this._screenFlags = this._sock.rQshift32(); // flags - } else { - this._sock.rQskipBytes(16); - } - } - - /* - * The x-position indicates the reason for the change: - * - * 0 - server resized on its own - * 1 - this client requested the resize - * 2 - another client requested the resize - */ - - // We need to handle errors when we requested the resize. - if (this._FBU.x === 1 && this._FBU.y !== 0) { - let msg = ""; - // The y-position indicates the status code from the server - switch (this._FBU.y) { - case 1: - msg = "Resize is administratively prohibited"; - break; - case 2: - msg = "Out of resources"; - break; - case 3: - msg = "Invalid screen layout"; - break; - default: - msg = "Unknown reason"; - break; - } - Log.Warn("Server did not accept the resize request: " - + msg); - } else { - this._resize(this._FBU.width, this._FBU.height); - } - - // Normally we only apply the current resize mode after a - // window resize event. However there is no such trigger on the - // initial connect. And we don't know if the server supports - // resizing until we've gotten here. - if (firstUpdate) { - this._requestRemoteResize(); - } - - return true; - } - - _handleDataRect() { - let decoder = this._decoders[this._FBU.encoding]; - if (!decoder) { - this._fail("Unsupported encoding (encoding: " + - this._FBU.encoding + ")"); - return false; - } - - try { - return decoder.decodeRect(this._FBU.x, this._FBU.y, - this._FBU.width, this._FBU.height, - this._sock, this._display, - this._fbDepth); - } catch (err) { - this._fail("Error decoding rect: " + err); - return false; - } - } - - _updateContinuousUpdates() { - if (!this._enabledContinuousUpdates) { return; } - - RFB.messages.enableContinuousUpdates(this._sock, true, 0, 0, - this._fbWidth, this._fbHeight); - } - - _resize(width, height) { - this._fbWidth = width; - this._fbHeight = height; - - this._display.resize(this._fbWidth, this._fbHeight); - - // Adjust the visible viewport based on the new dimensions - this._updateClip(); - this._updateScale(); - - this._updateContinuousUpdates(); - - // Keep this size until browser client size changes - this._saveExpectedClientSize(); - } - - _xvpOp(ver, op) { - if (this._rfbXvpVer < ver) { return; } - Log.Info("Sending XVP operation " + op + " (version " + ver + ")"); - RFB.messages.xvpOp(this._sock, ver, op); - } - - _updateCursor(rgba, hotx, hoty, w, h) { - this._cursorImage = { - rgbaPixels: rgba, - hotx: hotx, hoty: hoty, w: w, h: h, - }; - this._refreshCursor(); - } - - _shouldShowDotCursor() { - // Called when this._cursorImage is updated - if (!this._showDotCursor) { - // User does not want to see the dot, so... - return false; - } - - // The dot should not be shown if the cursor is already visible, - // i.e. contains at least one not-fully-transparent pixel. - // So iterate through all alpha bytes in rgba and stop at the - // first non-zero. - for (let i = 3; i < this._cursorImage.rgbaPixels.length; i += 4) { - if (this._cursorImage.rgbaPixels[i]) { - return false; - } - } - - // At this point, we know that the cursor is fully transparent, and - // the user wants to see the dot instead of this. - return true; - } - - _refreshCursor() { - if (this._rfbConnectionState !== "connecting" && - this._rfbConnectionState !== "connected") { - return; - } - const image = this._shouldShowDotCursor() ? RFB.cursors.dot : this._cursorImage; - this._cursor.change(image.rgbaPixels, - image.hotx, image.hoty, - image.w, image.h - ); - } - - static genDES(password, challenge) { - const passwordChars = password.split('').map(c => c.charCodeAt(0)); - const key = legacyCrypto.importKey( - "raw", passwordChars, { name: "DES-ECB" }, false, ["encrypt"]); - return legacyCrypto.encrypt({ name: "DES-ECB" }, key, challenge); - } -} - -// Class Methods -RFB.messages = { - keyEvent(sock, keysym, down) { - sock.sQpush8(4); // msg-type - sock.sQpush8(down); - - sock.sQpush16(0); - - sock.sQpush32(keysym); - - sock.flush(); - }, - - QEMUExtendedKeyEvent(sock, keysym, down, keycode) { - function getRFBkeycode(xtScanCode) { - const upperByte = (keycode >> 8); - const lowerByte = (keycode & 0x00ff); - if (upperByte === 0xe0 && lowerByte < 0x7f) { - return lowerByte | 0x80; - } - return xtScanCode; - } - - sock.sQpush8(255); // msg-type - sock.sQpush8(0); // sub msg-type - - sock.sQpush16(down); - - sock.sQpush32(keysym); - - const RFBkeycode = getRFBkeycode(keycode); - - sock.sQpush32(RFBkeycode); - - sock.flush(); - }, - - pointerEvent(sock, x, y, mask) { - sock.sQpush8(5); // msg-type - - sock.sQpush8(mask); - - sock.sQpush16(x); - sock.sQpush16(y); - - sock.flush(); - }, - - // Used to build Notify and Request data. - _buildExtendedClipboardFlags(actions, formats) { - let data = new Uint8Array(4); - let formatFlag = 0x00000000; - let actionFlag = 0x00000000; - - for (let i = 0; i < actions.length; i++) { - actionFlag |= actions[i]; - } - - for (let i = 0; i < formats.length; i++) { - formatFlag |= formats[i]; - } - - data[0] = actionFlag >> 24; // Actions - data[1] = 0x00; // Reserved - data[2] = 0x00; // Reserved - data[3] = formatFlag; // Formats - - return data; - }, - - extendedClipboardProvide(sock, formats, inData) { - // Deflate incomming data and their sizes - let deflator = new Deflator(); - let dataToDeflate = []; - - for (let i = 0; i < formats.length; i++) { - // We only support the format Text at this time - if (formats[i] != extendedClipboardFormatText) { - throw new Error("Unsupported extended clipboard format for Provide message."); - } - - // Change lone \r or \n into \r\n as defined in rfbproto - inData[i] = inData[i].replace(/\r\n|\r|\n/gm, "\r\n"); - - // Check if it already has \0 - let text = encodeUTF8(inData[i] + "\0"); - - dataToDeflate.push( (text.length >> 24) & 0xFF, - (text.length >> 16) & 0xFF, - (text.length >> 8) & 0xFF, - (text.length & 0xFF)); - - for (let j = 0; j < text.length; j++) { - dataToDeflate.push(text.charCodeAt(j)); - } - } - - let deflatedData = deflator.deflate(new Uint8Array(dataToDeflate)); - - // Build data to send - let data = new Uint8Array(4 + deflatedData.length); - data.set(RFB.messages._buildExtendedClipboardFlags([extendedClipboardActionProvide], - formats)); - data.set(deflatedData, 4); - - RFB.messages.clientCutText(sock, data, true); - }, - - extendedClipboardNotify(sock, formats) { - let flags = RFB.messages._buildExtendedClipboardFlags([extendedClipboardActionNotify], - formats); - RFB.messages.clientCutText(sock, flags, true); - }, - - extendedClipboardRequest(sock, formats) { - let flags = RFB.messages._buildExtendedClipboardFlags([extendedClipboardActionRequest], - formats); - RFB.messages.clientCutText(sock, flags, true); - }, - - extendedClipboardCaps(sock, actions, formats) { - let formatKeys = Object.keys(formats); - let data = new Uint8Array(4 + (4 * formatKeys.length)); - - formatKeys.map(x => parseInt(x)); - formatKeys.sort((a, b) => a - b); - - data.set(RFB.messages._buildExtendedClipboardFlags(actions, [])); - - let loopOffset = 4; - for (let i = 0; i < formatKeys.length; i++) { - data[loopOffset] = formats[formatKeys[i]] >> 24; - data[loopOffset + 1] = formats[formatKeys[i]] >> 16; - data[loopOffset + 2] = formats[formatKeys[i]] >> 8; - data[loopOffset + 3] = formats[formatKeys[i]] >> 0; - - loopOffset += 4; - data[3] |= (1 << formatKeys[i]); // Update our format flags - } - - RFB.messages.clientCutText(sock, data, true); - }, - - clientCutText(sock, data, extended = false) { - sock.sQpush8(6); // msg-type - - sock.sQpush8(0); // padding - sock.sQpush8(0); // padding - sock.sQpush8(0); // padding - - let length; - if (extended) { - length = toUnsigned32bit(-data.length); - } else { - length = data.length; - } - - sock.sQpush32(length); - sock.sQpushBytes(data); - sock.flush(); - }, - - setDesktopSize(sock, width, height, id, flags) { - sock.sQpush8(251); // msg-type - - sock.sQpush8(0); // padding - - sock.sQpush16(width); - sock.sQpush16(height); - - sock.sQpush8(1); // number-of-screens - - sock.sQpush8(0); // padding - - // screen array - sock.sQpush32(id); - sock.sQpush16(0); // x-position - sock.sQpush16(0); // y-position - sock.sQpush16(width); - sock.sQpush16(height); - sock.sQpush32(flags); - - sock.flush(); - }, - - clientFence(sock, flags, payload) { - sock.sQpush8(248); // msg-type - - sock.sQpush8(0); // padding - sock.sQpush8(0); // padding - sock.sQpush8(0); // padding - - sock.sQpush32(flags); - - sock.sQpush8(payload.length); - sock.sQpushString(payload); - - sock.flush(); - }, - - enableContinuousUpdates(sock, enable, x, y, width, height) { - sock.sQpush8(150); // msg-type - - sock.sQpush8(enable); - - sock.sQpush16(x); - sock.sQpush16(y); - sock.sQpush16(width); - sock.sQpush16(height); - - sock.flush(); - }, - - pixelFormat(sock, depth, trueColor) { - let bpp; - - if (depth > 16) { - bpp = 32; - } else if (depth > 8) { - bpp = 16; - } else { - bpp = 8; - } - - const bits = Math.floor(depth/3); - - sock.sQpush8(0); // msg-type - - sock.sQpush8(0); // padding - sock.sQpush8(0); // padding - sock.sQpush8(0); // padding - - sock.sQpush8(bpp); - sock.sQpush8(depth); - sock.sQpush8(0); // little-endian - sock.sQpush8(trueColor ? 1 : 0); - - sock.sQpush16((1 << bits) - 1); // red-max - sock.sQpush16((1 << bits) - 1); // green-max - sock.sQpush16((1 << bits) - 1); // blue-max - - sock.sQpush8(bits * 0); // red-shift - sock.sQpush8(bits * 1); // green-shift - sock.sQpush8(bits * 2); // blue-shift - - sock.sQpush8(0); // padding - sock.sQpush8(0); // padding - sock.sQpush8(0); // padding - - sock.flush(); - }, - - clientEncodings(sock, encodings) { - sock.sQpush8(2); // msg-type - - sock.sQpush8(0); // padding - - sock.sQpush16(encodings.length); - for (let i = 0; i < encodings.length; i++) { - sock.sQpush32(encodings[i]); - } - - sock.flush(); - }, - - fbUpdateRequest(sock, incremental, x, y, w, h) { - if (typeof(x) === "undefined") { x = 0; } - if (typeof(y) === "undefined") { y = 0; } - - sock.sQpush8(3); // msg-type - - sock.sQpush8(incremental ? 1 : 0); - - sock.sQpush16(x); - sock.sQpush16(y); - sock.sQpush16(w); - sock.sQpush16(h); - - sock.flush(); - }, - - xvpOp(sock, ver, op) { - sock.sQpush8(250); // msg-type - - sock.sQpush8(0); // padding - - sock.sQpush8(ver); - sock.sQpush8(op); - - sock.flush(); - } -}; - -RFB.cursors = { - none: { - rgbaPixels: new Uint8Array(), - w: 0, h: 0, - hotx: 0, hoty: 0, - }, - - dot: { - /* eslint-disable indent */ - rgbaPixels: new Uint8Array([ - 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255, - ]), - /* eslint-enable indent */ - w: 3, h: 3, - hotx: 1, hoty: 1, - } -}; diff --git a/base/app/novnc/core/util/browser.js b/base/app/novnc/core/util/browser.js deleted file mode 100644 index bbc9f5c..0000000 --- a/base/app/novnc/core/util/browser.js +++ /dev/null @@ -1,152 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2019 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - * - * Browser feature support detection - */ - -import * as Log from './logging.js'; - -// Touch detection -export let isTouchDevice = ('ontouchstart' in document.documentElement) || - // requried for Chrome debugger - (document.ontouchstart !== undefined) || - // required for MS Surface - (navigator.maxTouchPoints > 0) || - (navigator.msMaxTouchPoints > 0); -window.addEventListener('touchstart', function onFirstTouch() { - isTouchDevice = true; - window.removeEventListener('touchstart', onFirstTouch, false); -}, false); - - -// The goal is to find a certain physical width, the devicePixelRatio -// brings us a bit closer but is not optimal. -export let dragThreshold = 10 * (window.devicePixelRatio || 1); - -let _supportsCursorURIs = false; - -try { - const target = document.createElement('canvas'); - target.style.cursor = 'url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default'; - - if (target.style.cursor.indexOf("url") === 0) { - Log.Info("Data URI scheme cursor supported"); - _supportsCursorURIs = true; - } else { - Log.Warn("Data URI scheme cursor not supported"); - } -} catch (exc) { - Log.Error("Data URI scheme cursor test exception: " + exc); -} - -export const supportsCursorURIs = _supportsCursorURIs; - -let _hasScrollbarGutter = true; -try { - // Create invisible container - const container = document.createElement('div'); - container.style.visibility = 'hidden'; - container.style.overflow = 'scroll'; // forcing scrollbars - document.body.appendChild(container); - - // Create a div and place it in the container - const child = document.createElement('div'); - container.appendChild(child); - - // Calculate the difference between the container's full width - // and the child's width - the difference is the scrollbars - const scrollbarWidth = (container.offsetWidth - child.offsetWidth); - - // Clean up - container.parentNode.removeChild(container); - - _hasScrollbarGutter = scrollbarWidth != 0; -} catch (exc) { - Log.Error("Scrollbar test exception: " + exc); -} -export const hasScrollbarGutter = _hasScrollbarGutter; - -/* - * The functions for detection of platforms and browsers below are exported - * but the use of these should be minimized as much as possible. - * - * It's better to use feature detection than platform detection. - */ - -/* OS */ - -export function isMac() { - return !!(/mac/i).exec(navigator.platform); -} - -export function isWindows() { - return !!(/win/i).exec(navigator.platform); -} - -export function isIOS() { - return (!!(/ipad/i).exec(navigator.platform) || - !!(/iphone/i).exec(navigator.platform) || - !!(/ipod/i).exec(navigator.platform)); -} - -export function isAndroid() { - /* Android sets navigator.platform to Linux :/ */ - return !!navigator.userAgent.match('Android '); -} - -export function isChromeOS() { - /* ChromeOS sets navigator.platform to Linux :/ */ - return !!navigator.userAgent.match(' CrOS '); -} - -/* Browser */ - -export function isSafari() { - return !!navigator.userAgent.match('Safari/...') && - !navigator.userAgent.match('Chrome/...') && - !navigator.userAgent.match('Chromium/...') && - !navigator.userAgent.match('Epiphany/...'); -} - -export function isFirefox() { - return !!navigator.userAgent.match('Firefox/...') && - !navigator.userAgent.match('Seamonkey/...'); -} - -export function isChrome() { - return !!navigator.userAgent.match('Chrome/...') && - !navigator.userAgent.match('Chromium/...') && - !navigator.userAgent.match('Edg/...') && - !navigator.userAgent.match('OPR/...'); -} - -export function isChromium() { - return !!navigator.userAgent.match('Chromium/...'); -} - -export function isOpera() { - return !!navigator.userAgent.match('OPR/...'); -} - -export function isEdge() { - return !!navigator.userAgent.match('Edg/...'); -} - -/* Engine */ - -export function isGecko() { - return !!navigator.userAgent.match('Gecko/...'); -} - -export function isWebKit() { - return !!navigator.userAgent.match('AppleWebKit/...') && - !navigator.userAgent.match('Chrome/...'); -} - -export function isBlink() { - return !!navigator.userAgent.match('Chrome/...'); -} diff --git a/base/app/novnc/core/util/cursor.js b/base/app/novnc/core/util/cursor.js deleted file mode 100644 index 20e75f1..0000000 --- a/base/app/novnc/core/util/cursor.js +++ /dev/null @@ -1,249 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2019 The noVNC Authors - * Licensed under MPL 2.0 or any later version (see LICENSE.txt) - */ - -import { supportsCursorURIs, isTouchDevice } from './browser.js'; - -const useFallback = !supportsCursorURIs || isTouchDevice; - -export default class Cursor { - constructor() { - this._target = null; - - this._canvas = document.createElement('canvas'); - - if (useFallback) { - this._canvas.style.position = 'fixed'; - this._canvas.style.zIndex = '65535'; - this._canvas.style.pointerEvents = 'none'; - // Safari on iOS can select the cursor image - // https://bugs.webkit.org/show_bug.cgi?id=249223 - this._canvas.style.userSelect = 'none'; - this._canvas.style.WebkitUserSelect = 'none'; - // Can't use "display" because of Firefox bug #1445997 - this._canvas.style.visibility = 'hidden'; - } - - this._position = { x: 0, y: 0 }; - this._hotSpot = { x: 0, y: 0 }; - - this._eventHandlers = { - 'mouseover': this._handleMouseOver.bind(this), - 'mouseleave': this._handleMouseLeave.bind(this), - 'mousemove': this._handleMouseMove.bind(this), - 'mouseup': this._handleMouseUp.bind(this), - }; - } - - attach(target) { - if (this._target) { - this.detach(); - } - - this._target = target; - - if (useFallback) { - document.body.appendChild(this._canvas); - - const options = { capture: true, passive: true }; - this._target.addEventListener('mouseover', this._eventHandlers.mouseover, options); - this._target.addEventListener('mouseleave', this._eventHandlers.mouseleave, options); - this._target.addEventListener('mousemove', this._eventHandlers.mousemove, options); - this._target.addEventListener('mouseup', this._eventHandlers.mouseup, options); - } - - this.clear(); - } - - detach() { - if (!this._target) { - return; - } - - if (useFallback) { - const options = { capture: true, passive: true }; - this._target.removeEventListener('mouseover', this._eventHandlers.mouseover, options); - this._target.removeEventListener('mouseleave', this._eventHandlers.mouseleave, options); - this._target.removeEventListener('mousemove', this._eventHandlers.mousemove, options); - this._target.removeEventListener('mouseup', this._eventHandlers.mouseup, options); - - if (document.contains(this._canvas)) { - document.body.removeChild(this._canvas); - } - } - - this._target = null; - } - - change(rgba, hotx, hoty, w, h) { - if ((w === 0) || (h === 0)) { - this.clear(); - return; - } - - this._position.x = this._position.x + this._hotSpot.x - hotx; - this._position.y = this._position.y + this._hotSpot.y - hoty; - this._hotSpot.x = hotx; - this._hotSpot.y = hoty; - - let ctx = this._canvas.getContext('2d'); - - this._canvas.width = w; - this._canvas.height = h; - - let img = new ImageData(new Uint8ClampedArray(rgba), w, h); - ctx.clearRect(0, 0, w, h); - ctx.putImageData(img, 0, 0); - - if (useFallback) { - this._updatePosition(); - } else { - let url = this._canvas.toDataURL(); - this._target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default'; - } - } - - clear() { - this._target.style.cursor = 'none'; - this._canvas.width = 0; - this._canvas.height = 0; - this._position.x = this._position.x + this._hotSpot.x; - this._position.y = this._position.y + this._hotSpot.y; - this._hotSpot.x = 0; - this._hotSpot.y = 0; - } - - // Mouse events might be emulated, this allows - // moving the cursor in such cases - move(clientX, clientY) { - if (!useFallback) { - return; - } - // clientX/clientY are relative the _visual viewport_, - // but our position is relative the _layout viewport_, - // so try to compensate when we can - if (window.visualViewport) { - this._position.x = clientX + window.visualViewport.offsetLeft; - this._position.y = clientY + window.visualViewport.offsetTop; - } else { - this._position.x = clientX; - this._position.y = clientY; - } - this._updatePosition(); - let target = document.elementFromPoint(clientX, clientY); - this._updateVisibility(target); - } - - _handleMouseOver(event) { - // This event could be because we're entering the target, or - // moving around amongst its sub elements. Let the move handler - // sort things out. - this._handleMouseMove(event); - } - - _handleMouseLeave(event) { - // Check if we should show the cursor on the element we are leaving to - this._updateVisibility(event.relatedTarget); - } - - _handleMouseMove(event) { - this._updateVisibility(event.target); - - this._position.x = event.clientX - this._hotSpot.x; - this._position.y = event.clientY - this._hotSpot.y; - - this._updatePosition(); - } - - _handleMouseUp(event) { - // We might get this event because of a drag operation that - // moved outside of the target. Check what's under the cursor - // now and adjust visibility based on that. - let target = document.elementFromPoint(event.clientX, event.clientY); - this._updateVisibility(target); - - // Captures end with a mouseup but we can't know the event order of - // mouseup vs releaseCapture. - // - // In the cases when releaseCapture comes first, the code above is - // enough. - // - // In the cases when the mouseup comes first, we need wait for the - // browser to flush all events and then check again if the cursor - // should be visible. - if (this._captureIsActive()) { - window.setTimeout(() => { - // We might have detached at this point - if (!this._target) { - return; - } - // Refresh the target from elementFromPoint since queued events - // might have altered the DOM - target = document.elementFromPoint(event.clientX, - event.clientY); - this._updateVisibility(target); - }, 0); - } - } - - _showCursor() { - if (this._canvas.style.visibility === 'hidden') { - this._canvas.style.visibility = ''; - } - } - - _hideCursor() { - if (this._canvas.style.visibility !== 'hidden') { - this._canvas.style.visibility = 'hidden'; - } - } - - // Should we currently display the cursor? - // (i.e. are we over the target, or a child of the target without a - // different cursor set) - _shouldShowCursor(target) { - if (!target) { - return false; - } - // Easy case - if (target === this._target) { - return true; - } - // Other part of the DOM? - if (!this._target.contains(target)) { - return false; - } - // Has the child its own cursor? - // FIXME: How can we tell that a sub element has an - // explicit "cursor: none;"? - if (window.getComputedStyle(target).cursor !== 'none') { - return false; - } - return true; - } - - _updateVisibility(target) { - // When the cursor target has capture we want to show the cursor. - // So, if a capture is active - look at the captured element instead. - if (this._captureIsActive()) { - target = document.captureElement; - } - if (this._shouldShowCursor(target)) { - this._showCursor(); - } else { - this._hideCursor(); - } - } - - _updatePosition() { - this._canvas.style.left = this._position.x + "px"; - this._canvas.style.top = this._position.y + "px"; - } - - _captureIsActive() { - return document.captureElement && - document.documentElement.contains(document.captureElement); - } -} diff --git a/base/app/novnc/core/util/element.js b/base/app/novnc/core/util/element.js deleted file mode 100644 index 466a745..0000000 --- a/base/app/novnc/core/util/element.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2020 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - */ - -/* - * HTML element utility functions - */ - -export function clientToElement(x, y, elem) { - const bounds = elem.getBoundingClientRect(); - let pos = { x: 0, y: 0 }; - // Clip to target bounds - if (x < bounds.left) { - pos.x = 0; - } else if (x >= bounds.right) { - pos.x = bounds.width - 1; - } else { - pos.x = x - bounds.left; - } - if (y < bounds.top) { - pos.y = 0; - } else if (y >= bounds.bottom) { - pos.y = bounds.height - 1; - } else { - pos.y = y - bounds.top; - } - return pos; -} diff --git a/base/app/novnc/core/util/events.js b/base/app/novnc/core/util/events.js deleted file mode 100644 index eb09fe1..0000000 --- a/base/app/novnc/core/util/events.js +++ /dev/null @@ -1,138 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2018 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - */ - -/* - * Cross-browser event and position routines - */ - -export function getPointerEvent(e) { - return e.changedTouches ? e.changedTouches[0] : e.touches ? e.touches[0] : e; -} - -export function stopEvent(e) { - e.stopPropagation(); - e.preventDefault(); -} - -// Emulate Element.setCapture() when not supported -let _captureRecursion = false; -let _elementForUnflushedEvents = null; -document.captureElement = null; -function _captureProxy(e) { - // Recursion protection as we'll see our own event - if (_captureRecursion) return; - - // Clone the event as we cannot dispatch an already dispatched event - const newEv = new e.constructor(e.type, e); - - _captureRecursion = true; - if (document.captureElement) { - document.captureElement.dispatchEvent(newEv); - } else { - _elementForUnflushedEvents.dispatchEvent(newEv); - } - _captureRecursion = false; - - // Avoid double events - e.stopPropagation(); - - // Respect the wishes of the redirected event handlers - if (newEv.defaultPrevented) { - e.preventDefault(); - } - - // Implicitly release the capture on button release - if (e.type === "mouseup") { - releaseCapture(); - } -} - -// Follow cursor style of target element -function _capturedElemChanged() { - const proxyElem = document.getElementById("noVNC_mouse_capture_elem"); - proxyElem.style.cursor = window.getComputedStyle(document.captureElement).cursor; -} - -const _captureObserver = new MutationObserver(_capturedElemChanged); - -export function setCapture(target) { - if (target.setCapture) { - - target.setCapture(); - document.captureElement = target; - } else { - // Release any existing capture in case this method is - // called multiple times without coordination - releaseCapture(); - - let proxyElem = document.getElementById("noVNC_mouse_capture_elem"); - - if (proxyElem === null) { - proxyElem = document.createElement("div"); - proxyElem.id = "noVNC_mouse_capture_elem"; - proxyElem.style.position = "fixed"; - proxyElem.style.top = "0px"; - proxyElem.style.left = "0px"; - proxyElem.style.width = "100%"; - proxyElem.style.height = "100%"; - proxyElem.style.zIndex = 10000; - proxyElem.style.display = "none"; - document.body.appendChild(proxyElem); - - // This is to make sure callers don't get confused by having - // our blocking element as the target - proxyElem.addEventListener('contextmenu', _captureProxy); - - proxyElem.addEventListener('mousemove', _captureProxy); - proxyElem.addEventListener('mouseup', _captureProxy); - } - - document.captureElement = target; - - // Track cursor and get initial cursor - _captureObserver.observe(target, {attributes: true}); - _capturedElemChanged(); - - proxyElem.style.display = ""; - - // We listen to events on window in order to keep tracking if it - // happens to leave the viewport - window.addEventListener('mousemove', _captureProxy); - window.addEventListener('mouseup', _captureProxy); - } -} - -export function releaseCapture() { - if (document.releaseCapture) { - - document.releaseCapture(); - document.captureElement = null; - - } else { - if (!document.captureElement) { - return; - } - - // There might be events already queued. The event proxy needs - // access to the captured element for these queued events. - // E.g. contextmenu (right-click) in Microsoft Edge - // - // Before removing the capturedElem pointer we save it to a - // temporary variable that the unflushed events can use. - _elementForUnflushedEvents = document.captureElement; - document.captureElement = null; - - _captureObserver.disconnect(); - - const proxyElem = document.getElementById("noVNC_mouse_capture_elem"); - proxyElem.style.display = "none"; - - window.removeEventListener('mousemove', _captureProxy); - window.removeEventListener('mouseup', _captureProxy); - } -} diff --git a/base/app/novnc/core/util/eventtarget.js b/base/app/novnc/core/util/eventtarget.js deleted file mode 100644 index a21aa54..0000000 --- a/base/app/novnc/core/util/eventtarget.js +++ /dev/null @@ -1,35 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2019 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - */ - -export default class EventTargetMixin { - constructor() { - this._listeners = new Map(); - } - - addEventListener(type, callback) { - if (!this._listeners.has(type)) { - this._listeners.set(type, new Set()); - } - this._listeners.get(type).add(callback); - } - - removeEventListener(type, callback) { - if (this._listeners.has(type)) { - this._listeners.get(type).delete(callback); - } - } - - dispatchEvent(event) { - if (!this._listeners.has(event.type)) { - return true; - } - this._listeners.get(event.type) - .forEach(callback => callback.call(this, event)); - return !event.defaultPrevented; - } -} diff --git a/base/app/novnc/core/util/int.js b/base/app/novnc/core/util/int.js deleted file mode 100644 index 001f40f..0000000 --- a/base/app/novnc/core/util/int.js +++ /dev/null @@ -1,15 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2020 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - */ - -export function toUnsigned32bit(toConvert) { - return toConvert >>> 0; -} - -export function toSigned32bit(toConvert) { - return toConvert | 0; -} diff --git a/base/app/novnc/core/util/logging.js b/base/app/novnc/core/util/logging.js deleted file mode 100644 index fe449e9..0000000 --- a/base/app/novnc/core/util/logging.js +++ /dev/null @@ -1,56 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2019 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - */ - -/* - * Logging/debug routines - */ - -let _logLevel = 'warn'; - -let Debug = () => {}; -let Info = () => {}; -let Warn = () => {}; -let Error = () => {}; - -export function initLogging(level) { - if (typeof level === 'undefined') { - level = _logLevel; - } else { - _logLevel = level; - } - - Debug = Info = Warn = Error = () => {}; - - if (typeof window.console !== "undefined") { - /* eslint-disable no-console, no-fallthrough */ - switch (level) { - case 'debug': - Debug = console.debug.bind(window.console); - case 'info': - Info = console.info.bind(window.console); - case 'warn': - Warn = console.warn.bind(window.console); - case 'error': - Error = console.error.bind(window.console); - case 'none': - break; - default: - throw new window.Error("invalid logging type '" + level + "'"); - } - /* eslint-enable no-console, no-fallthrough */ - } -} - -export function getLogging() { - return _logLevel; -} - -export { Debug, Info, Warn, Error }; - -// Initialize logging level -initLogging(); diff --git a/base/app/novnc/core/util/strings.js b/base/app/novnc/core/util/strings.js deleted file mode 100644 index 3dd4b29..0000000 --- a/base/app/novnc/core/util/strings.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2019 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - */ - -// Decode from UTF-8 -export function decodeUTF8(utf8string, allowLatin1=false) { - try { - return decodeURIComponent(escape(utf8string)); - } catch (e) { - if (e instanceof URIError) { - if (allowLatin1) { - // If we allow Latin1 we can ignore any decoding fails - // and in these cases return the original string - return utf8string; - } - } - throw e; - } -} - -// Encode to UTF-8 -export function encodeUTF8(DOMString) { - return unescape(encodeURIComponent(DOMString)); -} diff --git a/base/app/novnc/core/websock.js b/base/app/novnc/core/websock.js deleted file mode 100644 index 21327c3..0000000 --- a/base/app/novnc/core/websock.js +++ /dev/null @@ -1,365 +0,0 @@ -/* - * Websock: high-performance buffering wrapper - * Copyright (C) 2019 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * Websock is similar to the standard WebSocket / RTCDataChannel object - * but with extra buffer handling. - * - * Websock has built-in receive queue buffering; the message event - * does not contain actual data but is simply a notification that - * there is new data available. Several rQ* methods are available to - * read binary data off of the receive queue. - */ - -import * as Log from './util/logging.js'; - -// this has performance issues in some versions Chromium, and -// doesn't gain a tremendous amount of performance increase in Firefox -// at the moment. It may be valuable to turn it on in the future. -const MAX_RQ_GROW_SIZE = 40 * 1024 * 1024; // 40 MiB - -// Constants pulled from RTCDataChannelState enum -// https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel/readyState#RTCDataChannelState_enum -const DataChannel = { - CONNECTING: "connecting", - OPEN: "open", - CLOSING: "closing", - CLOSED: "closed" -}; - -const ReadyStates = { - CONNECTING: [WebSocket.CONNECTING, DataChannel.CONNECTING], - OPEN: [WebSocket.OPEN, DataChannel.OPEN], - CLOSING: [WebSocket.CLOSING, DataChannel.CLOSING], - CLOSED: [WebSocket.CLOSED, DataChannel.CLOSED], -}; - -// Properties a raw channel must have, WebSocket and RTCDataChannel are two examples -const rawChannelProps = [ - "send", - "close", - "binaryType", - "onerror", - "onmessage", - "onopen", - "protocol", - "readyState", -]; - -export default class Websock { - constructor() { - this._websocket = null; // WebSocket or RTCDataChannel object - - this._rQi = 0; // Receive queue index - this._rQlen = 0; // Next write position in the receive queue - this._rQbufferSize = 1024 * 1024 * 4; // Receive queue buffer size (4 MiB) - // called in init: this._rQ = new Uint8Array(this._rQbufferSize); - this._rQ = null; // Receive queue - - this._sQbufferSize = 1024 * 10; // 10 KiB - // called in init: this._sQ = new Uint8Array(this._sQbufferSize); - this._sQlen = 0; - this._sQ = null; // Send queue - - this._eventHandlers = { - message: () => {}, - open: () => {}, - close: () => {}, - error: () => {} - }; - } - - // Getters and Setters - - get readyState() { - let subState; - - if (this._websocket === null) { - return "unused"; - } - - subState = this._websocket.readyState; - - if (ReadyStates.CONNECTING.includes(subState)) { - return "connecting"; - } else if (ReadyStates.OPEN.includes(subState)) { - return "open"; - } else if (ReadyStates.CLOSING.includes(subState)) { - return "closing"; - } else if (ReadyStates.CLOSED.includes(subState)) { - return "closed"; - } - - return "unknown"; - } - - // Receive Queue - rQpeek8() { - return this._rQ[this._rQi]; - } - - rQskipBytes(bytes) { - this._rQi += bytes; - } - - rQshift8() { - return this._rQshift(1); - } - - rQshift16() { - return this._rQshift(2); - } - - rQshift32() { - return this._rQshift(4); - } - - // TODO(directxman12): test performance with these vs a DataView - _rQshift(bytes) { - let res = 0; - for (let byte = bytes - 1; byte >= 0; byte--) { - res += this._rQ[this._rQi++] << (byte * 8); - } - return res >>> 0; - } - - rQshiftStr(len) { - let str = ""; - // Handle large arrays in steps to avoid long strings on the stack - for (let i = 0; i < len; i += 4096) { - let part = this.rQshiftBytes(Math.min(4096, len - i), false); - str += String.fromCharCode.apply(null, part); - } - return str; - } - - rQshiftBytes(len, copy=true) { - this._rQi += len; - if (copy) { - return this._rQ.slice(this._rQi - len, this._rQi); - } else { - return this._rQ.subarray(this._rQi - len, this._rQi); - } - } - - rQshiftTo(target, len) { - // TODO: make this just use set with views when using a ArrayBuffer to store the rQ - target.set(new Uint8Array(this._rQ.buffer, this._rQi, len)); - this._rQi += len; - } - - rQpeekBytes(len, copy=true) { - if (copy) { - return this._rQ.slice(this._rQi, this._rQi + len); - } else { - return this._rQ.subarray(this._rQi, this._rQi + len); - } - } - - // Check to see if we must wait for 'num' bytes (default to FBU.bytes) - // to be available in the receive queue. Return true if we need to - // wait (and possibly print a debug message), otherwise false. - rQwait(msg, num, goback) { - if (this._rQlen - this._rQi < num) { - if (goback) { - if (this._rQi < goback) { - throw new Error("rQwait cannot backup " + goback + " bytes"); - } - this._rQi -= goback; - } - return true; // true means need more data - } - return false; - } - - // Send Queue - - sQpush8(num) { - this._sQensureSpace(1); - this._sQ[this._sQlen++] = num; - } - - sQpush16(num) { - this._sQensureSpace(2); - this._sQ[this._sQlen++] = (num >> 8) & 0xff; - this._sQ[this._sQlen++] = (num >> 0) & 0xff; - } - - sQpush32(num) { - this._sQensureSpace(4); - this._sQ[this._sQlen++] = (num >> 24) & 0xff; - this._sQ[this._sQlen++] = (num >> 16) & 0xff; - this._sQ[this._sQlen++] = (num >> 8) & 0xff; - this._sQ[this._sQlen++] = (num >> 0) & 0xff; - } - - sQpushString(str) { - let bytes = str.split('').map(chr => chr.charCodeAt(0)); - this.sQpushBytes(new Uint8Array(bytes)); - } - - sQpushBytes(bytes) { - for (let offset = 0;offset < bytes.length;) { - this._sQensureSpace(1); - - let chunkSize = this._sQbufferSize - this._sQlen; - if (chunkSize > bytes.length - offset) { - chunkSize = bytes.length - offset; - } - - this._sQ.set(bytes.subarray(offset, chunkSize), this._sQlen); - this._sQlen += chunkSize; - offset += chunkSize; - } - } - - flush() { - if (this._sQlen > 0 && this.readyState === 'open') { - this._websocket.send(new Uint8Array(this._sQ.buffer, 0, this._sQlen)); - this._sQlen = 0; - } - } - - _sQensureSpace(bytes) { - if (this._sQbufferSize - this._sQlen < bytes) { - this.flush(); - } - } - - // Event Handlers - off(evt) { - this._eventHandlers[evt] = () => {}; - } - - on(evt, handler) { - this._eventHandlers[evt] = handler; - } - - _allocateBuffers() { - this._rQ = new Uint8Array(this._rQbufferSize); - this._sQ = new Uint8Array(this._sQbufferSize); - } - - init() { - this._allocateBuffers(); - this._rQi = 0; - this._websocket = null; - } - - open(uri, protocols) { - this.attach(new WebSocket(uri, protocols)); - } - - attach(rawChannel) { - this.init(); - - // Must get object and class methods to be compatible with the tests. - const channelProps = [...Object.keys(rawChannel), ...Object.getOwnPropertyNames(Object.getPrototypeOf(rawChannel))]; - for (let i = 0; i < rawChannelProps.length; i++) { - const prop = rawChannelProps[i]; - if (channelProps.indexOf(prop) < 0) { - throw new Error('Raw channel missing property: ' + prop); - } - } - - this._websocket = rawChannel; - this._websocket.binaryType = "arraybuffer"; - this._websocket.onmessage = this._recvMessage.bind(this); - - this._websocket.onopen = () => { - Log.Debug('>> WebSock.onopen'); - if (this._websocket.protocol) { - Log.Info("Server choose sub-protocol: " + this._websocket.protocol); - } - - this._eventHandlers.open(); - Log.Debug("<< WebSock.onopen"); - }; - - this._websocket.onclose = (e) => { - Log.Debug(">> WebSock.onclose"); - this._eventHandlers.close(e); - Log.Debug("<< WebSock.onclose"); - }; - - this._websocket.onerror = (e) => { - Log.Debug(">> WebSock.onerror: " + e); - this._eventHandlers.error(e); - Log.Debug("<< WebSock.onerror: " + e); - }; - } - - close() { - if (this._websocket) { - if (this.readyState === 'connecting' || - this.readyState === 'open') { - Log.Info("Closing WebSocket connection"); - this._websocket.close(); - } - - this._websocket.onmessage = () => {}; - } - } - - // private methods - - // We want to move all the unread data to the start of the queue, - // e.g. compacting. - // The function also expands the receive que if needed, and for - // performance reasons we combine these two actions to avoid - // unnecessary copying. - _expandCompactRQ(minFit) { - // if we're using less than 1/8th of the buffer even with the incoming bytes, compact in place - // instead of resizing - const requiredBufferSize = (this._rQlen - this._rQi + minFit) * 8; - const resizeNeeded = this._rQbufferSize < requiredBufferSize; - - if (resizeNeeded) { - // Make sure we always *at least* double the buffer size, and have at least space for 8x - // the current amount of data - this._rQbufferSize = Math.max(this._rQbufferSize * 2, requiredBufferSize); - } - - // we don't want to grow unboundedly - if (this._rQbufferSize > MAX_RQ_GROW_SIZE) { - this._rQbufferSize = MAX_RQ_GROW_SIZE; - if (this._rQbufferSize - (this._rQlen - this._rQi) < minFit) { - throw new Error("Receive Queue buffer exceeded " + MAX_RQ_GROW_SIZE + " bytes, and the new message could not fit"); - } - } - - if (resizeNeeded) { - const oldRQbuffer = this._rQ.buffer; - this._rQ = new Uint8Array(this._rQbufferSize); - this._rQ.set(new Uint8Array(oldRQbuffer, this._rQi, this._rQlen - this._rQi)); - } else { - this._rQ.copyWithin(0, this._rQi, this._rQlen); - } - - this._rQlen = this._rQlen - this._rQi; - this._rQi = 0; - } - - // push arraybuffer values onto the end of the receive que - _recvMessage(e) { - if (this._rQlen == this._rQi) { - // All data has now been processed, this means we - // can reset the receive queue. - this._rQlen = 0; - this._rQi = 0; - } - const u8 = new Uint8Array(e.data); - if (u8.length > this._rQbufferSize - this._rQlen) { - this._expandCompactRQ(u8.length); - } - this._rQ.set(u8, this._rQlen); - this._rQlen += u8.length; - - if (this._rQlen - this._rQi > 0) { - this._eventHandlers.message(); - } else { - Log.Debug("Ignoring empty message"); - } - } -} diff --git a/base/app/novnc/docs/API-internal.md b/base/app/novnc/docs/API-internal.md deleted file mode 100644 index c41e0f3..0000000 --- a/base/app/novnc/docs/API-internal.md +++ /dev/null @@ -1,83 +0,0 @@ -# 1. Internal Modules - -The noVNC client is composed of several internal modules that handle -rendering, input, networking, etc. Each of the modules is designed to -be cross-browser and independent from each other. - -Note however that the API of these modules is not guaranteed to be -stable, and this documentation is not maintained as well as the -official external API. - - -## 1.1 Module List - -* __Keyboard__ (core/input/keyboard.js): Keyboard input event handler with -non-US keyboard support. Translates keyDown and keyUp events to X11 -keysym values. - -* __Display__ (core/display.js): Efficient 2D rendering abstraction -layered on the HTML5 canvas element. - -* __Websock__ (core/websock.js): Websock client from websockify -with transparent binary data support. -[Websock API](https://github.com/novnc/websockify-js/wiki/websock.js) wiki page. - - -## 1.2 Callbacks - -For the Mouse, Keyboard and Display objects the callback functions are -assigned to configuration attributes, just as for the RFB object. The -WebSock module has a method named 'on' that takes two parameters: the -callback event name, and the callback function. - -## 2. Modules - -## 2.1 Keyboard Module - -### 2.1.1 Configuration Attributes - -None - -### 2.1.2 Methods - -| name | parameters | description -| ------ | ---------- | ------------ -| grab | () | Begin capturing keyboard events -| ungrab | () | Stop capturing keyboard events - -### 2.1.3 Callbacks - -| name | parameters | description -| ---------- | -------------------- | ------------ -| onkeypress | (keysym, code, down) | Handler for key press/release - - -## 2.2 Display Module - -### 2.2.1 Configuration Attributes - -| name | type | mode | default | description -| ------------ | ----- | ---- | ------- | ------------ -| scale | float | RW | 1.0 | Display area scale factor 0.0 - 1.0 -| clipViewport | bool | RW | false | Use viewport clipping -| width | int | RO | | Display area width -| height | int | RO | | Display area height - -### 2.2.2 Methods - -| name | parameters | description -| ------------------ | ------------------------------------------------------- | ------------ -| viewportChangePos | (deltaX, deltaY) | Move the viewport relative to the current location -| viewportChangeSize | (width, height) | Change size of the viewport -| absX | (x) | Return X relative to the remote display -| absY | (y) | Return Y relative to the remote display -| resize | (width, height) | Set width and height -| flip | (from_queue) | Update the visible canvas with the contents of the rendering canvas -| pending | () | Check if there are waiting items in the render queue -| flush | () | Resume processing the render queue unless it's empty -| fillRect | (x, y, width, height, color, from_queue) | Draw a filled in rectangle -| copyImage | (old_x, old_y, new_x, new_y, width, height, from_queue) | Copy a rectangular area -| imageRect | (x, y, width, height, mime, arr) | Draw a rectangle with an image -| blitImage | (x, y, width, height, arr, offset, from_queue) | Blit pixels (of R,G,B,A) to the display -| drawImage | (img, x, y) | Draw image and track damage -| autoscale | (containerWidth, containerHeight) | Scale the display diff --git a/base/app/novnc/docs/API.md b/base/app/novnc/docs/API.md deleted file mode 100644 index eb3ec33..0000000 --- a/base/app/novnc/docs/API.md +++ /dev/null @@ -1,546 +0,0 @@ -# noVNC API - -The interface of the noVNC client consists of a single RFB object that -is instantiated once per connection. - -## RFB - -The `RFB` object represents a single connection to a VNC server. It -communicates using a WebSocket that must provide a standard RFB -protocol stream. - -### Constructor - -[`RFB()`](#rfb-1) - - Creates and returns a new `RFB` object. - -### Properties - -`background` - - Is a valid CSS [background][mdn-bg] style value indicating which - background style should be applied to the element containing the - remote session screen. The default value is `rgb(40, 40, 40)` (solid - gray color). - -[mdn-bg]: https://developer.mozilla.org/en-US/docs/Web/CSS/background - -`capabilities` *Read only* - - Is an `Object` indicating which optional extensions are available - on the server. Some methods may only be called if the corresponding - capability is set. The following capabilities are defined: - - | name | type | description - | -------- | --------- | ----------- - | `power` | `boolean` | Machine power control is available - -`clippingViewport` *Read only* - - Is a `boolean` indicating if the remote session is currently being - clipped to its container. Only relevant if `clipViewport` is - enabled. - -`clipViewport` - - Is a `boolean` indicating if the remote session should be clipped - to its container. When disabled scrollbars will be shown to handle - the resulting overflow. Disabled by default. - -`compressionLevel` - - Is an `int` in range `[0-9]` controlling the desired compression - level. Value `0` means no compression. Level 1 uses a minimum of CPU - resources and achieves weak compression ratios, while level 9 offers - best compression but is slow in terms of CPU consumption on the server - side. Use high levels with very slow network connections. - Default value is `2`. - -`dragViewport` - - Is a `boolean` indicating if mouse events should control the - relative position of a clipped remote session. Only relevant if - `clipViewport` is enabled. Disabled by default. - -`focusOnClick` - - Is a `boolean` indicating if keyboard focus should automatically be - moved to the remote session when a `mousedown` or `touchstart` - event is received. Enabled by default. - -`qualityLevel` - - Is an `int` in range `[0-9]` controlling the desired JPEG quality. - Value `0` implies low quality and `9` implies high quality. - Default value is `6`. - -`resizeSession` - - Is a `boolean` indicating if a request to resize the remote session - should be sent whenever the container changes dimensions. Disabled - by default. - -`scaleViewport` - - Is a `boolean` indicating if the remote session should be scaled - locally so it fits its container. When disabled it will be centered - if the remote session is smaller than its container, or handled - according to `clipViewport` if it is larger. Disabled by default. - -`showDotCursor` - - Is a `boolean` indicating whether a dot cursor should be shown - instead of a zero-sized or fully-transparent cursor if the server - sets such invisible cursor. Disabled by default. - -`viewOnly` - - Is a `boolean` indicating if any events (e.g. key presses or mouse - movement) should be prevented from being sent to the server. - Disabled by default. - -### Events - -[`bell`](#bell) - - The `bell` event is fired when a audible bell request is received - from the server. - -[`capabilities`](#capabilities) - - The `capabilities` event is fired when `RFB.capabilities` is - updated. - -[`clipboard`](#clipboard) - - The `clipboard` event is fired when clipboard data is received from - the server. - -[`clippingviewport`](#clippingviewport) - - The `clippingviewport` event is fired when `RFB.clippingViewport` is - updated. - -[`connect`](#connect) - - The `connect` event is fired when the `RFB` object has completed - the connection and handshaking with the server. - -[`credentialsrequired`](#credentialsrequired) - - The `credentialsrequired` event is fired when more credentials must - be given to continue. - -[`desktopname`](#desktopname) - - The `desktopname` event is fired when the remote desktop name - changes. - -[`disconnect`](#disconnect) - - The `disconnect` event is fired when the `RFB` object disconnects. - -[`securityfailure`](#securityfailure) - - The `securityfailure` event is fired when the security negotiation - with the server fails. - -[`serververification`](#serververification) - - The `serververification` event is fired when the server identity - must be confirmed by the user. - -### Methods - -[`RFB.approveServer()`](#rfbapproveserver) - - Proceed connecting to the server. Should be called after the - [`serververification`](#serververification) event has fired and the - user has verified the identity of the server. - -[`RFB.blur()`](#rfbblur) - - Remove keyboard focus from the remote session. - -[`RFB.clipboardPasteFrom()`](#rfbclipboardpastefrom) - - Send clipboard contents to server. - -[`RFB.disconnect()`](#rfbdisconnect) - - Disconnect from the server. - -[`RFB.focus()`](#rfbfocus) - - Move keyboard focus to the remote session. - -[`RFB.getImageData()`](#rfbgetimagedata) - - Return the current content of the screen as an ImageData array. - -[`RFB.machineReboot()`](#rfbmachinereboot) - - Request a reboot of the remote machine. - -[`RFB.machineReset()`](#rfbmachinereset) - - Request a reset of the remote machine. - -[`RFB.machineShutdown()`](#rfbmachineshutdown) - - Request a shutdown of the remote machine. - -[`RFB.sendCredentials()`](#rfbsendcredentials) - - Send credentials to server. Should be called after the - [`credentialsrequired`](#credentialsrequired) event has fired. - -[`RFB.sendCtrlAltDel()`](#rfbsendctrlaltdel) - - Send Ctrl-Alt-Del key sequence. - -[`RFB.sendKey()`](#rfbsendkey) - - Send a key event. - -[`RFB.toBlob()`](#rfbtoblob) - - Return the current content of the screen as Blob encoded image file. - -[`RFB.toDataURL()`](#rfbtodataurl) - - Return the current content of the screen as data-url encoded image file. - -### Details - -#### RFB() - -The `RFB()` constructor returns a new `RFB` object and initiates a new -connection to a specified VNC server. - -##### Syntax - -```js -new RFB(target, urlOrChannel); -new RFB(target, urlOrChannel, options); -``` - -###### Parameters - -**`target`** - - A block [`HTMLElement`][mdn-elem] that specifies where the `RFB` - object should attach itself. The existing contents of the - `HTMLElement` will be untouched, but new elements will be added - during the lifetime of the `RFB` object. - -[mdn-elem]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement - -**`urlOrChannel`** - - A `DOMString` specifying the VNC server to connect to. This must be - a valid WebSocket URL. This can also be a `WebSocket` or `RTCDataChannel`. - -**`options`** *Optional* - - An `Object` specifying extra details about how the connection - should be made. - - Possible options: - - `shared` - - A `boolean` indicating if the remote server should be shared or - if any other connected clients should be disconnected. Enabled - by default. - - `credentials` - - An `Object` specifying the credentials to provide to the server - when authenticating. The following credentials are possible: - - | name | type | description - | ------------ | ----------- | ----------- - | `"username"` | `DOMString` | The user that authenticates - | `"password"` | `DOMString` | Password for the user - | `"target"` | `DOMString` | Target machine or session - - `repeaterID` - - A `DOMString` specifying the ID to provide to any VNC repeater - encountered. - - `wsProtocols` - - An `Array` of `DOMString`s specifying the sub-protocols to use - in the WebSocket connection. Empty by default. - -#### bell - -The `bell` event is fired when the server has requested an audible -bell. - -#### capabilities - -The `capabilities` event is fired whenever an entry is added or removed -from `RFB.capabilities`. The `detail` property is an `Object` with the -property `capabilities` containing the new value of `RFB.capabilities`. - -#### clippingviewport - -The `clippingviewport` event is fired whenever `RFB.clippingViewport` -changes between `true` and `false`. The `detail` property is a `boolean` -with the new value of `RFB.clippingViewport`. - -#### clipboard - -The `clipboard` event is fired when the server has sent clipboard data. -The `detail` property is an `Object` containing the property `text` -which is a `DOMString` with the clipboard data. - -#### credentialsrequired - -The `credentialsrequired` event is fired when the server requests more -credentials than were specified to [`RFB()`](#rfb-1). The `detail` -property is an `Object` containing the property `types` which is an -`Array` of `DOMString` listing the credentials that are required. - -#### connect - -The `connect` event is fired after all the handshaking with the server -is completed and the connection is fully established. After this event -the `RFB` object is ready to recieve graphics updates and to send input. - -#### desktopname - -The `desktopname` event is fired when the name of the remote desktop -changes. The `detail` property is an `Object` with the property `name` -which is a `DOMString` specifying the new name. - -#### disconnect - -The `disconnect` event is fired when the connection has been -terminated. The `detail` property is an `Object` that contains the -property `clean`. `clean` is a `boolean` indicating if the termination -was clean or not. In the event of an unexpected termination or an error -`clean` will be set to false. - -#### securityfailure - -The `securityfailure` event is fired when the handshaking process with -the server fails during the security negotiation step. The `detail` -property is an `Object` containing the following properties: - -| Property | Type | Description -| -------- | ----------- | ----------- -| `status` | `long` | The failure status code -| `reason` | `DOMString` | The **optional** reason for the failure - -The property `status` corresponds to the [SecurityResult][rfb-secresult] -status code in cases of failure. A status of zero will not be sent in -this event since that indicates a successful security handshaking -process. The optional property `reason` is provided by the server and -thus the language of the string is not known. However most servers will -probably send English strings. The server can choose to not send a -reason and in these cases the `reason` property will be omitted. - -[rfb-secresult]: https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst#securityresult - -#### serververification - -The `serververification` event is fired when the server provides -information that allows the user to verify that it is the correct server -and protect against a man-in-the-middle attack. The `detail` property is -an `Object` containing the property `type` which is a `DOMString` -specifying which type of information the server has provided. Other -properties are also available, depending on the value of `type`: - -`"RSA"` - - The server identity is verified using just a RSA key. The property - `publickey` is a `Uint8Array` containing the public key in a unsigned - big endian representation. - -#### RFB.approveServer() - -The `RFB.approveServer()` method is used to signal that the user has -verified the server identity provided in a `serververification` event -and that the connection can continue. - -##### Syntax - -```js -RFB.approveServer(); -``` - -#### RFB.blur() - -The `RFB.blur()` method remove keyboard focus on the remote session. -Keyboard events will no longer be sent to the remote server after this -point. - -##### Syntax - -```js -RFB.blur(); -``` - -#### RFB.clipboardPasteFrom() - -The `RFB.clipboardPasteFrom()` method is used to send clipboard data -to the remote server. - -##### Syntax - -```js -RFB.clipboardPasteFrom(text); -``` - -###### Parameters - -**`text`** - - A `DOMString` specifying the clipboard data to send. - -#### RFB.disconnect() - -The `RFB.disconnect()` method is used to disconnect from the currently -connected server. - -##### Syntax - -```js -RFB.disconnect(); -``` - -#### RFB.focus() - -The `RFB.focus()` method sets the keyboard focus on the remote session. -Keyboard events will be sent to the remote server after this point. - -##### Syntax - -```js -RFB.focus(); -RFB.focus(options); -``` - -###### Parameters - -**`options`** *Optional* - - A `object` providing options to control how the focus will be - performed. Please see [`HTMLElement.focus()`][mdn-focus] for - available options. - -[mdn-focus]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus - -#### RFB.getImageData() - -The `RFB.getImageData()` method is used to return the current content of -the screen encoded as [`ImageData`][mdn-imagedata]. - -[mdn-imagedata]: https://developer.mozilla.org/en-US/docs/Web/API/ImageData - -##### Syntax - -```js -RFB.getImageData(); -``` - -#### RFB.machineReboot() - -The `RFB.machineReboot()` method is used to request a clean reboot of -the remote machine. The capability `power` must be set for this method -to have any effect. - -##### Syntax - -```js -RFB.machineReboot(); -``` - -#### RFB.machineReset() - -The `RFB.machineReset()` method is used to request a forced reset of -the remote machine. The capability `power` must be set for this method -to have any effect. - -##### Syntax - -```js -RFB.machineReset(); -``` - -#### RFB.machineShutdown() - -The `RFB.machineShutdown()` method is used to request to shut down the -remote machine. The capability `power` must be set for this method to -have any effect. - -##### Syntax - -```js -RFB.machineShutdown(); -``` - -#### RFB.sendCredentials() - -The `RFB.sendCredentials()` method is used to provide the missing -credentials after a `credentialsrequired` event has been fired. - -##### Syntax - -```js -RFB.sendCredentials(credentials); -``` - -###### Parameters - -**`credentials`** - - An `Object` specifying the credentials to provide to the server - when authenticating. See [`RFB()`](#rfb-1) for details. - -#### RFB.sendCtrlAltDel() - -The `RFB.sendCtrlAltDel()` method is used to send the key sequence -*left Control*, *left Alt*, *Delete*. This is a convenience wrapper -around [`RFB.sendKey()`](#rfbsendkey). - -##### Syntax - -```js -RFB.sendCtrlAltDel(); -``` - -#### RFB.sendKey() - -The `RFB.sendKey()` method is used to send a key event to the server. - -##### Syntax - -```js -RFB.sendKey(keysym, code); -RFB.sendKey(keysym, code, down); -``` - -###### Parameters - -**`keysym`** - - A `long` specifying the RFB keysym to send. Can be `0` if a valid - **`code`** is specified. - -**`code`** - - A `DOMString` specifying the physical key to send. Valid values are - those that can be specified to [`KeyboardEvent.code`][mdn-keycode]. - If the physical key cannot be determined then `null` shall be - specified. - -[mdn-keycode]: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code - -**`down`** *Optional* - - A `boolean` specifying if a press or a release event should be - sent. If omitted then both a press and release event are sent. - -#### RFB.toBlob() - -The `RFB.toBlob()` method is used to return the current content of the -screen encoded as [`Blob`][mdn-blob]. - -[mdn-blob]: https://developer.mozilla.org/en-US/docs/Web/API/Blob - -##### Syntax - -```js -RFB.toBlob(callback); -RFB.toBlob(callback, type); -RFB.toBlob(callback, type, quality); -``` - -###### Parameters - -**`callback`** - - A callback function which will receive the resulting - [`Blob`][mdn-blob] as the single argument - -**`type`** *Optional* - - A string indicating the requested MIME type of the image - -**`quality`** *Optional* - - A number between 0 and 1 indicating the image quality. - -#### RFB.toDataURL() - -The `RFB.toDataURL()` method is used to return the current content of the -screen encoded as a data URL that could for example be put in the `src` attribute -of an `img` tag. - -##### Syntax - -```js -RFB.toDataURL(); -RFB.toDataURL(type); -RFB.toDataURL(type, encoderOptions); -``` - -###### Parameters - -**`type`** *Optional* - - A string indicating the requested MIME type of the image - -**`encoderOptions`** *Optional* - - A number between 0 and 1 indicating the image quality. diff --git a/base/app/novnc/docs/EMBEDDING.md b/base/app/novnc/docs/EMBEDDING.md deleted file mode 100644 index 1050014..0000000 --- a/base/app/novnc/docs/EMBEDDING.md +++ /dev/null @@ -1,105 +0,0 @@ -# Embedding and Deploying noVNC Application - -This document describes how to embed and deploy the noVNC application, which -includes settings and a full user interface. If you are looking for -documentation on how to use the core noVNC library in your own application, -then please see our [library documentation](LIBRARY.md). - -## Files - -The noVNC application consists of the following files and directories: - -* `vnc.html` - The main page for the application and where users should go. It - is possible to rename this file. - -* `app/` - Support files for the application. Contains code, images, styles and - translations. - -* `core/` - The core noVNC library. - -* `vendor/` - Third party support libraries used by the application and the - core library. - -The most basic deployment consists of simply serving these files from a web -server and setting up a WebSocket proxy to the VNC server. - -## Parameters - -The noVNC application can be controlled by including certain settings in the -query string. Currently the following options are available: - -* `autoconnect` - Automatically connect as soon as the page has finished - loading. - -* `reconnect` - If noVNC should automatically reconnect if the connection is - dropped. - -* `reconnect_delay` - How long to wait in milliseconds before attempting to - reconnect. - -* `host` - The WebSocket host to connect to. - -* `port` - The WebSocket port to connect to. - -* `encrypt` - If TLS should be used for the WebSocket connection. - -* `path` - The WebSocket path to use. - -* `password` - The password sent to the server, if required. - -* `repeaterID` - The repeater ID to use if a VNC repeater is detected. - -* `shared` - If other VNC clients should be disconnected when noVNC connects. - -* `bell` - If the keyboard bell should be enabled or not. - -* `view_only` - If the remote session should be in non-interactive mode. - -* `view_clip` - If the remote session should be clipped or use scrollbars if - it cannot fit in the browser. - -* `resize` - How to resize the remote session if it is not the same size as - the browser window. Can be one of `off`, `scale` and `remote`. - -* `quality` - The session JPEG quality level. Can be `0` to `9`. - -* `compression` - The session compression level. Can be `0` to `9`. - -* `show_dot` - If a dot cursor should be shown when the remote server provides - no local cursor, or provides a fully-transparent (invisible) cursor. - -* `logging` - The console log level. Can be one of `error`, `warn`, `info` or - `debug`. - -## HTTP Serving Considerations -### Browser Cache Issue - -If you serve noVNC files using a web server that provides an ETag header, and -include any options in the query string, a nasty browser cache issue can bite -you on upgrade, resulting in a red error box. The issue is caused by a mismatch -between the new vnc.html (which is reloaded because the user has used it with -new query string after the upgrade) and the old javascript files (that the -browser reuses from its cache). To avoid this issue, the browser must be told -to always revalidate cached files using conditional requests. The correct -semantics are achieved via the (confusingly named) `Cache-Control: no-cache` -header that needs to be provided in the web server responses. - -### Example Server Configurations - -Apache: - -``` - # In the main configuration file - # (Debian/Ubuntu users: use "a2enmod headers" instead) - LoadModule headers_module modules/mod_headers.so - - # In the <Directory> or <Location> block related to noVNC - Header set Cache-Control "no-cache" -``` - -Nginx: - -``` - # In the location block related to noVNC - add_header Cache-Control no-cache; -``` diff --git a/base/app/novnc/docs/LIBRARY.md b/base/app/novnc/docs/LIBRARY.md deleted file mode 100644 index 3890bb2..0000000 --- a/base/app/novnc/docs/LIBRARY.md +++ /dev/null @@ -1,31 +0,0 @@ -# Using the noVNC JavaScript library - -This document describes how to make use of the noVNC JavaScript library for -integration in your own VNC client application. If you wish to embed the more -complete noVNC application with its included user interface then please see -our [embedding documentation](EMBEDDING.md). - -## API - -The API of noVNC consists of a single object called `RFB`. The formal -documentation for that object can be found in our [API documentation](API.md). - -## Example - -noVNC includes a small example application called `vnc_lite.html`. This does -not make use of all the features of noVNC, but is a good start to see how to -do things. - -## Conversion of Modules - -noVNC is written using ECMAScript 6 modules. This is not supported by older -versions of Node.js. To use noVNC with those older versions of Node.js the -library must first be converted. - -Fortunately noVNC includes a script to handle this conversion. Please follow -the following steps: - - 1. Install Node.js - 2. Run `npm install` in the noVNC directory - -The result of the conversion is available in the `lib/` directory. diff --git a/base/app/novnc/docs/LICENSE.BSD-2-Clause b/base/app/novnc/docs/LICENSE.BSD-2-Clause deleted file mode 100644 index 9d66ec9..0000000 --- a/base/app/novnc/docs/LICENSE.BSD-2-Clause +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) <year>, <copyright holder> -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/base/app/novnc/docs/LICENSE.BSD-3-Clause b/base/app/novnc/docs/LICENSE.BSD-3-Clause deleted file mode 100644 index e160466..0000000 --- a/base/app/novnc/docs/LICENSE.BSD-3-Clause +++ /dev/null @@ -1,24 +0,0 @@ -Copyright (c) <year>, <copyright holder> -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the <organization> nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/base/app/novnc/docs/LICENSE.MPL-2.0 b/base/app/novnc/docs/LICENSE.MPL-2.0 deleted file mode 100644 index 14e2f77..0000000 --- a/base/app/novnc/docs/LICENSE.MPL-2.0 +++ /dev/null @@ -1,373 +0,0 @@ -Mozilla Public License Version 2.0 -================================== - -1. Definitions --------------- - -1.1. "Contributor" - means each individual or legal entity that creates, contributes to - the creation of, or owns Covered Software. - -1.2. "Contributor Version" - means the combination of the Contributions of others (if any) used - by a Contributor and that particular Contributor's Contribution. - -1.3. "Contribution" - means Covered Software of a particular Contributor. - -1.4. "Covered Software" - means Source Code Form to which the initial Contributor has attached - the notice in Exhibit A, the Executable Form of such Source Code - Form, and Modifications of such Source Code Form, in each case - including portions thereof. - -1.5. "Incompatible With Secondary Licenses" - means - - (a) that the initial Contributor has attached the notice described - in Exhibit B to the Covered Software; or - - (b) that the Covered Software was made available under the terms of - version 1.1 or earlier of the License, but not also under the - terms of a Secondary License. - -1.6. "Executable Form" - means any form of the work other than Source Code Form. - -1.7. "Larger Work" - means a work that combines Covered Software with other material, in - a separate file or files, that is not Covered Software. - -1.8. "License" - means this document. - -1.9. "Licensable" - means having the right to grant, to the maximum extent possible, - whether at the time of the initial grant or subsequently, any and - all of the rights conveyed by this License. - -1.10. "Modifications" - means any of the following: - - (a) any file in Source Code Form that results from an addition to, - deletion from, or modification of the contents of Covered - Software; or - - (b) any new file in Source Code Form that contains any Covered - Software. - -1.11. "Patent Claims" of a Contributor - means any patent claim(s), including without limitation, method, - process, and apparatus claims, in any patent Licensable by such - Contributor that would be infringed, but for the grant of the - License, by the making, using, selling, offering for sale, having - made, import, or transfer of either its Contributions or its - Contributor Version. - -1.12. "Secondary License" - means either the GNU General Public License, Version 2.0, the GNU - Lesser General Public License, Version 2.1, the GNU Affero General - Public License, Version 3.0, or any later versions of those - licenses. - -1.13. "Source Code Form" - means the form of the work preferred for making modifications. - -1.14. "You" (or "Your") - means an individual or a legal entity exercising rights under this - License. For legal entities, "You" includes any entity that - controls, is controlled by, or is under common control with You. For - purposes of this definition, "control" means (a) the power, direct - or indirect, to cause the direction or management of such entity, - whether by contract or otherwise, or (b) ownership of more than - fifty percent (50%) of the outstanding shares or beneficial - ownership of such entity. - -2. License Grants and Conditions --------------------------------- - -2.1. Grants - -Each Contributor hereby grants You a world-wide, royalty-free, -non-exclusive license: - -(a) under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or - as part of a Larger Work; and - -(b) under Patent Claims of such Contributor to make, use, sell, offer - for sale, have made, import, and otherwise transfer either its - Contributions or its Contributor Version. - -2.2. Effective Date - -The licenses granted in Section 2.1 with respect to any Contribution -become effective for each Contribution on the date the Contributor first -distributes such Contribution. - -2.3. Limitations on Grant Scope - -The licenses granted in this Section 2 are the only rights granted under -this License. No additional rights or licenses will be implied from the -distribution or licensing of Covered Software under this License. -Notwithstanding Section 2.1(b) above, no patent license is granted by a -Contributor: - -(a) for any code that a Contributor has removed from Covered Software; - or - -(b) for infringements caused by: (i) Your and any other third party's - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - -(c) under Patent Claims infringed by Covered Software in the absence of - its Contributions. - -This License does not grant any rights in the trademarks, service marks, -or logos of any Contributor (except as may be necessary to comply with -the notice requirements in Section 3.4). - -2.4. Subsequent Licenses - -No Contributor makes additional grants as a result of Your choice to -distribute the Covered Software under a subsequent version of this -License (see Section 10.2) or under the terms of a Secondary License (if -permitted under the terms of Section 3.3). - -2.5. Representation - -Each Contributor represents that the Contributor believes its -Contributions are its original creation(s) or it has sufficient rights -to grant the rights to its Contributions conveyed by this License. - -2.6. Fair Use - -This License is not intended to limit any rights You have under -applicable copyright doctrines of fair use, fair dealing, or other -equivalents. - -2.7. Conditions - -Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted -in Section 2.1. - -3. Responsibilities -------------------- - -3.1. Distribution of Source Form - -All distribution of Covered Software in Source Code Form, including any -Modifications that You create or to which You contribute, must be under -the terms of this License. You must inform recipients that the Source -Code Form of the Covered Software is governed by the terms of this -License, and how they can obtain a copy of this License. You may not -attempt to alter or restrict the recipients' rights in the Source Code -Form. - -3.2. Distribution of Executable Form - -If You distribute Covered Software in Executable Form then: - -(a) such Covered Software must also be made available in Source Code - Form, as described in Section 3.1, and You must inform recipients of - the Executable Form how they can obtain a copy of such Source Code - Form by reasonable means in a timely manner, at a charge no more - than the cost of distribution to the recipient; and - -(b) You may distribute such Executable Form under the terms of this - License, or sublicense it under different terms, provided that the - license for the Executable Form does not attempt to limit or alter - the recipients' rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - -You may create and distribute a Larger Work under terms of Your choice, -provided that You also comply with the requirements of this License for -the Covered Software. If the Larger Work is a combination of Covered -Software with a work governed by one or more Secondary Licenses, and the -Covered Software is not Incompatible With Secondary Licenses, this -License permits You to additionally distribute such Covered Software -under the terms of such Secondary License(s), so that the recipient of -the Larger Work may, at their option, further distribute the Covered -Software under the terms of either this License or such Secondary -License(s). - -3.4. Notices - -You may not remove or alter the substance of any license notices -(including copyright notices, patent notices, disclaimers of warranty, -or limitations of liability) contained within the Source Code Form of -the Covered Software, except that You may alter any license notices to -the extent required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - -You may choose to offer, and to charge a fee for, warranty, support, -indemnity or liability obligations to one or more recipients of Covered -Software. However, You may do so only on Your own behalf, and not on -behalf of any Contributor. You must make it absolutely clear that any -such warranty, support, indemnity, or liability obligation is offered by -You alone, and You hereby agree to indemnify every Contributor for any -liability incurred by such Contributor as a result of warranty, support, -indemnity or liability terms You offer. You may include additional -disclaimers of warranty and limitations of liability specific to any -jurisdiction. - -4. Inability to Comply Due to Statute or Regulation ---------------------------------------------------- - -If it is impossible for You to comply with any of the terms of this -License with respect to some or all of the Covered Software due to -statute, judicial order, or regulation then You must: (a) comply with -the terms of this License to the maximum extent possible; and (b) -describe the limitations and the code they affect. Such description must -be placed in a text file included with all distributions of the Covered -Software under this License. Except to the extent prohibited by statute -or regulation, such description must be sufficiently detailed for a -recipient of ordinary skill to be able to understand it. - -5. Termination --------------- - -5.1. The rights granted under this License will terminate automatically -if You fail to comply with any of its terms. However, if You become -compliant, then the rights granted under this License from a particular -Contributor are reinstated (a) provisionally, unless and until such -Contributor explicitly and finally terminates Your grants, and (b) on an -ongoing basis, if such Contributor fails to notify You of the -non-compliance by some reasonable means prior to 60 days after You have -come back into compliance. Moreover, Your grants from a particular -Contributor are reinstated on an ongoing basis if such Contributor -notifies You of the non-compliance by some reasonable means, this is the -first time You have received notice of non-compliance with this License -from such Contributor, and You become compliant prior to 30 days after -Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent -infringement claim (excluding declaratory judgment actions, -counter-claims, and cross-claims) alleging that a Contributor Version -directly or indirectly infringes any patent, then the rights granted to -You by any and all Contributors for the Covered Software under Section -2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all -end user license agreements (excluding distributors and resellers) which -have been validly granted by You or Your distributors under this License -prior to termination shall survive termination. - -************************************************************************ -* * -* 6. Disclaimer of Warranty * -* ------------------------- * -* * -* Covered Software is provided under this License on an "as is" * -* basis, without warranty of any kind, either expressed, implied, or * -* statutory, including, without limitation, warranties that the * -* Covered Software is free of defects, merchantable, fit for a * -* particular purpose or non-infringing. The entire risk as to the * -* quality and performance of the Covered Software is with You. * -* Should any Covered Software prove defective in any respect, You * -* (not any Contributor) assume the cost of any necessary servicing, * -* repair, or correction. This disclaimer of warranty constitutes an * -* essential part of this License. No use of any Covered Software is * -* authorized under this License except under this disclaimer. * -* * -************************************************************************ - -************************************************************************ -* * -* 7. Limitation of Liability * -* -------------------------- * -* * -* Under no circumstances and under no legal theory, whether tort * -* (including negligence), contract, or otherwise, shall any * -* Contributor, or anyone who distributes Covered Software as * -* permitted above, be liable to You for any direct, indirect, * -* special, incidental, or consequential damages of any character * -* including, without limitation, damages for lost profits, loss of * -* goodwill, work stoppage, computer failure or malfunction, or any * -* and all other commercial damages or losses, even if such party * -* shall have been informed of the possibility of such damages. This * -* limitation of liability shall not apply to liability for death or * -* personal injury resulting from such party's negligence to the * -* extent applicable law prohibits such limitation. Some * -* jurisdictions do not allow the exclusion or limitation of * -* incidental or consequential damages, so this exclusion and * -* limitation may not apply to You. * -* * -************************************************************************ - -8. Litigation -------------- - -Any litigation relating to this License may be brought only in the -courts of a jurisdiction where the defendant maintains its principal -place of business and such litigation shall be governed by laws of that -jurisdiction, without reference to its conflict-of-law provisions. -Nothing in this Section shall prevent a party's ability to bring -cross-claims or counter-claims. - -9. Miscellaneous ----------------- - -This License represents the complete agreement concerning the subject -matter hereof. If any provision of this License is held to be -unenforceable, such provision shall be reformed only to the extent -necessary to make it enforceable. Any law or regulation which provides -that the language of a contract shall be construed against the drafter -shall not be used to construe this License against a Contributor. - -10. Versions of the License ---------------------------- - -10.1. New Versions - -Mozilla Foundation is the license steward. Except as provided in Section -10.3, no one other than the license steward has the right to modify or -publish new versions of this License. Each version will be given a -distinguishing version number. - -10.2. Effect of New Versions - -You may distribute the Covered Software under the terms of the version -of the License under which You originally received the Covered Software, -or under the terms of any subsequent version published by the license -steward. - -10.3. Modified Versions - -If you create software not governed by this License, and you want to -create a new license for such software, you may create and use a -modified version of this License if you rename the license and remove -any references to the name of the license steward (except to note that -such modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary -Licenses - -If You choose to distribute Source Code Form that is Incompatible With -Secondary Licenses under the terms of this version of the License, the -notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice -------------------------------------------- - - This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular -file, then You may include the notice in a location (such as a LICENSE -file in a relevant directory) where a recipient would be likely to look -for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - "Incompatible With Secondary Licenses" Notice ---------------------------------------------------------- - - This Source Code Form is "Incompatible With Secondary Licenses", as - defined by the Mozilla Public License, v. 2.0. diff --git a/base/app/novnc/docs/LICENSE.OFL-1.1 b/base/app/novnc/docs/LICENSE.OFL-1.1 deleted file mode 100644 index 77b1731..0000000 --- a/base/app/novnc/docs/LICENSE.OFL-1.1 +++ /dev/null @@ -1,91 +0,0 @@ -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -http://scripts.sil.org/OFL - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/base/app/novnc/docs/flash_policy.txt b/base/app/novnc/docs/flash_policy.txt deleted file mode 100644 index df325c0..0000000 --- a/base/app/novnc/docs/flash_policy.txt +++ /dev/null @@ -1,4 +0,0 @@ -Manual setup: - -DATA="echo \'<cross-domain-policy><allow-access-from domain=\\\"*\\\" to-ports=\\\"*\\\" /></cross-domain-policy>\'" -/usr/bin/socat -T 1 TCP-L:843,reuseaddr,fork,crlf SYSTEM:"$DATA" diff --git a/base/app/novnc/docs/links b/base/app/novnc/docs/links deleted file mode 100644 index 31544ce..0000000 --- a/base/app/novnc/docs/links +++ /dev/null @@ -1,76 +0,0 @@ -New tight PNG protocol: - http://wiki.qemu.org/VNC_Tight_PNG - http://xf.iksaif.net/blog/index.php?post/2010/06/14/QEMU:-Tight-PNG-and-some-profiling - -RFB protocol and extensions: - http://tigervnc.org/cgi-bin/rfbproto - -Canvas Browser Compatibility: - http://philip.html5.org/tests/canvas/suite/tests/results.html - -WebSockets API standard: - http://www.whatwg.org/specs/web-apps/current-work/complete.html#websocket - http://dev.w3.org/html5/websockets/ - http://www.ietf.org/id/draft-ietf-hybi-thewebsocketprotocol-00.txt - -Browser Keyboard Events detailed: - http://unixpapa.com/js/key.html - -ActionScript (Flash) WebSocket implementation: - http://github.com/gimite/web-socket-js - -ActionScript (Flash) crypto/TLS library: - http://code.google.com/p/as3crypto - http://github.com/lyokato/as3crypto_patched - -TLS Protocol: - http://en.wikipedia.org/wiki/Transport_Layer_Security - -Generate self-signed certificate: - http://docs.python.org/dev/library/ssl.html#certificates - -Cursor appearance/style (for Cursor pseudo-encoding): - http://en.wikipedia.org/wiki/ICO_(file_format) - http://www.daubnet.com/en/file-format-cur - https://developer.mozilla.org/en/Using_URL_values_for_the_cursor_property - http://www.fileformat.info/format/bmp/egff.htm - -Icon/Cursor file format: - http://msdn.microsoft.com/en-us/library/ms997538 - http://msdn.microsoft.com/en-us/library/aa921550.aspx - http://msdn.microsoft.com/en-us/library/aa930622.aspx - - -RDP Protocol specification: - http://msdn.microsoft.com/en-us/library/cc240445(v=PROT.10).aspx - - -Related projects: - - guacamole: http://guacamole.sourceforge.net/ - - - Web client, but Java servlet does pre-processing - - jsvnc: http://code.google.com/p/jsvnc/ - - - No releases - - webvnc: http://code.google.com/p/webvnc/ - - - Jetty web server gateway, no updates since April 2008. - - RealVNC Java applet: http://www.realvnc.com/support/javavncviewer.html - - - Java applet - - Flashlight-VNC: http://www.wizhelp.com/flashlight-vnc/ - - - Adobe Flash implementation - - FVNC: http://osflash.org/fvnc - - - Adbove Flash implementation - - CanVNC: http://canvnc.sourceforge.net/ - - - HTML client with REST to VNC python proxy. Mostly vapor. diff --git a/base/app/novnc/docs/notes b/base/app/novnc/docs/notes deleted file mode 100644 index dfef0bd..0000000 --- a/base/app/novnc/docs/notes +++ /dev/null @@ -1,5 +0,0 @@ -Rebuilding inflator.js - -- Download pako from npm -- Install browserify using npm -- browserify core/inflator.mod.js -o core/inflator.js -s Inflator diff --git a/base/app/novnc/docs/novnc_proxy.1 b/base/app/novnc/docs/novnc_proxy.1 deleted file mode 100644 index 259e1b4..0000000 --- a/base/app/novnc/docs/novnc_proxy.1 +++ /dev/null @@ -1,37 +0,0 @@ -.TH novnc_proxy 1 "June 25, 2020" "version 1.2.0" "USER COMMANDS" - -.SH NAME -novnc_proxy - noVNC proxy server -.SH SYNOPSIS -.B novnc_proxy [--listen [HOST:]PORT] [--vnc VNC_HOST:PORT] [--cert CERT] [--ssl-only] - -Starts the WebSockets proxy and a mini-webserver and -provides a cut-and-paste URL to go to. - - --listen [HOST:]PORT Port for proxy/webserver to listen on - Default: 6080 (on all interfaces) - --vnc VNC_HOST:PORT VNC server host:port proxy target - Default: localhost:5900 - --cert CERT Path to combined cert/key file, or just - the cert file if used with --key - Default: self.pem - --key KEY Path to key file, when not combined with cert - --web WEB Path to web files (e.g. vnc.html) - Default: ./ - --ssl-only Disable non-https connections. - - --record FILE Record traffic to FILE.session.js - - --syslog SERVER Can be local socket such as /dev/log, or a UDP host:port pair. - - --heartbeat SEC send a ping to the client every SEC seconds - --timeout SEC after SEC seconds exit when not connected - --idle-timeout SEC server exits after SEC seconds if there are no - active connections - -.SH AUTHOR -The noVNC Authors -https://github.com/novnc/noVNC - -.SH SEE ALSO -websockify(1), nova-novncproxy(1) diff --git a/base/app/novnc/docs/rfb_notes b/base/app/novnc/docs/rfb_notes deleted file mode 100644 index 643e16c..0000000 --- a/base/app/novnc/docs/rfb_notes +++ /dev/null @@ -1,147 +0,0 @@ -5.1.1 ProtocolVersion: 12, 12 bytes - - - Sent by server, max supported - 12 ascii - "RFB 003.008\n" - - Response by client, version to use - 12 ascii - "RFB 003.003\n" - -5.1.2 Authentication: >=4, [16, 4] bytes - - - Sent by server - CARD32 - authentication-scheme - 0 - connection failed - CARD32 - length - length - reason - 1 - no authentication - - 2 - VNC authentication - 16 CARD8 - challenge (random bytes) - - - Response by client (if VNC authentication) - 16 CARD8 - client encrypts the challenge with DES, using user - password as key, sends resulting 16 byte response - - - Response by server (if VNC authentication) - CARD32 - 0 - OK - 1 - failed - 2 - too-many - -5.1.3 ClientInitialisation: 1 byte - - Sent by client - CARD8 - shared-flag, 0 exclusive, non-zero shared - -5.1.4 ServerInitialisation: >=24 bytes - - Sent by server - CARD16 - framebuffer-width - CARD16 - framebuffer-height - 16 byte PIXEL_FORMAT - server-pixel-format - CARD8 - bits-per-pixel - CARD8 - depth - CARD8 - big-endian-flag, non-zero is big endian - CARD8 - true-color-flag, non-zero then next 6 apply - CARD16 - red-max - CARD16 - green-max - CARD16 - blue-max - CARD8 - red-shift - CARD8 - green-shift - CARD8 - blue-shift - 3 bytes - padding - CARD32 - name-length - - CARD8[length] - name-string - - - -Client to Server Messages: - -5.2.1 SetPixelFormat: 20 bytes - CARD8: 0 - message-type - ... - -5.2.2 FixColourMapEntries: >=6 bytes - CARD8: 1 - message-type - ... - -5.2.3 SetEncodings: >=8 bytes - CARD8: 2 - message-type - CARD8 - padding - CARD16 - numer-of-encodings - - CARD32 - encoding-type in preference order - 0 - raw - 1 - copy-rectangle - 2 - RRE - 4 - CoRRE - 5 - hextile - -5.2.4 FramebufferUpdateRequest (10 bytes) - CARD8: 3 - message-type - CARD8 - incremental (0 for full-update, non-zero for incremental) - CARD16 - x-position - CARD16 - y-position - CARD16 - width - CARD16 - height - - -5.2.5 KeyEvent: 8 bytes - CARD8: 4 - message-type - CARD8 - down-flag - 2 bytes - padding - CARD32 - key (X-Windows keysym values) - -5.2.6 PointerEvent: 6 bytes - CARD8: 5 - message-type - CARD8 - button-mask - CARD16 - x-position - CARD16 - y-position - -5.2.7 ClientCutText: >=9 bytes - CARD8: 6 - message-type - ... - - -Server to Client Messages: - -5.3.1 FramebufferUpdate - CARD8: 0 - message-type - 1 byte - padding - CARD16 - number-of-rectangles - - CARD16 - x-position - CARD16 - y-position - CARD16 - width - CARD16 - height - CARD16 - encoding-type: - 0 - raw - 1 - copy rectangle - 2 - RRE - 4 - CoRRE - 5 - hextile - - raw: - - width x height pixel values - - copy rectangle: - CARD16 - src-x-position - CARD16 - src-y-position - - RRE: - CARD32 - N number-of-subrectangles - Nxd bytes - background-pixel-value (d bits-per-pixel) - - ... - -5.3.2 SetColourMapEntries (no support) - CARD8: 1 - message-type - ... - -5.3.3 Bell - CARD8: 2 - message-type - -5.3.4 ServerCutText - CARD8: 3 - message-type - - - - - diff --git a/base/app/novnc/docs/rfbproto-3.3.pdf b/base/app/novnc/docs/rfbproto-3.3.pdf deleted file mode 100644 index 56b8764..0000000 Binary files a/base/app/novnc/docs/rfbproto-3.3.pdf and /dev/null differ diff --git a/base/app/novnc/docs/rfbproto-3.7.pdf b/base/app/novnc/docs/rfbproto-3.7.pdf deleted file mode 100644 index 1ef5462..0000000 Binary files a/base/app/novnc/docs/rfbproto-3.7.pdf and /dev/null differ diff --git a/base/app/novnc/docs/rfbproto-3.8.pdf b/base/app/novnc/docs/rfbproto-3.8.pdf deleted file mode 100644 index 8f0730f..0000000 Binary files a/base/app/novnc/docs/rfbproto-3.8.pdf and /dev/null differ diff --git a/base/app/novnc/eslint.config.mjs b/base/app/novnc/eslint.config.mjs deleted file mode 100644 index c88e7b7..0000000 --- a/base/app/novnc/eslint.config.mjs +++ /dev/null @@ -1,102 +0,0 @@ -import globals from "globals"; -import js from "@eslint/js"; - -export default [ - js.configs.recommended, - { - languageOptions: { - ecmaVersion: 2020, - sourceType: "module", - globals: { - ...globals.browser, - ...globals.es2020, - } - }, - ignores: ["**/xtscancodes.js"], - rules: { - // Unsafe or confusing stuff that we forbid - - "no-unused-vars": ["error", { "vars": "all", - "args": "none", - "ignoreRestSiblings": true, - "caughtErrors": "none" }], - "no-constant-condition": ["error", { "checkLoops": false }], - "no-var": "error", - "no-useless-constructor": "error", - "object-shorthand": ["error", "methods", { "avoidQuotes": true }], - "prefer-arrow-callback": "error", - "arrow-body-style": ["error", "as-needed", { "requireReturnForObjectLiteral": false } ], - "arrow-parens": ["error", "as-needed", { "requireForBlockBody": true }], - "arrow-spacing": ["error"], - "no-confusing-arrow": ["error", { "allowParens": true }], - - // Enforced coding style - - "brace-style": ["error", "1tbs", { "allowSingleLine": true }], - "indent": ["error", 4, { "SwitchCase": 1, - "VariableDeclarator": "first", - "FunctionDeclaration": { "parameters": "first" }, - "FunctionExpression": { "parameters": "first" }, - "CallExpression": { "arguments": "first" }, - "ArrayExpression": "first", - "ObjectExpression": "first", - "ImportDeclaration": "first", - "ignoreComments": true }], - "comma-spacing": ["error"], - "comma-style": ["error"], - "curly": ["error", "multi-line"], - "func-call-spacing": ["error"], - "func-names": ["error"], - "func-style": ["error", "declaration", { "allowArrowFunctions": true }], - "key-spacing": ["error"], - "keyword-spacing": ["error"], - "no-trailing-spaces": ["error"], - "semi": ["error"], - "space-before-blocks": ["error"], - "space-before-function-paren": ["error", { "anonymous": "always", - "named": "never", - "asyncArrow": "always" }], - "switch-colon-spacing": ["error"], - "camelcase": ["error", { "allow": ["^XK_", "^XF86XK_"] }], - "no-console": ["error"], - } - }, - { - files: ["po/po2js", "po/xgettext-html"], - languageOptions: { - globals: { - ...globals.node, - } - }, - rules: { - "no-console": 0, - }, - }, - { - files: ["tests/*"], - languageOptions: { - globals: { - ...globals.node, - ...globals.mocha, - sinon: false, - chai: false, - } - }, - rules: { - "prefer-arrow-callback": 0, - // Too many anonymous callbacks - "func-names": "off", - }, - }, - { - files: ["utils/*"], - languageOptions: { - globals: { - ...globals.node, - } - }, - rules: { - "no-console": 0, - }, - }, -]; diff --git a/base/app/novnc/karma.conf.js b/base/app/novnc/karma.conf.js deleted file mode 100644 index 1ea1747..0000000 --- a/base/app/novnc/karma.conf.js +++ /dev/null @@ -1,85 +0,0 @@ -// Karma configuration - -// The Safari launcher is broken, so construct our own -function SafariBrowser(id, baseBrowserDecorator, args) { - baseBrowserDecorator(this); - - this._start = function(url) { - this._execCommand('/usr/bin/open', ['-W', '-n', '-a', 'Safari', url]); - } -} - -SafariBrowser.prototype = { - name: 'Safari' -} - -module.exports = (config) => { - let browsers = []; - - if (process.env.TEST_BROWSER_NAME) { - browsers = process.env.TEST_BROWSER_NAME.split(','); - } - - const my_conf = { - - // base path that will be used to resolve all patterns (eg. files, exclude) - basePath: '', - - // frameworks to use - // available frameworks: https://npmjs.org/browse/keyword/karma-adapter - frameworks: ['mocha', 'sinon-chai'], - - // list of files / patterns to load in the browser (loaded in order) - files: [ - { pattern: 'app/localization.js', included: false, type: 'module' }, - { pattern: 'app/webutil.js', included: false, type: 'module' }, - { pattern: 'core/**/*.js', included: false, type: 'module' }, - { pattern: 'vendor/pako/**/*.js', included: false, type: 'module' }, - { pattern: 'tests/test.*.js', type: 'module' }, - { pattern: 'tests/fake.*.js', included: false, type: 'module' }, - { pattern: 'tests/assertions.js', type: 'module' }, - ], - - client: { - mocha: { - // replace Karma debug page with mocha display - 'reporter': 'html', - 'ui': 'bdd' - } - }, - - // list of files to exclude - exclude: [ - ], - - plugins: [ - 'karma-*', - '@chiragrupani/karma-chromium-edge-launcher', - { 'launcher:Safari': [ 'type', SafariBrowser ] }, - ], - - // start these browsers - // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher - browsers: browsers, - - // test results reporter to use - // possible values: 'dots', 'progress' - // available reporters: https://npmjs.org/browse/keyword/karma-reporter - reporters: ['mocha'], - - - // level of logging - // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG - logLevel: config.LOG_INFO, - - - // enable / disable watching file and executing tests whenever any file changes - autoWatch: false, - - // Continuous Integration mode - // if true, Karma captures browsers, runs the tests and exits - singleRun: true, - }; - - config.set(my_conf); -}; diff --git a/base/app/novnc/package.json b/base/app/novnc/package.json deleted file mode 100644 index 9fa8c31..0000000 --- a/base/app/novnc/package.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "name": "@novnc/novnc", - "version": "1.5.0", - "description": "An HTML5 VNC client", - "browser": "lib/rfb", - "directories": { - "lib": "lib", - "doc": "docs", - "test": "tests" - }, - "files": [ - "lib", - "AUTHORS", - "VERSION", - "docs/API.md", - "docs/LIBRARY.md", - "docs/LICENSE*" - ], - "scripts": { - "lint": "eslint app core po/po2js po/xgettext-html tests utils", - "test": "karma start karma.conf.js", - "prepublish": "node ./utils/convert.js --clean" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/novnc/noVNC.git" - }, - "author": "Joel Martin <github@martintribe.org> (https://github.com/kanaka)", - "contributors": [ - "Samuel Mannehed <samuel@cendio.se> (https://github.com/samhed)", - "Pierre Ossman <ossman@cendio.se> (https://github.com/CendioOssman)" - ], - "license": "MPL-2.0", - "bugs": { - "url": "https://github.com/novnc/noVNC/issues" - }, - "homepage": "https://github.com/novnc/noVNC", - "devDependencies": { - "@babel/core": "latest", - "@babel/preset-env": "latest", - "babel-plugin-import-redirect": "latest", - "browserify": "latest", - "chai": "latest", - "commander": "latest", - "eslint": "latest", - "fs-extra": "latest", - "globals": "latest", - "jsdom": "latest", - "karma": "latest", - "karma-mocha": "latest", - "karma-chrome-launcher": "latest", - "@chiragrupani/karma-chromium-edge-launcher": "latest", - "karma-firefox-launcher": "latest", - "karma-ie-launcher": "latest", - "karma-mocha-reporter": "latest", - "karma-safari-launcher": "latest", - "karma-script-launcher": "latest", - "karma-sinon-chai": "latest", - "mocha": "latest", - "node-getopt": "latest", - "po2json": "latest", - "sinon": "latest", - "sinon-chai": "latest" - }, - "dependencies": {}, - "keywords": [ - "vnc", - "rfb", - "novnc", - "websockify" - ] -} diff --git a/base/app/novnc/po/Makefile b/base/app/novnc/po/Makefile deleted file mode 100644 index 61a233b..0000000 --- a/base/app/novnc/po/Makefile +++ /dev/null @@ -1,36 +0,0 @@ -all: -.PHONY: update-po update-js update-pot -.PHONY: FORCE - -LINGUAS := cs de el es fr it ja ko nl pl pt_BR ru sv tr zh_CN zh_TW - -VERSION := $(shell grep '"version"' ../package.json | cut -d '"' -f 4) - -POFILES := $(addsuffix .po,$(LINGUAS)) -JSONFILES := $(addprefix ../app/locale/,$(addsuffix .json,$(LINGUAS))) - -update-po: $(POFILES) -update-js: $(JSONFILES) - -%.po: FORCE - msgmerge --update --lang=$* $@ noVNC.pot -../app/locale/%.json: FORCE - ./po2js $*.po $@ - -update-pot: - xgettext --output=noVNC.js.pot \ - --copyright-holder="The noVNC Authors" \ - --package-name="noVNC" \ - --package-version="$(VERSION)" \ - --msgid-bugs-address="novnc@googlegroups.com" \ - --add-comments=TRANSLATORS: \ - --from-code=UTF-8 \ - --sort-by-file \ - ../app/*.js \ - ../core/*.js \ - ../core/input/*.js - ./xgettext-html --output=noVNC.html.pot \ - ../vnc.html - msgcat --output-file=noVNC.pot \ - --sort-by-file noVNC.js.pot noVNC.html.pot - rm -f noVNC.js.pot noVNC.html.pot diff --git a/base/app/novnc/po/cs.po b/base/app/novnc/po/cs.po deleted file mode 100644 index 2b1efd8..0000000 --- a/base/app/novnc/po/cs.po +++ /dev/null @@ -1,294 +0,0 @@ -# Czech translations for noVNC package. -# Copyright (C) 2018 The noVNC Authors -# This file is distributed under the same license as the noVNC package. -# Petr <petr@kle.cz>, 2018. -# -msgid "" -msgstr "" -"Project-Id-Version: noVNC 1.0.0-testing.2\n" -"Report-Msgid-Bugs-To: novnc@googlegroups.com\n" -"POT-Creation-Date: 2018-10-19 12:00+0200\n" -"PO-Revision-Date: 2018-10-19 12:00+0200\n" -"Last-Translator: Petr <petr@kle.cz>\n" -"Language-Team: Czech\n" -"Language: cs\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" - -#: ../app/ui.js:389 -msgid "Connecting..." -msgstr "Připojení..." - -#: ../app/ui.js:396 -msgid "Disconnecting..." -msgstr "Odpojení..." - -#: ../app/ui.js:402 -msgid "Reconnecting..." -msgstr "Obnova připojení..." - -#: ../app/ui.js:407 -msgid "Internal error" -msgstr "Vnitřní chyba" - -#: ../app/ui.js:997 -msgid "Must set host" -msgstr "Hostitel musí být nastavení" - -#: ../app/ui.js:1079 -msgid "Connected (encrypted) to " -msgstr "Připojení (šifrované) k " - -#: ../app/ui.js:1081 -msgid "Connected (unencrypted) to " -msgstr "Připojení (nešifrované) k " - -#: ../app/ui.js:1104 -msgid "Something went wrong, connection is closed" -msgstr "Něco se pokazilo, odpojeno" - -#: ../app/ui.js:1107 -msgid "Failed to connect to server" -msgstr "Chyba připojení k serveru" - -#: ../app/ui.js:1117 -msgid "Disconnected" -msgstr "Odpojeno" - -#: ../app/ui.js:1130 -msgid "New connection has been rejected with reason: " -msgstr "Nové připojení bylo odmítnuto s odůvodněním: " - -#: ../app/ui.js:1133 -msgid "New connection has been rejected" -msgstr "Nové připojení bylo odmítnuto" - -#: ../app/ui.js:1153 -msgid "Password is required" -msgstr "Je vyžadováno heslo" - -#: ../vnc.html:84 -msgid "noVNC encountered an error:" -msgstr "noVNC narazilo na chybu:" - -#: ../vnc.html:94 -msgid "Hide/Show the control bar" -msgstr "Skrýt/zobrazit ovládací panel" - -#: ../vnc.html:101 -msgid "Move/Drag Viewport" -msgstr "Přesunout/přetáhnout výřez" - -#: ../vnc.html:101 -msgid "viewport drag" -msgstr "přesun výřezu" - -#: ../vnc.html:107 ../vnc.html:110 ../vnc.html:113 ../vnc.html:116 -msgid "Active Mouse Button" -msgstr "Aktivní tlačítka myši" - -#: ../vnc.html:107 -msgid "No mousebutton" -msgstr "Žádné" - -#: ../vnc.html:110 -msgid "Left mousebutton" -msgstr "Levé tlačítko myši" - -#: ../vnc.html:113 -msgid "Middle mousebutton" -msgstr "Prostřední tlačítko myši" - -#: ../vnc.html:116 -msgid "Right mousebutton" -msgstr "Pravé tlačítko myši" - -#: ../vnc.html:119 -msgid "Keyboard" -msgstr "Klávesnice" - -#: ../vnc.html:119 -msgid "Show Keyboard" -msgstr "Zobrazit klávesnici" - -#: ../vnc.html:126 -msgid "Extra keys" -msgstr "Extra klávesy" - -#: ../vnc.html:126 -msgid "Show Extra Keys" -msgstr "Zobrazit extra klávesy" - -#: ../vnc.html:131 -msgid "Ctrl" -msgstr "Ctrl" - -#: ../vnc.html:131 -msgid "Toggle Ctrl" -msgstr "Přepnout Ctrl" - -#: ../vnc.html:134 -msgid "Alt" -msgstr "Alt" - -#: ../vnc.html:134 -msgid "Toggle Alt" -msgstr "Přepnout Alt" - -#: ../vnc.html:137 -msgid "Send Tab" -msgstr "Odeslat tabulátor" - -#: ../vnc.html:137 -msgid "Tab" -msgstr "Tab" - -#: ../vnc.html:140 -msgid "Esc" -msgstr "Esc" - -#: ../vnc.html:140 -msgid "Send Escape" -msgstr "Odeslat Esc" - -#: ../vnc.html:143 -msgid "Ctrl+Alt+Del" -msgstr "Ctrl+Alt+Del" - -#: ../vnc.html:143 -msgid "Send Ctrl-Alt-Del" -msgstr "Poslat Ctrl-Alt-Del" - -#: ../vnc.html:151 -msgid "Shutdown/Reboot" -msgstr "Vypnutí/Restart" - -#: ../vnc.html:151 -msgid "Shutdown/Reboot..." -msgstr "Vypnutí/Restart..." - -#: ../vnc.html:157 -msgid "Power" -msgstr "Napájení" - -#: ../vnc.html:159 -msgid "Shutdown" -msgstr "Vypnout" - -#: ../vnc.html:160 -msgid "Reboot" -msgstr "Restart" - -#: ../vnc.html:161 -msgid "Reset" -msgstr "Reset" - -#: ../vnc.html:166 ../vnc.html:172 -msgid "Clipboard" -msgstr "Schránka" - -#: ../vnc.html:176 -msgid "Clear" -msgstr "Vymazat" - -#: ../vnc.html:182 -msgid "Fullscreen" -msgstr "Celá obrazovka" - -#: ../vnc.html:187 ../vnc.html:194 -msgid "Settings" -msgstr "Nastavení" - -#: ../vnc.html:197 -msgid "Shared Mode" -msgstr "Sdílený režim" - -#: ../vnc.html:200 -msgid "View Only" -msgstr "Pouze prohlížení" - -#: ../vnc.html:204 -msgid "Clip to Window" -msgstr "Přizpůsobit oknu" - -#: ../vnc.html:207 -msgid "Scaling Mode:" -msgstr "Přizpůsobení velikosti" - -#: ../vnc.html:209 -msgid "None" -msgstr "Žádné" - -#: ../vnc.html:210 -msgid "Local Scaling" -msgstr "Místní" - -#: ../vnc.html:211 -msgid "Remote Resizing" -msgstr "Vzdálené" - -#: ../vnc.html:216 -msgid "Advanced" -msgstr "Pokročilé" - -#: ../vnc.html:219 -msgid "Repeater ID:" -msgstr "ID opakovače" - -#: ../vnc.html:223 -msgid "WebSocket" -msgstr "WebSocket" - -#: ../vnc.html:226 -msgid "Encrypt" -msgstr "Šifrování:" - -#: ../vnc.html:229 -msgid "Host:" -msgstr "Hostitel:" - -#: ../vnc.html:233 -msgid "Port:" -msgstr "Port:" - -#: ../vnc.html:237 -msgid "Path:" -msgstr "Cesta" - -#: ../vnc.html:244 -msgid "Automatic Reconnect" -msgstr "Automatická obnova připojení" - -#: ../vnc.html:247 -msgid "Reconnect Delay (ms):" -msgstr "Zpoždění připojení (ms)" - -#: ../vnc.html:252 -msgid "Show Dot when No Cursor" -msgstr "Tečka místo chybějícího kurzoru myši" - -#: ../vnc.html:257 -msgid "Logging:" -msgstr "Logování:" - -#: ../vnc.html:269 -msgid "Disconnect" -msgstr "Odpojit" - -#: ../vnc.html:288 -msgid "Connect" -msgstr "Připojit" - -#: ../vnc.html:298 -msgid "Password:" -msgstr "Heslo" - -#: ../vnc.html:302 -msgid "Send Password" -msgstr "Odeslat heslo" - -#: ../vnc.html:312 -msgid "Cancel" -msgstr "Zrušit" diff --git a/base/app/novnc/po/de.po b/base/app/novnc/po/de.po deleted file mode 100644 index 0c3fa0d..0000000 --- a/base/app/novnc/po/de.po +++ /dev/null @@ -1,303 +0,0 @@ -# German translations for noVNC package -# German translation for noVNC. -# Copyright (C) 2018 The noVNC Authors -# This file is distributed under the same license as the noVNC package. -# Loek Janssen <loekjanssen@gmail.com>, 2016. -# -msgid "" -msgstr "" -"Project-Id-Version: noVNC 0.6.1\n" -"Report-Msgid-Bugs-To: novnc@googlegroups.com\n" -"POT-Creation-Date: 2017-11-24 07:16+0000\n" -"PO-Revision-Date: 2017-11-24 08:20+0100\n" -"Last-Translator: Dominik Csapak <d.csapak@proxmox.com>\n" -"Language-Team: none\n" -"Language: de\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 1.8.11\n" - -#: ../app/ui.js:404 -msgid "Connecting..." -msgstr "Verbinden..." - -#: ../app/ui.js:411 -msgid "Disconnecting..." -msgstr "Verbindung trennen..." - -#: ../app/ui.js:417 -msgid "Reconnecting..." -msgstr "Verbindung wiederherstellen..." - -#: ../app/ui.js:422 -msgid "Internal error" -msgstr "Interner Fehler" - -#: ../app/ui.js:1019 -msgid "Must set host" -msgstr "Richten Sie den Server ein" - -#: ../app/ui.js:1099 -msgid "Connected (encrypted) to " -msgstr "Verbunden mit (verschlüsselt) " - -#: ../app/ui.js:1101 -msgid "Connected (unencrypted) to " -msgstr "Verbunden mit (unverschlüsselt) " - -#: ../app/ui.js:1119 -msgid "Something went wrong, connection is closed" -msgstr "Etwas lief schief, Verbindung wurde getrennt" - -#: ../app/ui.js:1129 -msgid "Disconnected" -msgstr "Verbindung zum Server getrennt" - -#: ../app/ui.js:1142 -msgid "New connection has been rejected with reason: " -msgstr "Verbindung wurde aus folgendem Grund abgelehnt: " - -#: ../app/ui.js:1145 -msgid "New connection has been rejected" -msgstr "Verbindung wurde abgelehnt" - -#: ../app/ui.js:1166 -msgid "Password is required" -msgstr "Passwort ist erforderlich" - -#: ../vnc.html:89 -msgid "noVNC encountered an error:" -msgstr "Ein Fehler ist aufgetreten:" - -#: ../vnc.html:99 -msgid "Hide/Show the control bar" -msgstr "Kontrollleiste verstecken/anzeigen" - -#: ../vnc.html:106 -msgid "Move/Drag Viewport" -msgstr "Ansichtsfenster verschieben/ziehen" - -#: ../vnc.html:106 -msgid "viewport drag" -msgstr "Ansichtsfenster ziehen" - -#: ../vnc.html:112 ../vnc.html:115 ../vnc.html:118 ../vnc.html:121 -msgid "Active Mouse Button" -msgstr "Aktive Maustaste" - -#: ../vnc.html:112 -msgid "No mousebutton" -msgstr "Keine Maustaste" - -#: ../vnc.html:115 -msgid "Left mousebutton" -msgstr "Linke Maustaste" - -#: ../vnc.html:118 -msgid "Middle mousebutton" -msgstr "Mittlere Maustaste" - -#: ../vnc.html:121 -msgid "Right mousebutton" -msgstr "Rechte Maustaste" - -#: ../vnc.html:124 -msgid "Keyboard" -msgstr "Tastatur" - -#: ../vnc.html:124 -msgid "Show Keyboard" -msgstr "Tastatur anzeigen" - -#: ../vnc.html:131 -msgid "Extra keys" -msgstr "Zusatztasten" - -#: ../vnc.html:131 -msgid "Show Extra Keys" -msgstr "Zusatztasten anzeigen" - -#: ../vnc.html:136 -msgid "Ctrl" -msgstr "Strg" - -#: ../vnc.html:136 -msgid "Toggle Ctrl" -msgstr "Strg umschalten" - -#: ../vnc.html:139 -msgid "Alt" -msgstr "Alt" - -#: ../vnc.html:139 -msgid "Toggle Alt" -msgstr "Alt umschalten" - -#: ../vnc.html:142 -msgid "Send Tab" -msgstr "Tab senden" - -#: ../vnc.html:142 -msgid "Tab" -msgstr "Tab" - -#: ../vnc.html:145 -msgid "Esc" -msgstr "Esc" - -#: ../vnc.html:145 -msgid "Send Escape" -msgstr "Escape senden" - -#: ../vnc.html:148 -msgid "Ctrl+Alt+Del" -msgstr "Strg+Alt+Entf" - -#: ../vnc.html:148 -msgid "Send Ctrl-Alt-Del" -msgstr "Strg+Alt+Entf senden" - -#: ../vnc.html:156 -msgid "Shutdown/Reboot" -msgstr "Herunterfahren/Neustarten" - -#: ../vnc.html:156 -msgid "Shutdown/Reboot..." -msgstr "Herunterfahren/Neustarten..." - -#: ../vnc.html:162 -msgid "Power" -msgstr "Energie" - -#: ../vnc.html:164 -msgid "Shutdown" -msgstr "Herunterfahren" - -#: ../vnc.html:165 -msgid "Reboot" -msgstr "Neustarten" - -#: ../vnc.html:166 -msgid "Reset" -msgstr "Zurücksetzen" - -#: ../vnc.html:171 ../vnc.html:177 -msgid "Clipboard" -msgstr "Zwischenablage" - -#: ../vnc.html:181 -msgid "Clear" -msgstr "Löschen" - -#: ../vnc.html:187 -msgid "Fullscreen" -msgstr "Vollbild" - -#: ../vnc.html:192 ../vnc.html:199 -msgid "Settings" -msgstr "Einstellungen" - -#: ../vnc.html:202 -msgid "Shared Mode" -msgstr "Geteilter Modus" - -#: ../vnc.html:205 -msgid "View Only" -msgstr "Nur betrachten" - -#: ../vnc.html:209 -msgid "Clip to Window" -msgstr "Auf Fenster begrenzen" - -#: ../vnc.html:212 -msgid "Scaling Mode:" -msgstr "Skalierungsmodus:" - -#: ../vnc.html:214 -msgid "None" -msgstr "Keiner" - -#: ../vnc.html:215 -msgid "Local Scaling" -msgstr "Lokales skalieren" - -#: ../vnc.html:216 -msgid "Remote Resizing" -msgstr "Serverseitiges skalieren" - -#: ../vnc.html:221 -msgid "Advanced" -msgstr "Erweitert" - -#: ../vnc.html:224 -msgid "Repeater ID:" -msgstr "Repeater ID:" - -#: ../vnc.html:228 -msgid "WebSocket" -msgstr "WebSocket" - -#: ../vnc.html:231 -msgid "Encrypt" -msgstr "Verschlüsselt" - -#: ../vnc.html:234 -msgid "Host:" -msgstr "Server:" - -#: ../vnc.html:238 -msgid "Port:" -msgstr "Port:" - -#: ../vnc.html:242 -msgid "Path:" -msgstr "Pfad:" - -#: ../vnc.html:249 -msgid "Automatic Reconnect" -msgstr "Automatisch wiederverbinden" - -#: ../vnc.html:252 -msgid "Reconnect Delay (ms):" -msgstr "Wiederverbindungsverzögerung (ms):" - -#: ../vnc.html:258 -msgid "Logging:" -msgstr "Protokollierung:" - -#: ../vnc.html:270 -msgid "Disconnect" -msgstr "Verbindung trennen" - -#: ../vnc.html:289 -msgid "Connect" -msgstr "Verbinden" - -#: ../vnc.html:299 -msgid "Password:" -msgstr "Passwort:" - -#: ../vnc.html:313 -msgid "Cancel" -msgstr "Abbrechen" - -#: ../vnc.html:329 -msgid "Canvas not supported." -msgstr "Canvas nicht unterstützt." - -#~ msgid "Disconnect timeout" -#~ msgstr "Zeitüberschreitung beim Trennen" - -#~ msgid "Local Downscaling" -#~ msgstr "Lokales herunterskalieren" - -#~ msgid "Local Cursor" -#~ msgstr "Lokaler Mauszeiger" - -#~ msgid "Forcing clipping mode since scrollbars aren't supported by IE in fullscreen" -#~ msgstr "'Clipping-Modus' aktiviert, Scrollbalken in 'IE-Vollbildmodus' werden nicht unterstützt" - -#~ msgid "True Color" -#~ msgstr "True Color" diff --git a/base/app/novnc/po/el.po b/base/app/novnc/po/el.po deleted file mode 100644 index de690fe..0000000 --- a/base/app/novnc/po/el.po +++ /dev/null @@ -1,399 +0,0 @@ -# Greek translations for noVNC package. -# Copyright (C) 2018 The noVNC Authors -# This file is distributed under the same license as the noVNC package. -# Giannis Kosmas <kosmasgiannis@gmail.com>, 2016. -# -msgid "" -msgstr "" -"Project-Id-Version: noVNC 0.6.1\n" -"Report-Msgid-Bugs-To: novnc@googlegroups.com\n" -"POT-Creation-Date: 2022-12-27 15:24+0100\n" -"PO-Revision-Date: 2017-10-11 16:16+0200\n" -"Last-Translator: Giannis Kosmas <kosmasgiannis@gmail.com>\n" -"Language-Team: none\n" -"Language: el\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: ../app/ui.js:69 -msgid "HTTPS is required for full functionality" -msgstr "Το HTTPS είναι απαιτούμενο για πλήρη λειτουργικότητα" - -#: ../app/ui.js:410 -msgid "Connecting..." -msgstr "Συνδέεται..." - -#: ../app/ui.js:417 -msgid "Disconnecting..." -msgstr "Aποσυνδέεται..." - -#: ../app/ui.js:423 -msgid "Reconnecting..." -msgstr "Επανασυνδέεται..." - -#: ../app/ui.js:428 -msgid "Internal error" -msgstr "Εσωτερικό σφάλμα" - -#: ../app/ui.js:1026 -msgid "Must set host" -msgstr "Πρέπει να οριστεί ο διακομιστής" - -#: ../app/ui.js:1110 -msgid "Connected (encrypted) to " -msgstr "Συνδέθηκε (κρυπτογραφημένα) με το " - -#: ../app/ui.js:1112 -msgid "Connected (unencrypted) to " -msgstr "Συνδέθηκε (μη κρυπτογραφημένα) με το " - -#: ../app/ui.js:1135 -msgid "Something went wrong, connection is closed" -msgstr "Κάτι πήγε στραβά, η σύνδεση διακόπηκε" - -#: ../app/ui.js:1138 -msgid "Failed to connect to server" -msgstr "Αποτυχία στη σύνδεση με το διακομιστή" - -#: ../app/ui.js:1150 -msgid "Disconnected" -msgstr "Αποσυνδέθηκε" - -#: ../app/ui.js:1165 -msgid "New connection has been rejected with reason: " -msgstr "Η νέα σύνδεση απορρίφθηκε διότι: " - -#: ../app/ui.js:1168 -msgid "New connection has been rejected" -msgstr "Η νέα σύνδεση απορρίφθηκε " - -#: ../app/ui.js:1234 -msgid "Credentials are required" -msgstr "Απαιτούνται διαπιστευτήρια" - -#: ../vnc.html:57 -msgid "noVNC encountered an error:" -msgstr "το noVNC αντιμετώπισε ένα σφάλμα:" - -#: ../vnc.html:67 -msgid "Hide/Show the control bar" -msgstr "Απόκρυψη/Εμφάνιση γραμμής ελέγχου" - -#: ../vnc.html:76 -msgid "Drag" -msgstr "Σύρσιμο" - -#: ../vnc.html:76 -msgid "Move/Drag Viewport" -msgstr "Μετακίνηση/Σύρσιμο Θεατού πεδίου" - -#: ../vnc.html:82 -msgid "Keyboard" -msgstr "Πληκτρολόγιο" - -#: ../vnc.html:82 -msgid "Show Keyboard" -msgstr "Εμφάνιση Πληκτρολογίου" - -#: ../vnc.html:87 -msgid "Extra keys" -msgstr "Επιπλέον πλήκτρα" - -#: ../vnc.html:87 -msgid "Show Extra Keys" -msgstr "Εμφάνιση Επιπλέον Πλήκτρων" - -#: ../vnc.html:92 -msgid "Ctrl" -msgstr "Ctrl" - -#: ../vnc.html:92 -msgid "Toggle Ctrl" -msgstr "Εναλλαγή Ctrl" - -#: ../vnc.html:95 -msgid "Alt" -msgstr "Alt" - -#: ../vnc.html:95 -msgid "Toggle Alt" -msgstr "Εναλλαγή Alt" - -#: ../vnc.html:98 -msgid "Toggle Windows" -msgstr "Εναλλαγή Παράθυρων" - -#: ../vnc.html:98 -msgid "Windows" -msgstr "Παράθυρα" - -#: ../vnc.html:101 -msgid "Send Tab" -msgstr "Αποστολή Tab" - -#: ../vnc.html:101 -msgid "Tab" -msgstr "Tab" - -#: ../vnc.html:104 -msgid "Esc" -msgstr "Esc" - -#: ../vnc.html:104 -msgid "Send Escape" -msgstr "Αποστολή Escape" - -#: ../vnc.html:107 -msgid "Ctrl+Alt+Del" -msgstr "Ctrl+Alt+Del" - -#: ../vnc.html:107 -msgid "Send Ctrl-Alt-Del" -msgstr "Αποστολή Ctrl-Alt-Del" - -#: ../vnc.html:114 -msgid "Shutdown/Reboot" -msgstr "Κλείσιμο/Επανεκκίνηση" - -#: ../vnc.html:114 -msgid "Shutdown/Reboot..." -msgstr "Κλείσιμο/Επανεκκίνηση..." - -#: ../vnc.html:120 -msgid "Power" -msgstr "Απενεργοποίηση" - -#: ../vnc.html:122 -msgid "Shutdown" -msgstr "Κλείσιμο" - -#: ../vnc.html:123 -msgid "Reboot" -msgstr "Επανεκκίνηση" - -#: ../vnc.html:124 -msgid "Reset" -msgstr "Επαναφορά" - -#: ../vnc.html:129 ../vnc.html:135 -msgid "Clipboard" -msgstr "Πρόχειρο" - -#: ../vnc.html:137 -msgid "Edit clipboard content in the textarea below." -msgstr "Επεξεργαστείτε το περιεχόμενο του πρόχειρου στην περιοχή κειμένου παρακάτω." - -#: ../vnc.html:145 -#, fuzzy -msgid "Full Screen" -msgstr "Πλήρης Οθόνη" - -#: ../vnc.html:150 ../vnc.html:156 -msgid "Settings" -msgstr "Ρυθμίσεις" - -#: ../vnc.html:160 -msgid "Shared Mode" -msgstr "Κοινόχρηστη Λειτουργία" - -#: ../vnc.html:163 -msgid "View Only" -msgstr "Μόνο Θέαση" - -#: ../vnc.html:167 -msgid "Clip to Window" -msgstr "Αποκοπή στο όριο του Παράθυρου" - -#: ../vnc.html:170 -msgid "Scaling Mode:" -msgstr "Λειτουργία Κλιμάκωσης:" - -#: ../vnc.html:172 -msgid "None" -msgstr "Καμία" - -#: ../vnc.html:173 -msgid "Local Scaling" -msgstr "Τοπική Κλιμάκωση" - -#: ../vnc.html:174 -msgid "Remote Resizing" -msgstr "Απομακρυσμένη Αλλαγή μεγέθους" - -#: ../vnc.html:179 -msgid "Advanced" -msgstr "Για προχωρημένους" - -#: ../vnc.html:182 -msgid "Quality:" -msgstr "Ποιότητα:" - -#: ../vnc.html:186 -msgid "Compression level:" -msgstr "Επίπεδο συμπίεσης:" - -#: ../vnc.html:191 -msgid "Repeater ID:" -msgstr "Repeater ID:" - -#: ../vnc.html:195 -msgid "WebSocket" -msgstr "WebSocket" - -#: ../vnc.html:198 -msgid "Encrypt" -msgstr "Κρυπτογράφηση" - -#: ../vnc.html:201 -msgid "Host:" -msgstr "Όνομα διακομιστή:" - -#: ../vnc.html:205 -msgid "Port:" -msgstr "Πόρτα διακομιστή:" - -#: ../vnc.html:209 -msgid "Path:" -msgstr "Διαδρομή:" - -#: ../vnc.html:216 -msgid "Automatic Reconnect" -msgstr "Αυτόματη επανασύνδεση" - -#: ../vnc.html:219 -msgid "Reconnect Delay (ms):" -msgstr "Καθυστέρηση επανασύνδεσης (ms):" - -#: ../vnc.html:224 -msgid "Show Dot when No Cursor" -msgstr "Εμφάνιση Τελείας όταν δεν υπάρχει Δρομέας" - -#: ../vnc.html:229 -msgid "Logging:" -msgstr "Καταγραφή:" - -#: ../vnc.html:238 -msgid "Version:" -msgstr "Έκδοση:" - -#: ../vnc.html:246 -msgid "Disconnect" -msgstr "Αποσύνδεση" - -#: ../vnc.html:269 -msgid "Connect" -msgstr "Σύνδεση" - -#: ../vnc.html:278 -msgid "Server identity" -msgstr "Ταυτότητα Διακομιστή" - -#: ../vnc.html:281 -msgid "The server has provided the following identifying information:" -msgstr "Ο διακομιστής παρείχε την ακόλουθη πληροφορία ταυτοποίησης:" - -#: ../vnc.html:285 -msgid "Fingerprint:" -msgstr "Δακτυλικό αποτύπωμα:" - -#: ../vnc.html:288 -msgid "" -"Please verify that the information is correct and press \"Approve\". " -"Otherwise press \"Reject\"." -msgstr "" -"Παρακαλώ επαληθεύσετε ότι η πληροφορία είναι σωστή και πιέστε \"Αποδοχή\". " -"Αλλιώς πιέστε \"Απόρριψη\"." - -#: ../vnc.html:293 -msgid "Approve" -msgstr "Αποδοχή" - -#: ../vnc.html:294 -msgid "Reject" -msgstr "Απόρριψη" - -#: ../vnc.html:302 -msgid "Credentials" -msgstr "Διαπιστευτήρια" - -#: ../vnc.html:306 -msgid "Username:" -msgstr "Κωδικός Χρήστη:" - -#: ../vnc.html:310 -msgid "Password:" -msgstr "Κωδικός Πρόσβασης:" - -#: ../vnc.html:314 -msgid "Send Credentials" -msgstr "Αποστολή Διαπιστευτηρίων" - -#: ../vnc.html:323 -msgid "Cancel" -msgstr "Ακύρωση" - -#~ msgid "Password is required" -#~ msgstr "Απαιτείται ο κωδικός πρόσβασης" - -#~ msgid "viewport drag" -#~ msgstr "σύρσιμο θεατού πεδίου" - -#~ msgid "Active Mouse Button" -#~ msgstr "Ενεργό Πλήκτρο Ποντικιού" - -#~ msgid "No mousebutton" -#~ msgstr "Χωρίς Πλήκτρο Ποντικιού" - -#~ msgid "Left mousebutton" -#~ msgstr "Αριστερό Πλήκτρο Ποντικιού" - -#~ msgid "Middle mousebutton" -#~ msgstr "Μεσαίο Πλήκτρο Ποντικιού" - -#~ msgid "Right mousebutton" -#~ msgstr "Δεξί Πλήκτρο Ποντικιού" - -#~ msgid "Clear" -#~ msgstr "Καθάρισμα" - -#~ msgid "Canvas not supported." -#~ msgstr "Δεν υποστηρίζεται το στοιχείο Canvas" - -#~ msgid "Disconnect timeout" -#~ msgstr "Παρέλευση χρονικού ορίου αποσύνδεσης" - -#~ msgid "Local Downscaling" -#~ msgstr "Τοπική Συρρίκνωση" - -#~ msgid "Local Cursor" -#~ msgstr "Τοπικός Δρομέας" - -#~ msgid "" -#~ "Forcing clipping mode since scrollbars aren't supported by IE in " -#~ "fullscreen" -#~ msgstr "" -#~ "Εφαρμογή λειτουργίας αποκοπής αφού δεν υποστηρίζονται οι λωρίδες κύλισης " -#~ "σε πλήρη οθόνη στον IE" - -#~ msgid "True Color" -#~ msgstr "Πραγματικά Χρώματα" - -#~ msgid "Style:" -#~ msgstr "Στυλ:" - -#~ msgid "default" -#~ msgstr "προεπιλεγμένο" - -#~ msgid "Apply" -#~ msgstr "Εφαρμογή" - -#~ msgid "Connection" -#~ msgstr "Σύνδεση" - -#~ msgid "Token:" -#~ msgstr "Διακριτικό:" - -#~ msgid "Send Password" -#~ msgstr "Αποστολή Κωδικού Πρόσβασης" diff --git a/base/app/novnc/po/es.po b/base/app/novnc/po/es.po deleted file mode 100644 index 1230402..0000000 --- a/base/app/novnc/po/es.po +++ /dev/null @@ -1,284 +0,0 @@ -# Spanish translations for noVNC package -# Traducciones al español para el paquete noVNC. -# Copyright (C) 2018 The noVNC Authors -# This file is distributed under the same license as the noVNC package. -# Juanjo Diaz <juanjo.diazmo@gmail.com>, 2018. -# Adrian Scillato <ascillato@gmail.com>, 2021. -# -msgid "" -msgstr "" -"Project-Id-Version: noVNC 1.0.0-testing.2\n" -"Report-Msgid-Bugs-To: novnc@googlegroups.com\n" -"POT-Creation-Date: 2017-10-06 10:07+0200\n" -"PO-Revision-Date: 2021-04-23 12:00-0300\n" -"Last-Translator: Adrian Scillato <ascillato@gmail.com>\n" -"Language-Team: Spanish\n" -"Language: es\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: ../app/ui.js:430 -msgid "Connecting..." -msgstr "Conectando..." - -#: ../app/ui.js:438 -msgid "Connected (encrypted) to " -msgstr "Conectado (con encriptación) a" - -#: ../app/ui.js:440 -msgid "Connected (unencrypted) to " -msgstr "Conectado (sin encriptación) a" - -#: ../app/ui.js:446 -msgid "Disconnecting..." -msgstr "Desconectando..." - -#: ../app/ui.js:450 -msgid "Disconnected" -msgstr "Desconectado" - -#: ../app/ui.js:1052 ../core/rfb.js:248 -msgid "Must set host" -msgstr "Se debe configurar el host" - -#: ../app/ui.js:1101 -msgid "Reconnecting..." -msgstr "Reconectando..." - -#: ../app/ui.js:1140 -msgid "Password is required" -msgstr "La contraseña es obligatoria" - -#: ../core/rfb.js:548 -msgid "Disconnect timeout" -msgstr "Tiempo de desconexión agotado" - -#: ../vnc.html:89 -msgid "noVNC encountered an error:" -msgstr "noVNC ha encontrado un error:" - -#: ../vnc.html:99 -msgid "Hide/Show the control bar" -msgstr "Ocultar/Mostrar la barra de control" - -#: ../vnc.html:106 -msgid "Move/Drag Viewport" -msgstr "Mover/Arrastrar la ventana" - -#: ../vnc.html:106 -msgid "viewport drag" -msgstr "Arrastrar la ventana" - -#: ../vnc.html:112 ../vnc.html:115 ../vnc.html:118 ../vnc.html:121 -msgid "Active Mouse Button" -msgstr "Botón activo del ratón" - -#: ../vnc.html:112 -msgid "No mousebutton" -msgstr "Ningún botón del ratón" - -#: ../vnc.html:115 -msgid "Left mousebutton" -msgstr "Botón izquierdo del ratón" - -#: ../vnc.html:118 -msgid "Middle mousebutton" -msgstr "Botón central del ratón" - -#: ../vnc.html:121 -msgid "Right mousebutton" -msgstr "Botón derecho del ratón" - -#: ../vnc.html:124 -msgid "Keyboard" -msgstr "Teclado" - -#: ../vnc.html:124 -msgid "Show Keyboard" -msgstr "Mostrar teclado" - -#: ../vnc.html:131 -msgid "Extra keys" -msgstr "Teclas adicionales" - -#: ../vnc.html:131 -msgid "Show Extra Keys" -msgstr "Mostrar Teclas Adicionales" - -#: ../vnc.html:136 -msgid "Ctrl" -msgstr "Ctrl" - -#: ../vnc.html:136 -msgid "Toggle Ctrl" -msgstr "Pulsar/Soltar Ctrl" - -#: ../vnc.html:139 -msgid "Alt" -msgstr "Alt" - -#: ../vnc.html:139 -msgid "Toggle Alt" -msgstr "Pulsar/Soltar Alt" - -#: ../vnc.html:142 -msgid "Send Tab" -msgstr "Enviar Tabulación" - -#: ../vnc.html:142 -msgid "Tab" -msgstr "Tabulación" - -#: ../vnc.html:145 -msgid "Esc" -msgstr "Esc" - -#: ../vnc.html:145 -msgid "Send Escape" -msgstr "Enviar Escape" - -#: ../vnc.html:148 -msgid "Ctrl+Alt+Del" -msgstr "Ctrl+Alt+Del" - -#: ../vnc.html:148 -msgid "Send Ctrl-Alt-Del" -msgstr "Enviar Ctrl+Alt+Del" - -#: ../vnc.html:156 -msgid "Shutdown/Reboot" -msgstr "Apagar/Reiniciar" - -#: ../vnc.html:156 -msgid "Shutdown/Reboot..." -msgstr "Apagar/Reiniciar..." - -#: ../vnc.html:162 -msgid "Power" -msgstr "Encender" - -#: ../vnc.html:164 -msgid "Shutdown" -msgstr "Apagar" - -#: ../vnc.html:165 -msgid "Reboot" -msgstr "Reiniciar" - -#: ../vnc.html:166 -msgid "Reset" -msgstr "Restablecer" - -#: ../vnc.html:171 ../vnc.html:177 -msgid "Clipboard" -msgstr "Portapapeles" - -#: ../vnc.html:181 -msgid "Clear" -msgstr "Vaciar" - -#: ../vnc.html:187 -msgid "Fullscreen" -msgstr "Pantalla Completa" - -#: ../vnc.html:192 ../vnc.html:199 -msgid "Settings" -msgstr "Configuraciones" - -#: ../vnc.html:200 -msgid "Encrypt" -msgstr "Encriptar" - -#: ../vnc.html:202 -msgid "Shared Mode" -msgstr "Modo Compartido" - -#: ../vnc.html:205 -msgid "View Only" -msgstr "Solo visualización" - -#: ../vnc.html:209 -msgid "Clip to Window" -msgstr "Recortar al tamaño de la ventana" - -#: ../vnc.html:212 -msgid "Scaling Mode:" -msgstr "Modo de escalado:" - -#: ../vnc.html:214 -msgid "None" -msgstr "Ninguno" - -#: ../vnc.html:215 -msgid "Local Scaling" -msgstr "Escalado Local" - -#: ../vnc.html:216 -msgid "Local Downscaling" -msgstr "Reducción de escala local" - -#: ../vnc.html:217 -msgid "Remote Resizing" -msgstr "Cambio de tamaño remoto" - -#: ../vnc.html:222 -msgid "Advanced" -msgstr "Avanzado" - -#: ../vnc.html:225 -msgid "Local Cursor" -msgstr "Cursor Local" - -#: ../vnc.html:229 -msgid "Repeater ID:" -msgstr "ID del Repetidor:" - -#: ../vnc.html:233 -msgid "WebSocket" -msgstr "WebSocket" - -#: ../vnc.html:239 -msgid "Host:" -msgstr "Host:" - -#: ../vnc.html:243 -msgid "Port:" -msgstr "Puerto:" - -#: ../vnc.html:247 -msgid "Path:" -msgstr "Ruta:" - -#: ../vnc.html:254 -msgid "Automatic Reconnect" -msgstr "Reconexión automática" - -#: ../vnc.html:257 -msgid "Reconnect Delay (ms):" -msgstr "Retraso en la reconexión (ms):" - -#: ../vnc.html:263 -msgid "Logging:" -msgstr "Registrando:" - -#: ../vnc.html:275 -msgid "Disconnect" -msgstr "Desconectar" - -#: ../vnc.html:294 -msgid "Connect" -msgstr "Conectar" - -#: ../vnc.html:304 -msgid "Password:" -msgstr "Contraseña:" - -#: ../vnc.html:318 -msgid "Cancel" -msgstr "Cancelar" - -#: ../vnc.html:334 -msgid "Canvas not supported." -msgstr "Canvas no soportado." diff --git a/base/app/novnc/po/fr.po b/base/app/novnc/po/fr.po deleted file mode 100644 index 748a4db..0000000 --- a/base/app/novnc/po/fr.po +++ /dev/null @@ -1,300 +0,0 @@ -# French translations for noVNC package -# Traductions françaises du paquet noVNC. -# Copyright (C) 2021 The noVNC Authors -# This file is distributed under the same license as the noVNC package. -# Jose <jose.matsuda@canada.ca>, 2021. -# Lowxorx <lowxorx@lahan.fr>, 2022. -# -msgid "" -msgstr "" -"Project-Id-Version: noVNC 1.2.0\n" -"Report-Msgid-Bugs-To: novnc@googlegroups.com\n" -"POT-Creation-Date: 2020-07-03 16:11+0200\n" -"PO-Revision-Date: 2022-04-25 23:40+0200\n" -"Last-Translator: Lowxorx <lowxorx@lahan.fr>\n" -"Language-Team: French\n" -"Language: fr\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" - -#: ../app/ui.js:394 -msgid "Connecting..." -msgstr "En cours de connexion..." - -#: ../app/ui.js:401 -msgid "Disconnecting..." -msgstr "Déconnexion en cours..." - -#: ../app/ui.js:407 -msgid "Reconnecting..." -msgstr "Reconnexion en cours..." - -#: ../app/ui.js:412 -msgid "Internal error" -msgstr "Erreur interne" - -#: ../app/ui.js:1008 -msgid "Must set host" -msgstr "Doit définir l'hôte" - -#: ../app/ui.js:1090 -msgid "Connected (encrypted) to " -msgstr "Connecté (chiffré) à " - -#: ../app/ui.js:1092 -msgid "Connected (unencrypted) to " -msgstr "Connecté (non chiffré) à " - -#: ../app/ui.js:1115 -msgid "Something went wrong, connection is closed" -msgstr "Quelque chose s'est mal passé, la connexion a été fermée" - -#: ../app/ui.js:1118 -msgid "Failed to connect to server" -msgstr "Échec de connexion au serveur" - -#: ../app/ui.js:1128 -msgid "Disconnected" -msgstr "Déconnecté" - -#: ../app/ui.js:1143 -msgid "New connection has been rejected with reason: " -msgstr "Une nouvelle connexion a été rejetée avec motif : " - -#: ../app/ui.js:1146 -msgid "New connection has been rejected" -msgstr "Une nouvelle connexion a été rejetée" - -#: ../app/ui.js:1181 -msgid "Credentials are required" -msgstr "Les identifiants sont requis" - -#: ../vnc.html:74 -msgid "noVNC encountered an error:" -msgstr "noVNC a rencontré une erreur :" - -#: ../vnc.html:84 -msgid "Hide/Show the control bar" -msgstr "Masquer/Afficher la barre de contrôle" - -#: ../vnc.html:91 -msgid "Drag" -msgstr "Faire glisser" - -#: ../vnc.html:91 -msgid "Move/Drag Viewport" -msgstr "Déplacer/faire glisser le Viewport" - -#: ../vnc.html:97 -msgid "Keyboard" -msgstr "Clavier" - -#: ../vnc.html:97 -msgid "Show Keyboard" -msgstr "Afficher le clavier" - -#: ../vnc.html:102 -msgid "Extra keys" -msgstr "Touches supplémentaires" - -#: ../vnc.html:102 -msgid "Show Extra Keys" -msgstr "Afficher les touches supplémentaires" - -#: ../vnc.html:107 -msgid "Ctrl" -msgstr "Ctrl" - -#: ../vnc.html:107 -msgid "Toggle Ctrl" -msgstr "Basculer Ctrl" - -#: ../vnc.html:110 -msgid "Alt" -msgstr "Alt" - -#: ../vnc.html:110 -msgid "Toggle Alt" -msgstr "Basculer Alt" - -#: ../vnc.html:113 -msgid "Toggle Windows" -msgstr "Basculer Windows" - -#: ../vnc.html:113 -msgid "Windows" -msgstr "Windows" - -#: ../vnc.html:116 -msgid "Send Tab" -msgstr "Envoyer l'onglet" - -#: ../vnc.html:116 -msgid "Tab" -msgstr "l'onglet" - -#: ../vnc.html:119 -msgid "Esc" -msgstr "Esc" - -#: ../vnc.html:119 -msgid "Send Escape" -msgstr "Envoyer Escape" - -#: ../vnc.html:122 -msgid "Ctrl+Alt+Del" -msgstr "Ctrl+Alt+Del" - -#: ../vnc.html:122 -msgid "Send Ctrl-Alt-Del" -msgstr "Envoyer Ctrl-Alt-Del" - -#: ../vnc.html:129 -msgid "Shutdown/Reboot" -msgstr "Arrêter/Redémarrer" - -#: ../vnc.html:129 -msgid "Shutdown/Reboot..." -msgstr "Arrêter/Redémarrer..." - -#: ../vnc.html:135 -msgid "Power" -msgstr "Alimentation" - -#: ../vnc.html:137 -msgid "Shutdown" -msgstr "Arrêter" - -#: ../vnc.html:138 -msgid "Reboot" -msgstr "Redémarrer" - -#: ../vnc.html:139 -msgid "Reset" -msgstr "Réinitialiser" - -#: ../vnc.html:144 ../vnc.html:150 -msgid "Clipboard" -msgstr "Presse-papiers" - -#: ../vnc.html:154 -msgid "Clear" -msgstr "Effacer" - -#: ../vnc.html:160 -msgid "Fullscreen" -msgstr "Plein écran" - -#: ../vnc.html:165 ../vnc.html:172 -msgid "Settings" -msgstr "Paramètres" - -#: ../vnc.html:175 -msgid "Shared Mode" -msgstr "Mode partagé" - -#: ../vnc.html:178 -msgid "View Only" -msgstr "Afficher uniquement" - -#: ../vnc.html:182 -msgid "Clip to Window" -msgstr "Clip à fenêtre" - -#: ../vnc.html:185 -msgid "Scaling Mode:" -msgstr "Mode mise à l'échelle :" - -#: ../vnc.html:187 -msgid "None" -msgstr "Aucun" - -#: ../vnc.html:188 -msgid "Local Scaling" -msgstr "Mise à l'échelle locale" - -#: ../vnc.html:189 -msgid "Remote Resizing" -msgstr "Redimensionnement à distance" - -#: ../vnc.html:194 -msgid "Advanced" -msgstr "Avancé" - -#: ../vnc.html:197 -msgid "Quality:" -msgstr "Qualité :" - -#: ../vnc.html:201 -msgid "Compression level:" -msgstr "Niveau de compression :" - -#: ../vnc.html:206 -msgid "Repeater ID:" -msgstr "ID Répéteur :" - -#: ../vnc.html:210 -msgid "WebSocket" -msgstr "WebSocket" - -#: ../vnc.html:213 -msgid "Encrypt" -msgstr "Chiffrer" - -#: ../vnc.html:216 -msgid "Host:" -msgstr "Hôte :" - -#: ../vnc.html:220 -msgid "Port:" -msgstr "Port :" - -#: ../vnc.html:224 -msgid "Path:" -msgstr "Chemin :" - -#: ../vnc.html:231 -msgid "Automatic Reconnect" -msgstr "Reconnecter automatiquemen" - -#: ../vnc.html:234 -msgid "Reconnect Delay (ms):" -msgstr "Délai de reconnexion (ms) :" - -#: ../vnc.html:239 -msgid "Show Dot when No Cursor" -msgstr "Afficher le point lorsqu'il n'y a pas de curseur" - -#: ../vnc.html:244 -msgid "Logging:" -msgstr "Se connecter :" - -#: ../vnc.html:253 -msgid "Version:" -msgstr "Version :" - -#: ../vnc.html:261 -msgid "Disconnect" -msgstr "Déconnecter" - -#: ../vnc.html:280 -msgid "Connect" -msgstr "Connecter" - -#: ../vnc.html:290 -msgid "Username:" -msgstr "Nom d'utilisateur :" - -#: ../vnc.html:294 -msgid "Password:" -msgstr "Mot de passe :" - -#: ../vnc.html:298 -msgid "Send Credentials" -msgstr "Envoyer les identifiants" - -#: ../vnc.html:308 -msgid "Cancel" -msgstr "Annuler" diff --git a/base/app/novnc/po/it.po b/base/app/novnc/po/it.po deleted file mode 100644 index d08ec53..0000000 --- a/base/app/novnc/po/it.po +++ /dev/null @@ -1,300 +0,0 @@ -# Italian translations for noVNC -# Traduzione italiana di noVNC -# Copyright (C) 2022 The noVNC Authors -# This file is distributed under the same license as the noVNC package. -# Fabio Fantoni <fabio.fantoni@m2r.biz>, 2022. -# -msgid "" -msgstr "" -"Project-Id-Version: noVNC 1.3.0\n" -"Report-Msgid-Bugs-To: novnc@googlegroups.com\n" -"POT-Creation-Date: 2021-08-27 16:03+0200\n" -"PO-Revision-Date: 2022-09-08 13:27+0200\n" -"Last-Translator: Fabio Fantoni <fabio.fantoni@m2r.biz>\n" -"Language-Team: Italian\n" -"Language: it\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 3.1.1\n" - -#: ../app/ui.js:400 -msgid "Connecting..." -msgstr "Connessione in corso..." - -#: ../app/ui.js:407 -msgid "Disconnecting..." -msgstr "Disconnessione..." - -#: ../app/ui.js:413 -msgid "Reconnecting..." -msgstr "Riconnessione..." - -#: ../app/ui.js:418 -msgid "Internal error" -msgstr "Errore interno" - -#: ../app/ui.js:1009 -msgid "Must set host" -msgstr "Devi impostare l'host" - -#: ../app/ui.js:1091 -msgid "Connected (encrypted) to " -msgstr "Connesso (crittografato) a " - -#: ../app/ui.js:1093 -msgid "Connected (unencrypted) to " -msgstr "Connesso (non crittografato) a" - -#: ../app/ui.js:1116 -msgid "Something went wrong, connection is closed" -msgstr "Qualcosa è andato storto, la connessione è stata chiusa" - -#: ../app/ui.js:1119 -msgid "Failed to connect to server" -msgstr "Impossibile connettersi al server" - -#: ../app/ui.js:1129 -msgid "Disconnected" -msgstr "Disconnesso" - -#: ../app/ui.js:1144 -msgid "New connection has been rejected with reason: " -msgstr "La nuova connessione è stata rifiutata con motivo: " - -#: ../app/ui.js:1147 -msgid "New connection has been rejected" -msgstr "La nuova connessione è stata rifiutata" - -#: ../app/ui.js:1182 -msgid "Credentials are required" -msgstr "Le credenziali sono obbligatorie" - -#: ../vnc.html:61 -msgid "noVNC encountered an error:" -msgstr "noVNC ha riscontrato un errore:" - -#: ../vnc.html:71 -msgid "Hide/Show the control bar" -msgstr "Nascondi/Mostra la barra di controllo" - -#: ../vnc.html:78 -msgid "Drag" -msgstr "" - -#: ../vnc.html:78 -msgid "Move/Drag Viewport" -msgstr "" - -#: ../vnc.html:84 -msgid "Keyboard" -msgstr "Tastiera" - -#: ../vnc.html:84 -msgid "Show Keyboard" -msgstr "Mostra tastiera" - -#: ../vnc.html:89 -msgid "Extra keys" -msgstr "Tasti Aggiuntivi" - -#: ../vnc.html:89 -msgid "Show Extra Keys" -msgstr "Mostra Tasti Aggiuntivi" - -#: ../vnc.html:94 -msgid "Ctrl" -msgstr "Ctrl" - -#: ../vnc.html:94 -msgid "Toggle Ctrl" -msgstr "Tieni premuto Ctrl" - -#: ../vnc.html:97 -msgid "Alt" -msgstr "Alt" - -#: ../vnc.html:97 -msgid "Toggle Alt" -msgstr "Tieni premuto Alt" - -#: ../vnc.html:100 -msgid "Toggle Windows" -msgstr "Tieni premuto Windows" - -#: ../vnc.html:100 -msgid "Windows" -msgstr "Windows" - -#: ../vnc.html:103 -msgid "Send Tab" -msgstr "Invia Tab" - -#: ../vnc.html:103 -msgid "Tab" -msgstr "Tab" - -#: ../vnc.html:106 -msgid "Esc" -msgstr "Esc" - -#: ../vnc.html:106 -msgid "Send Escape" -msgstr "Invia Esc" - -#: ../vnc.html:109 -msgid "Ctrl+Alt+Del" -msgstr "Ctrl+Alt+Canc" - -#: ../vnc.html:109 -msgid "Send Ctrl-Alt-Del" -msgstr "Invia Ctrl-Alt-Canc" - -#: ../vnc.html:116 -msgid "Shutdown/Reboot" -msgstr "Spegnimento/Riavvio" - -#: ../vnc.html:116 -msgid "Shutdown/Reboot..." -msgstr "Spegnimento/Riavvio..." - -#: ../vnc.html:122 -msgid "Power" -msgstr "Alimentazione" - -#: ../vnc.html:124 -msgid "Shutdown" -msgstr "Spegnimento" - -#: ../vnc.html:125 -msgid "Reboot" -msgstr "Riavvio" - -#: ../vnc.html:126 -msgid "Reset" -msgstr "Reset" - -#: ../vnc.html:131 ../vnc.html:137 -msgid "Clipboard" -msgstr "Clipboard" - -#: ../vnc.html:141 -msgid "Clear" -msgstr "Pulisci" - -#: ../vnc.html:147 -msgid "Fullscreen" -msgstr "Schermo intero" - -#: ../vnc.html:152 ../vnc.html:159 -msgid "Settings" -msgstr "Impostazioni" - -#: ../vnc.html:162 -msgid "Shared Mode" -msgstr "Modalità condivisa" - -#: ../vnc.html:165 -msgid "View Only" -msgstr "Sola Visualizzazione" - -#: ../vnc.html:169 -msgid "Clip to Window" -msgstr "" - -#: ../vnc.html:172 -msgid "Scaling Mode:" -msgstr "Modalità di ridimensionamento:" - -#: ../vnc.html:174 -msgid "None" -msgstr "Nessuna" - -#: ../vnc.html:175 -msgid "Local Scaling" -msgstr "Ridimensionamento Locale" - -#: ../vnc.html:176 -msgid "Remote Resizing" -msgstr "Ridimensionamento Remoto" - -#: ../vnc.html:181 -msgid "Advanced" -msgstr "Avanzate" - -#: ../vnc.html:184 -msgid "Quality:" -msgstr "Qualità:" - -#: ../vnc.html:188 -msgid "Compression level:" -msgstr "Livello Compressione:" - -#: ../vnc.html:193 -msgid "Repeater ID:" -msgstr "ID Ripetitore:" - -#: ../vnc.html:197 -msgid "WebSocket" -msgstr "WebSocket" - -#: ../vnc.html:200 -msgid "Encrypt" -msgstr "Crittografa" - -#: ../vnc.html:203 -msgid "Host:" -msgstr "Host:" - -#: ../vnc.html:207 -msgid "Port:" -msgstr "Porta:" - -#: ../vnc.html:211 -msgid "Path:" -msgstr "Percorso:" - -#: ../vnc.html:218 -msgid "Automatic Reconnect" -msgstr "Riconnessione Automatica" - -#: ../vnc.html:221 -msgid "Reconnect Delay (ms):" -msgstr "Ritardo Riconnessione (ms):" - -#: ../vnc.html:226 -msgid "Show Dot when No Cursor" -msgstr "Mostra Punto quando Nessun Cursore" - -#: ../vnc.html:231 -msgid "Logging:" -msgstr "" - -#: ../vnc.html:240 -msgid "Version:" -msgstr "Versione:" - -#: ../vnc.html:248 -msgid "Disconnect" -msgstr "Disconnetti" - -#: ../vnc.html:267 -msgid "Connect" -msgstr "Connetti" - -#: ../vnc.html:277 -msgid "Username:" -msgstr "Utente:" - -#: ../vnc.html:281 -msgid "Password:" -msgstr "Password:" - -#: ../vnc.html:285 -msgid "Send Credentials" -msgstr "Invia Credenziale" - -#: ../vnc.html:295 -msgid "Cancel" -msgstr "Annulla" diff --git a/base/app/novnc/po/ja.po b/base/app/novnc/po/ja.po deleted file mode 100644 index 64da732..0000000 --- a/base/app/novnc/po/ja.po +++ /dev/null @@ -1,363 +0,0 @@ -# Japanese translations for noVNC package -# noVNC パッケージに対する日訳 -# Copyright (C) 2019 The noVNC Authors -# This file is distributed under the same license as the noVNC package. -# nnn1590 <nnn1590@nnn1590.org>, 2019-2020. -# -msgid "" -msgstr "" -"Project-Id-Version: noVNC 1.1.0\n" -"Report-Msgid-Bugs-To: novnc@googlegroups.com\n" -"POT-Creation-Date: 2022-12-27 15:24+0100\n" -"PO-Revision-Date: 2023-03-21 12:42+0900\n" -"Last-Translator: nnn1590 <nnn1590@nnn1590.org>\n" -"Language-Team: Japanese\n" -"Language: ja\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Poedit 2.3\n" - -#: ../app/ui.js:69 -msgid "HTTPS is required for full functionality" -msgstr "すべての機能を使用するにはHTTPS接続が必要です" - -#: ../app/ui.js:410 -msgid "Connecting..." -msgstr "接続しています..." - -#: ../app/ui.js:417 -msgid "Disconnecting..." -msgstr "切断しています..." - -#: ../app/ui.js:423 -msgid "Reconnecting..." -msgstr "再接続しています..." - -#: ../app/ui.js:428 -msgid "Internal error" -msgstr "内部エラー" - -#: ../app/ui.js:1026 -msgid "Must set host" -msgstr "ホストを設定する必要があります" - -#: ../app/ui.js:1110 -msgid "Connected (encrypted) to " -msgstr "接続しました (暗号化済み): " - -#: ../app/ui.js:1112 -msgid "Connected (unencrypted) to " -msgstr "接続しました (暗号化されていません): " - -#: ../app/ui.js:1135 -msgid "Something went wrong, connection is closed" -msgstr "何らかの問題で、接続が閉じられました" - -#: ../app/ui.js:1138 -msgid "Failed to connect to server" -msgstr "サーバーへの接続に失敗しました" - -#: ../app/ui.js:1150 -msgid "Disconnected" -msgstr "切断しました" - -#: ../app/ui.js:1165 -msgid "New connection has been rejected with reason: " -msgstr "新規接続は次の理由で拒否されました: " - -#: ../app/ui.js:1168 -msgid "New connection has been rejected" -msgstr "新規接続は拒否されました" - -#: ../app/ui.js:1234 -msgid "Credentials are required" -msgstr "資格情報が必要です" - -#: ../vnc.html:57 -msgid "noVNC encountered an error:" -msgstr "noVNC でエラーが発生しました:" - -#: ../vnc.html:67 -msgid "Hide/Show the control bar" -msgstr "コントロールバーを隠す/表示する" - -#: ../vnc.html:76 -msgid "Drag" -msgstr "ドラッグ" - -#: ../vnc.html:76 -msgid "Move/Drag Viewport" -msgstr "ビューポートを移動/ドラッグ" - -#: ../vnc.html:82 -msgid "Keyboard" -msgstr "キーボード" - -#: ../vnc.html:82 -msgid "Show Keyboard" -msgstr "キーボードを表示" - -#: ../vnc.html:87 -msgid "Extra keys" -msgstr "追加キー" - -#: ../vnc.html:87 -msgid "Show Extra Keys" -msgstr "追加キーを表示" - -#: ../vnc.html:92 -msgid "Ctrl" -msgstr "Ctrl" - -#: ../vnc.html:92 -msgid "Toggle Ctrl" -msgstr "Ctrl キーをトグル" - -#: ../vnc.html:95 -msgid "Alt" -msgstr "Alt" - -#: ../vnc.html:95 -msgid "Toggle Alt" -msgstr "Alt キーをトグル" - -#: ../vnc.html:98 -msgid "Toggle Windows" -msgstr "Windows キーをトグル" - -#: ../vnc.html:98 -msgid "Windows" -msgstr "Windows" - -#: ../vnc.html:101 -msgid "Send Tab" -msgstr "Tab キーを送信" - -#: ../vnc.html:101 -msgid "Tab" -msgstr "Tab" - -#: ../vnc.html:104 -msgid "Esc" -msgstr "Esc" - -#: ../vnc.html:104 -msgid "Send Escape" -msgstr "Escape キーを送信" - -#: ../vnc.html:107 -msgid "Ctrl+Alt+Del" -msgstr "Ctrl+Alt+Del" - -#: ../vnc.html:107 -msgid "Send Ctrl-Alt-Del" -msgstr "Ctrl-Alt-Del を送信" - -#: ../vnc.html:114 -msgid "Shutdown/Reboot" -msgstr "シャットダウン/再起動" - -#: ../vnc.html:114 -msgid "Shutdown/Reboot..." -msgstr "シャットダウン/再起動..." - -#: ../vnc.html:120 -msgid "Power" -msgstr "電源" - -#: ../vnc.html:122 -msgid "Shutdown" -msgstr "シャットダウン" - -#: ../vnc.html:123 -msgid "Reboot" -msgstr "再起動" - -#: ../vnc.html:124 -msgid "Reset" -msgstr "リセット" - -#: ../vnc.html:129 ../vnc.html:135 -msgid "Clipboard" -msgstr "クリップボード" - -#: ../vnc.html:137 -msgid "Edit clipboard content in the textarea below." -msgstr "以下の入力欄からクリップボードの内容を編集できます。" - -#: ../vnc.html:145 -msgid "Full Screen" -msgstr "全画面表示" - -#: ../vnc.html:150 ../vnc.html:156 -msgid "Settings" -msgstr "設定" - -#: ../vnc.html:160 -msgid "Shared Mode" -msgstr "共有モード" - -#: ../vnc.html:163 -msgid "View Only" -msgstr "表示専用" - -#: ../vnc.html:167 -msgid "Clip to Window" -msgstr "ウィンドウにクリップ" - -#: ../vnc.html:170 -msgid "Scaling Mode:" -msgstr "スケーリングモード:" - -#: ../vnc.html:172 -msgid "None" -msgstr "なし" - -#: ../vnc.html:173 -msgid "Local Scaling" -msgstr "ローカルスケーリング" - -#: ../vnc.html:174 -msgid "Remote Resizing" -msgstr "リモートでリサイズ" - -#: ../vnc.html:179 -msgid "Advanced" -msgstr "高度" - -#: ../vnc.html:182 -msgid "Quality:" -msgstr "品質:" - -#: ../vnc.html:186 -msgid "Compression level:" -msgstr "圧縮レベル:" - -#: ../vnc.html:191 -msgid "Repeater ID:" -msgstr "リピーター ID:" - -#: ../vnc.html:195 -msgid "WebSocket" -msgstr "WebSocket" - -#: ../vnc.html:198 -msgid "Encrypt" -msgstr "暗号化" - -#: ../vnc.html:201 -msgid "Host:" -msgstr "ホスト:" - -#: ../vnc.html:205 -msgid "Port:" -msgstr "ポート:" - -#: ../vnc.html:209 -msgid "Path:" -msgstr "パス:" - -#: ../vnc.html:216 -msgid "Automatic Reconnect" -msgstr "自動再接続" - -#: ../vnc.html:219 -msgid "Reconnect Delay (ms):" -msgstr "再接続する遅延 (ミリ秒):" - -#: ../vnc.html:224 -msgid "Show Dot when No Cursor" -msgstr "カーソルがないときにドットを表示する" - -#: ../vnc.html:229 -msgid "Logging:" -msgstr "ロギング:" - -#: ../vnc.html:238 -msgid "Version:" -msgstr "バージョン:" - -#: ../vnc.html:246 -msgid "Disconnect" -msgstr "切断" - -#: ../vnc.html:269 -msgid "Connect" -msgstr "接続" - -#: ../vnc.html:278 -msgid "Server identity" -msgstr "サーバーの識別情報" - -#: ../vnc.html:281 -msgid "The server has provided the following identifying information:" -msgstr "サーバーは以下の識別情報を提供しています:" - -#: ../vnc.html:285 -msgid "Fingerprint:" -msgstr "フィンガープリント:" - -#: ../vnc.html:288 -msgid "" -"Please verify that the information is correct and press \"Approve\". " -"Otherwise press \"Reject\"." -msgstr "" -"この情報が正しい場合は「承認」を、そうでない場合は「拒否」を押してく" -"ださい。" - -#: ../vnc.html:293 -msgid "Approve" -msgstr "承認" - -#: ../vnc.html:294 -msgid "Reject" -msgstr "拒否" - -#: ../vnc.html:302 -msgid "Credentials" -msgstr "資格情報" - -#: ../vnc.html:306 -msgid "Username:" -msgstr "ユーザー名:" - -#: ../vnc.html:310 -msgid "Password:" -msgstr "パスワード:" - -#: ../vnc.html:314 -msgid "Send Credentials" -msgstr "資格情報を送信" - -#: ../vnc.html:323 -msgid "Cancel" -msgstr "キャンセル" - -#~ msgid "Clear" -#~ msgstr "クリア" - -#~ msgid "Password is required" -#~ msgstr "パスワードが必要です" - -#~ msgid "viewport drag" -#~ msgstr "ビューポートをドラッグ" - -#~ msgid "Active Mouse Button" -#~ msgstr "アクティブなマウスボタン" - -#~ msgid "No mousebutton" -#~ msgstr "マウスボタンなし" - -#~ msgid "Left mousebutton" -#~ msgstr "左マウスボタン" - -#~ msgid "Middle mousebutton" -#~ msgstr "中マウスボタン" - -#~ msgid "Right mousebutton" -#~ msgstr "右マウスボタン" - -#~ msgid "Send Password" -#~ msgstr "パスワードを送信" diff --git a/base/app/novnc/po/ko.po b/base/app/novnc/po/ko.po deleted file mode 100644 index 87ae106..0000000 --- a/base/app/novnc/po/ko.po +++ /dev/null @@ -1,290 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) 2018 The noVNC Authors -# This file is distributed under the same license as the noVNC package. -# Baw Appie <pp121324@gmail.com>, 2018. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: noVNC 1.0.0-testing.2\n" -"Report-Msgid-Bugs-To: novnc@googlegroups.com\n" -"POT-Creation-Date: 2018-01-31 16:29+0100\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Baw Appie <pp121324@gmail.com>\n" -"Language-Team: Korean\n" -"Language: ko\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - -#: ../app/ui.js:395 -msgid "Connecting..." -msgstr "연결중..." - -#: ../app/ui.js:402 -msgid "Disconnecting..." -msgstr "연결 해제중..." - -#: ../app/ui.js:408 -msgid "Reconnecting..." -msgstr "재연결중..." - -#: ../app/ui.js:413 -msgid "Internal error" -msgstr "내부 오류" - -#: ../app/ui.js:1002 -msgid "Must set host" -msgstr "호스트는 설정되어야 합니다." - -#: ../app/ui.js:1083 -msgid "Connected (encrypted) to " -msgstr "다음과 (암호화되어) 연결되었습니다:" - -#: ../app/ui.js:1085 -msgid "Connected (unencrypted) to " -msgstr "다음과 (암호화 없이) 연결되었습니다:" - -#: ../app/ui.js:1108 -msgid "Something went wrong, connection is closed" -msgstr "무언가 잘못되었습니다, 연결이 닫혔습니다." - -#: ../app/ui.js:1111 -msgid "Failed to connect to server" -msgstr "서버에 연결하지 못했습니다." - -#: ../app/ui.js:1121 -msgid "Disconnected" -msgstr "연결이 해제되었습니다." - -#: ../app/ui.js:1134 -msgid "New connection has been rejected with reason: " -msgstr "새 연결이 다음 이유로 거부되었습니다:" - -#: ../app/ui.js:1137 -msgid "New connection has been rejected" -msgstr "새 연결이 거부되었습니다." - -#: ../app/ui.js:1158 -msgid "Password is required" -msgstr "비밀번호가 필요합니다." - -#: ../vnc.html:91 -msgid "noVNC encountered an error:" -msgstr "noVNC에 오류가 발생했습니다:" - -#: ../vnc.html:101 -msgid "Hide/Show the control bar" -msgstr "컨트롤 바 숨기기/보이기" - -#: ../vnc.html:108 -msgid "Move/Drag Viewport" -msgstr "움직이기/드래그 뷰포트" - -#: ../vnc.html:108 -msgid "viewport drag" -msgstr "뷰포트 드래그" - -#: ../vnc.html:114 ../vnc.html:117 ../vnc.html:120 ../vnc.html:123 -msgid "Active Mouse Button" -msgstr "마우스 버튼 활성화" - -#: ../vnc.html:114 -msgid "No mousebutton" -msgstr "마우스 버튼 없음" - -#: ../vnc.html:117 -msgid "Left mousebutton" -msgstr "왼쪽 마우스 버튼" - -#: ../vnc.html:120 -msgid "Middle mousebutton" -msgstr "중간 마우스 버튼" - -#: ../vnc.html:123 -msgid "Right mousebutton" -msgstr "오른쪽 마우스 버튼" - -#: ../vnc.html:126 -msgid "Keyboard" -msgstr "키보드" - -#: ../vnc.html:126 -msgid "Show Keyboard" -msgstr "키보드 보이기" - -#: ../vnc.html:133 -msgid "Extra keys" -msgstr "기타 키들" - -#: ../vnc.html:133 -msgid "Show Extra Keys" -msgstr "기타 키들 보이기" - -#: ../vnc.html:138 -msgid "Ctrl" -msgstr "Ctrl" - -#: ../vnc.html:138 -msgid "Toggle Ctrl" -msgstr "Ctrl 켜기/끄기" - -#: ../vnc.html:141 -msgid "Alt" -msgstr "Alt" - -#: ../vnc.html:141 -msgid "Toggle Alt" -msgstr "Alt 켜기/끄기" - -#: ../vnc.html:144 -msgid "Send Tab" -msgstr "Tab 보내기" - -#: ../vnc.html:144 -msgid "Tab" -msgstr "Tab" - -#: ../vnc.html:147 -msgid "Esc" -msgstr "Esc" - -#: ../vnc.html:147 -msgid "Send Escape" -msgstr "Esc 보내기" - -#: ../vnc.html:150 -msgid "Ctrl+Alt+Del" -msgstr "Ctrl+Alt+Del" - -#: ../vnc.html:150 -msgid "Send Ctrl-Alt-Del" -msgstr "Ctrl+Alt+Del 보내기" - -#: ../vnc.html:158 -msgid "Shutdown/Reboot" -msgstr "셧다운/리붓" - -#: ../vnc.html:158 -msgid "Shutdown/Reboot..." -msgstr "셧다운/리붓..." - -#: ../vnc.html:164 -msgid "Power" -msgstr "전원" - -#: ../vnc.html:166 -msgid "Shutdown" -msgstr "셧다운" - -#: ../vnc.html:167 -msgid "Reboot" -msgstr "리붓" - -#: ../vnc.html:168 -msgid "Reset" -msgstr "리셋" - -#: ../vnc.html:173 ../vnc.html:179 -msgid "Clipboard" -msgstr "클립보드" - -#: ../vnc.html:183 -msgid "Clear" -msgstr "지우기" - -#: ../vnc.html:189 -msgid "Fullscreen" -msgstr "전체화면" - -#: ../vnc.html:194 ../vnc.html:201 -msgid "Settings" -msgstr "설정" - -#: ../vnc.html:204 -msgid "Shared Mode" -msgstr "공유 모드" - -#: ../vnc.html:207 -msgid "View Only" -msgstr "보기 전용" - -#: ../vnc.html:211 -msgid "Clip to Window" -msgstr "창에 클립" - -#: ../vnc.html:214 -msgid "Scaling Mode:" -msgstr "스케일링 모드:" - -#: ../vnc.html:216 -msgid "None" -msgstr "없음" - -#: ../vnc.html:217 -msgid "Local Scaling" -msgstr "로컬 스케일링" - -#: ../vnc.html:218 -msgid "Remote Resizing" -msgstr "원격 크기 조절" - -#: ../vnc.html:223 -msgid "Advanced" -msgstr "고급" - -#: ../vnc.html:226 -msgid "Repeater ID:" -msgstr "중계 ID" - -#: ../vnc.html:230 -msgid "WebSocket" -msgstr "웹소켓" - -#: ../vnc.html:233 -msgid "Encrypt" -msgstr "암호화" - -#: ../vnc.html:236 -msgid "Host:" -msgstr "호스트:" - -#: ../vnc.html:240 -msgid "Port:" -msgstr "포트:" - -#: ../vnc.html:244 -msgid "Path:" -msgstr "위치:" - -#: ../vnc.html:251 -msgid "Automatic Reconnect" -msgstr "자동 재연결" - -#: ../vnc.html:254 -msgid "Reconnect Delay (ms):" -msgstr "재연결 지연 시간 (ms)" - -#: ../vnc.html:260 -msgid "Logging:" -msgstr "로깅" - -#: ../vnc.html:272 -msgid "Disconnect" -msgstr "연결 해제" - -#: ../vnc.html:291 -msgid "Connect" -msgstr "연결" - -#: ../vnc.html:301 -msgid "Password:" -msgstr "비밀번호:" - -#: ../vnc.html:305 -msgid "Send Password" -msgstr "비밀번호 전송" - -#: ../vnc.html:315 -msgid "Cancel" -msgstr "취소" diff --git a/base/app/novnc/po/nl.po b/base/app/novnc/po/nl.po deleted file mode 100644 index 343204a..0000000 --- a/base/app/novnc/po/nl.po +++ /dev/null @@ -1,322 +0,0 @@ -# Dutch translations for noVNC package -# Nederlandse vertalingen voor het pakket noVNC. -# Copyright (C) 2018 The noVNC Authors -# This file is distributed under the same license as the noVNC package. -# Loek Janssen <loekjanssen@gmail.com>, 2016. -# -msgid "" -msgstr "" -"Project-Id-Version: noVNC 1.1.0\n" -"Report-Msgid-Bugs-To: novnc@googlegroups.com\n" -"POT-Creation-Date: 2019-04-09 11:06+0100\n" -"PO-Revision-Date: 2019-04-09 17:17+0100\n" -"Last-Translator: Arend Lapere <arend.lapere@gmail.com>\n" -"Language-Team: none\n" -"Language: nl\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: ../app/ui.js:383 -msgid "Connecting..." -msgstr "Verbinden..." - -#: ../app/ui.js:390 -msgid "Disconnecting..." -msgstr "Verbinding verbreken..." - -#: ../app/ui.js:396 -msgid "Reconnecting..." -msgstr "Opnieuw verbinding maken..." - -#: ../app/ui.js:401 -msgid "Internal error" -msgstr "Interne fout" - -#: ../app/ui.js:991 -msgid "Must set host" -msgstr "Host moeten worden ingesteld" - -#: ../app/ui.js:1073 -msgid "Connected (encrypted) to " -msgstr "Verbonden (versleuteld) met " - -#: ../app/ui.js:1075 -msgid "Connected (unencrypted) to " -msgstr "Verbonden (onversleuteld) met " - -#: ../app/ui.js:1098 -msgid "Something went wrong, connection is closed" -msgstr "Er iets fout gelopen, verbinding werd verbroken" - -#: ../app/ui.js:1101 -msgid "Failed to connect to server" -msgstr "Verbinding maken met server is mislukt" - -#: ../app/ui.js:1111 -msgid "Disconnected" -msgstr "Verbinding verbroken" - -#: ../app/ui.js:1124 -msgid "New connection has been rejected with reason: " -msgstr "Nieuwe verbinding is geweigerd omwille van de volgende reden: " - -#: ../app/ui.js:1127 -msgid "New connection has been rejected" -msgstr "Nieuwe verbinding is geweigerd" - -#: ../app/ui.js:1147 -msgid "Password is required" -msgstr "Wachtwoord is vereist" - -#: ../vnc.html:80 -msgid "noVNC encountered an error:" -msgstr "noVNC heeft een fout bemerkt:" - -#: ../vnc.html:90 -msgid "Hide/Show the control bar" -msgstr "Verberg/Toon de bedieningsbalk" - -#: ../vnc.html:97 -msgid "Move/Drag Viewport" -msgstr "Verplaats/Versleep Kijkvenster" - -#: ../vnc.html:97 -msgid "viewport drag" -msgstr "kijkvenster slepen" - -#: ../vnc.html:103 ../vnc.html:106 ../vnc.html:109 ../vnc.html:112 -msgid "Active Mouse Button" -msgstr "Actieve Muisknop" - -#: ../vnc.html:103 -msgid "No mousebutton" -msgstr "Geen muisknop" - -#: ../vnc.html:106 -msgid "Left mousebutton" -msgstr "Linker muisknop" - -#: ../vnc.html:109 -msgid "Middle mousebutton" -msgstr "Middelste muisknop" - -#: ../vnc.html:112 -msgid "Right mousebutton" -msgstr "Rechter muisknop" - -#: ../vnc.html:115 -msgid "Keyboard" -msgstr "Toetsenbord" - -#: ../vnc.html:115 -msgid "Show Keyboard" -msgstr "Toon Toetsenbord" - -#: ../vnc.html:121 -msgid "Extra keys" -msgstr "Extra toetsen" - -#: ../vnc.html:121 -msgid "Show Extra Keys" -msgstr "Toon Extra Toetsen" - -#: ../vnc.html:126 -msgid "Ctrl" -msgstr "Ctrl" - -#: ../vnc.html:126 -msgid "Toggle Ctrl" -msgstr "Ctrl omschakelen" - -#: ../vnc.html:129 -msgid "Alt" -msgstr "Alt" - -#: ../vnc.html:129 -msgid "Toggle Alt" -msgstr "Alt omschakelen" - -#: ../vnc.html:132 -msgid "Toggle Windows" -msgstr "Windows omschakelen" - -#: ../vnc.html:132 -msgid "Windows" -msgstr "Windows" - -#: ../vnc.html:135 -msgid "Send Tab" -msgstr "Tab Sturen" - -#: ../vnc.html:135 -msgid "Tab" -msgstr "Tab" - -#: ../vnc.html:138 -msgid "Esc" -msgstr "Esc" - -#: ../vnc.html:138 -msgid "Send Escape" -msgstr "Escape Sturen" - -#: ../vnc.html:141 -msgid "Ctrl+Alt+Del" -msgstr "Ctrl-Alt-Del" - -#: ../vnc.html:141 -msgid "Send Ctrl-Alt-Del" -msgstr "Ctrl-Alt-Del Sturen" - -#: ../vnc.html:149 -msgid "Shutdown/Reboot" -msgstr "Uitschakelen/Herstarten" - -#: ../vnc.html:149 -msgid "Shutdown/Reboot..." -msgstr "Uitschakelen/Herstarten..." - -#: ../vnc.html:155 -msgid "Power" -msgstr "Systeem" - -#: ../vnc.html:157 -msgid "Shutdown" -msgstr "Uitschakelen" - -#: ../vnc.html:158 -msgid "Reboot" -msgstr "Herstarten" - -#: ../vnc.html:159 -msgid "Reset" -msgstr "Resetten" - -#: ../vnc.html:164 ../vnc.html:170 -msgid "Clipboard" -msgstr "Klembord" - -#: ../vnc.html:174 -msgid "Clear" -msgstr "Wissen" - -#: ../vnc.html:180 -msgid "Fullscreen" -msgstr "Volledig Scherm" - -#: ../vnc.html:185 ../vnc.html:192 -msgid "Settings" -msgstr "Instellingen" - -#: ../vnc.html:195 -msgid "Shared Mode" -msgstr "Gedeelde Modus" - -#: ../vnc.html:198 -msgid "View Only" -msgstr "Alleen Kijken" - -#: ../vnc.html:202 -msgid "Clip to Window" -msgstr "Randen buiten venster afsnijden" - -#: ../vnc.html:205 -msgid "Scaling Mode:" -msgstr "Schaalmodus:" - -#: ../vnc.html:207 -msgid "None" -msgstr "Geen" - -#: ../vnc.html:208 -msgid "Local Scaling" -msgstr "Lokaal Schalen" - -#: ../vnc.html:209 -msgid "Remote Resizing" -msgstr "Op Afstand Formaat Wijzigen" - -#: ../vnc.html:214 -msgid "Advanced" -msgstr "Geavanceerd" - -#: ../vnc.html:217 -msgid "Repeater ID:" -msgstr "Repeater ID:" - -#: ../vnc.html:221 -msgid "WebSocket" -msgstr "WebSocket" - -#: ../vnc.html:224 -msgid "Encrypt" -msgstr "Versleutelen" - -#: ../vnc.html:227 -msgid "Host:" -msgstr "Host:" - -#: ../vnc.html:231 -msgid "Port:" -msgstr "Poort:" - -#: ../vnc.html:235 -msgid "Path:" -msgstr "Pad:" - -#: ../vnc.html:242 -msgid "Automatic Reconnect" -msgstr "Automatisch Opnieuw Verbinden" - -#: ../vnc.html:245 -msgid "Reconnect Delay (ms):" -msgstr "Vertraging voor Opnieuw Verbinden (ms):" - -#: ../vnc.html:250 -msgid "Show Dot when No Cursor" -msgstr "Geef stip weer indien geen cursor" - -#: ../vnc.html:255 -msgid "Logging:" -msgstr "Logmeldingen:" - -#: ../vnc.html:267 -msgid "Disconnect" -msgstr "Verbinding verbreken" - -#: ../vnc.html:286 -msgid "Connect" -msgstr "Verbinden" - -#: ../vnc.html:296 -msgid "Password:" -msgstr "Wachtwoord:" - -#: ../vnc.html:300 -msgid "Send Password" -msgstr "Verzend Wachtwoord:" - -#: ../vnc.html:310 -msgid "Cancel" -msgstr "Annuleren" - -#~ msgid "Disconnect timeout" -#~ msgstr "Timeout tijdens verbreken van verbinding" - -#~ msgid "Local Downscaling" -#~ msgstr "Lokaal Neerschalen" - -#~ msgid "Local Cursor" -#~ msgstr "Lokale Cursor" - -#~ msgid "Canvas not supported." -#~ msgstr "Canvas wordt niet ondersteund." - -#~ msgid "" -#~ "Forcing clipping mode since scrollbars aren't supported by IE in " -#~ "fullscreen" -#~ msgstr "" -#~ "''Clipping mode' ingeschakeld, omdat schuifbalken in volledige-scherm-" -#~ "modus in IE niet worden ondersteund" diff --git a/base/app/novnc/po/noVNC.pot b/base/app/novnc/po/noVNC.pot deleted file mode 100644 index 0f85a82..0000000 --- a/base/app/novnc/po/noVNC.pot +++ /dev/null @@ -1,337 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR The noVNC Authors -# This file is distributed under the same license as the noVNC package. -# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: noVNC 1.5.0\n" -"Report-Msgid-Bugs-To: novnc@googlegroups.com\n" -"POT-Creation-Date: 2024-06-03 14:10+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" -"Language-Team: LANGUAGE <LL@li.org>\n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" -"Content-Transfer-Encoding: 8bit\n" - -#: ../app/ui.js:69 -msgid "" -"Running without HTTPS is not recommended, crashes or other issues are likely." -msgstr "" - -#: ../app/ui.js:410 -msgid "Connecting..." -msgstr "" - -#: ../app/ui.js:417 -msgid "Disconnecting..." -msgstr "" - -#: ../app/ui.js:423 -msgid "Reconnecting..." -msgstr "" - -#: ../app/ui.js:428 -msgid "Internal error" -msgstr "" - -#: ../app/ui.js:1026 -msgid "Must set host" -msgstr "" - -#: ../app/ui.js:1052 -msgid "Failed to connect to server: " -msgstr "" - -#: ../app/ui.js:1118 -msgid "Connected (encrypted) to " -msgstr "" - -#: ../app/ui.js:1120 -msgid "Connected (unencrypted) to " -msgstr "" - -#: ../app/ui.js:1143 -msgid "Something went wrong, connection is closed" -msgstr "" - -#: ../app/ui.js:1146 -msgid "Failed to connect to server" -msgstr "" - -#: ../app/ui.js:1158 -msgid "Disconnected" -msgstr "" - -#: ../app/ui.js:1173 -msgid "New connection has been rejected with reason: " -msgstr "" - -#: ../app/ui.js:1176 -msgid "New connection has been rejected" -msgstr "" - -#: ../app/ui.js:1242 -msgid "Credentials are required" -msgstr "" - -#: ../vnc.html:55 -msgid "noVNC encountered an error:" -msgstr "" - -#: ../vnc.html:65 -msgid "Hide/Show the control bar" -msgstr "" - -#: ../vnc.html:74 -msgid "Drag" -msgstr "" - -#: ../vnc.html:74 -msgid "Move/Drag Viewport" -msgstr "" - -#: ../vnc.html:80 -msgid "Keyboard" -msgstr "" - -#: ../vnc.html:80 -msgid "Show Keyboard" -msgstr "" - -#: ../vnc.html:85 -msgid "Extra keys" -msgstr "" - -#: ../vnc.html:85 -msgid "Show Extra Keys" -msgstr "" - -#: ../vnc.html:90 -msgid "Ctrl" -msgstr "" - -#: ../vnc.html:90 -msgid "Toggle Ctrl" -msgstr "" - -#: ../vnc.html:93 -msgid "Alt" -msgstr "" - -#: ../vnc.html:93 -msgid "Toggle Alt" -msgstr "" - -#: ../vnc.html:96 -msgid "Toggle Windows" -msgstr "" - -#: ../vnc.html:96 -msgid "Windows" -msgstr "" - -#: ../vnc.html:99 -msgid "Send Tab" -msgstr "" - -#: ../vnc.html:99 -msgid "Tab" -msgstr "" - -#: ../vnc.html:102 -msgid "Esc" -msgstr "" - -#: ../vnc.html:102 -msgid "Send Escape" -msgstr "" - -#: ../vnc.html:105 -msgid "Ctrl+Alt+Del" -msgstr "" - -#: ../vnc.html:105 -msgid "Send Ctrl-Alt-Del" -msgstr "" - -#: ../vnc.html:112 -msgid "Shutdown/Reboot" -msgstr "" - -#: ../vnc.html:112 -msgid "Shutdown/Reboot..." -msgstr "" - -#: ../vnc.html:118 -msgid "Power" -msgstr "" - -#: ../vnc.html:120 -msgid "Shutdown" -msgstr "" - -#: ../vnc.html:121 -msgid "Reboot" -msgstr "" - -#: ../vnc.html:122 -msgid "Reset" -msgstr "" - -#: ../vnc.html:127 ../vnc.html:133 -msgid "Clipboard" -msgstr "" - -#: ../vnc.html:135 -msgid "Edit clipboard content in the textarea below." -msgstr "" - -#: ../vnc.html:143 -msgid "Full Screen" -msgstr "" - -#: ../vnc.html:148 ../vnc.html:154 -msgid "Settings" -msgstr "" - -#: ../vnc.html:158 -msgid "Shared Mode" -msgstr "" - -#: ../vnc.html:161 -msgid "View Only" -msgstr "" - -#: ../vnc.html:165 -msgid "Clip to Window" -msgstr "" - -#: ../vnc.html:168 -msgid "Scaling Mode:" -msgstr "" - -#: ../vnc.html:170 -msgid "None" -msgstr "" - -#: ../vnc.html:171 -msgid "Local Scaling" -msgstr "" - -#: ../vnc.html:172 -msgid "Remote Resizing" -msgstr "" - -#: ../vnc.html:177 -msgid "Advanced" -msgstr "" - -#: ../vnc.html:180 -msgid "Quality:" -msgstr "" - -#: ../vnc.html:184 -msgid "Compression level:" -msgstr "" - -#: ../vnc.html:189 -msgid "Repeater ID:" -msgstr "" - -#: ../vnc.html:193 -msgid "WebSocket" -msgstr "" - -#: ../vnc.html:196 -msgid "Encrypt" -msgstr "" - -#: ../vnc.html:199 -msgid "Host:" -msgstr "" - -#: ../vnc.html:203 -msgid "Port:" -msgstr "" - -#: ../vnc.html:207 -msgid "Path:" -msgstr "" - -#: ../vnc.html:214 -msgid "Automatic Reconnect" -msgstr "" - -#: ../vnc.html:217 -msgid "Reconnect Delay (ms):" -msgstr "" - -#: ../vnc.html:222 -msgid "Show Dot when No Cursor" -msgstr "" - -#: ../vnc.html:227 -msgid "Logging:" -msgstr "" - -#: ../vnc.html:236 -msgid "Version:" -msgstr "" - -#: ../vnc.html:244 -msgid "Disconnect" -msgstr "" - -#: ../vnc.html:267 -msgid "Connect" -msgstr "" - -#: ../vnc.html:276 -msgid "Server identity" -msgstr "" - -#: ../vnc.html:279 -msgid "The server has provided the following identifying information:" -msgstr "" - -#: ../vnc.html:283 -msgid "Fingerprint:" -msgstr "" - -#: ../vnc.html:286 -msgid "" -"Please verify that the information is correct and press \"Approve\". " -"Otherwise press \"Reject\"." -msgstr "" - -#: ../vnc.html:291 -msgid "Approve" -msgstr "" - -#: ../vnc.html:292 -msgid "Reject" -msgstr "" - -#: ../vnc.html:300 -msgid "Credentials" -msgstr "" - -#: ../vnc.html:304 -msgid "Username:" -msgstr "" - -#: ../vnc.html:308 -msgid "Password:" -msgstr "" - -#: ../vnc.html:312 -msgid "Send Credentials" -msgstr "" - -#: ../vnc.html:321 -msgid "Cancel" -msgstr "" diff --git a/base/app/novnc/po/pl.po b/base/app/novnc/po/pl.po deleted file mode 100644 index 5acfdc4..0000000 --- a/base/app/novnc/po/pl.po +++ /dev/null @@ -1,325 +0,0 @@ -# Polish translations for noVNC package. -# Copyright (C) 2018 The noVNC Authors -# This file is distributed under the same license as the noVNC package. -# Mariusz Jamro <mariusz.jamro@gmail.com>, 2017. -# -msgid "" -msgstr "" -"Project-Id-Version: noVNC 0.6.1\n" -"Report-Msgid-Bugs-To: novnc@googlegroups.com\n" -"POT-Creation-Date: 2017-11-21 19:53+0100\n" -"PO-Revision-Date: 2017-11-21 19:54+0100\n" -"Last-Translator: Mariusz Jamro <mariusz.jamro@gmail.com>\n" -"Language-Team: Polish\n" -"Language: pl\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " -"|| n%100>=20) ? 1 : 2);\n" -"X-Generator: Poedit 2.0.1\n" - -#: ../app/ui.js:404 -msgid "Connecting..." -msgstr "Łączenie..." - -#: ../app/ui.js:411 -msgid "Disconnecting..." -msgstr "Rozłączanie..." - -#: ../app/ui.js:417 -msgid "Reconnecting..." -msgstr "Łączenie..." - -#: ../app/ui.js:422 -msgid "Internal error" -msgstr "Błąd wewnętrzny" - -#: ../app/ui.js:1019 -msgid "Must set host" -msgstr "Host i port są wymagane" - -#: ../app/ui.js:1099 -msgid "Connected (encrypted) to " -msgstr "Połączenie (szyfrowane) z " - -#: ../app/ui.js:1101 -msgid "Connected (unencrypted) to " -msgstr "Połączenie (nieszyfrowane) z " - -#: ../app/ui.js:1119 -msgid "Something went wrong, connection is closed" -msgstr "Coś poszło źle, połączenie zostało zamknięte" - -#: ../app/ui.js:1129 -msgid "Disconnected" -msgstr "Rozłączony" - -#: ../app/ui.js:1142 -msgid "New connection has been rejected with reason: " -msgstr "Nowe połączenie zostało odrzucone z powodu: " - -#: ../app/ui.js:1145 -msgid "New connection has been rejected" -msgstr "Nowe połączenie zostało odrzucone" - -#: ../app/ui.js:1166 -msgid "Password is required" -msgstr "Hasło jest wymagane" - -#: ../vnc.html:89 -msgid "noVNC encountered an error:" -msgstr "noVNC napotkało błąd:" - -#: ../vnc.html:99 -msgid "Hide/Show the control bar" -msgstr "Pokaż/Ukryj pasek ustawień" - -#: ../vnc.html:106 -msgid "Move/Drag Viewport" -msgstr "Ruszaj/Przeciągaj Viewport" - -#: ../vnc.html:106 -msgid "viewport drag" -msgstr "przeciągnij viewport" - -#: ../vnc.html:112 ../vnc.html:115 ../vnc.html:118 ../vnc.html:121 -msgid "Active Mouse Button" -msgstr "Aktywny Przycisk Myszy" - -#: ../vnc.html:112 -msgid "No mousebutton" -msgstr "Brak przycisku myszy" - -#: ../vnc.html:115 -msgid "Left mousebutton" -msgstr "Lewy przycisk myszy" - -#: ../vnc.html:118 -msgid "Middle mousebutton" -msgstr "Środkowy przycisk myszy" - -#: ../vnc.html:121 -msgid "Right mousebutton" -msgstr "Prawy przycisk myszy" - -#: ../vnc.html:124 -msgid "Keyboard" -msgstr "Klawiatura" - -#: ../vnc.html:124 -msgid "Show Keyboard" -msgstr "Pokaż klawiaturę" - -#: ../vnc.html:131 -msgid "Extra keys" -msgstr "Przyciski dodatkowe" - -#: ../vnc.html:131 -msgid "Show Extra Keys" -msgstr "Pokaż przyciski dodatkowe" - -#: ../vnc.html:136 -msgid "Ctrl" -msgstr "Ctrl" - -#: ../vnc.html:136 -msgid "Toggle Ctrl" -msgstr "Przełącz Ctrl" - -#: ../vnc.html:139 -msgid "Alt" -msgstr "Alt" - -#: ../vnc.html:139 -msgid "Toggle Alt" -msgstr "Przełącz Alt" - -#: ../vnc.html:142 -msgid "Send Tab" -msgstr "Wyślij Tab" - -#: ../vnc.html:142 -msgid "Tab" -msgstr "Tab" - -#: ../vnc.html:145 -msgid "Esc" -msgstr "Esc" - -#: ../vnc.html:145 -msgid "Send Escape" -msgstr "Wyślij Escape" - -#: ../vnc.html:148 -msgid "Ctrl+Alt+Del" -msgstr "Ctrl+Alt+Del" - -#: ../vnc.html:148 -msgid "Send Ctrl-Alt-Del" -msgstr "Wyślij Ctrl-Alt-Del" - -#: ../vnc.html:156 -msgid "Shutdown/Reboot" -msgstr "Wyłącz/Uruchom ponownie" - -#: ../vnc.html:156 -msgid "Shutdown/Reboot..." -msgstr "Wyłącz/Uruchom ponownie..." - -#: ../vnc.html:162 -msgid "Power" -msgstr "Włączony" - -#: ../vnc.html:164 -msgid "Shutdown" -msgstr "Wyłącz" - -#: ../vnc.html:165 -msgid "Reboot" -msgstr "Uruchom ponownie" - -#: ../vnc.html:166 -msgid "Reset" -msgstr "Resetuj" - -#: ../vnc.html:171 ../vnc.html:177 -msgid "Clipboard" -msgstr "Schowek" - -#: ../vnc.html:181 -msgid "Clear" -msgstr "Wyczyść" - -#: ../vnc.html:187 -msgid "Fullscreen" -msgstr "Pełny ekran" - -#: ../vnc.html:192 ../vnc.html:199 -msgid "Settings" -msgstr "Ustawienia" - -#: ../vnc.html:202 -msgid "Shared Mode" -msgstr "Tryb Współdzielenia" - -#: ../vnc.html:205 -msgid "View Only" -msgstr "Tylko Podgląd" - -#: ../vnc.html:209 -msgid "Clip to Window" -msgstr "Przytnij do Okna" - -#: ../vnc.html:212 -msgid "Scaling Mode:" -msgstr "Tryb Skalowania:" - -#: ../vnc.html:214 -msgid "None" -msgstr "Brak" - -#: ../vnc.html:215 -msgid "Local Scaling" -msgstr "Skalowanie lokalne" - -#: ../vnc.html:216 -msgid "Remote Resizing" -msgstr "Skalowanie zdalne" - -#: ../vnc.html:221 -msgid "Advanced" -msgstr "Zaawansowane" - -#: ../vnc.html:224 -msgid "Repeater ID:" -msgstr "ID Repeatera:" - -#: ../vnc.html:228 -msgid "WebSocket" -msgstr "WebSocket" - -#: ../vnc.html:231 -msgid "Encrypt" -msgstr "Szyfrowanie" - -#: ../vnc.html:234 -msgid "Host:" -msgstr "Host:" - -#: ../vnc.html:238 -msgid "Port:" -msgstr "Port:" - -#: ../vnc.html:242 -msgid "Path:" -msgstr "Ścieżka:" - -#: ../vnc.html:249 -msgid "Automatic Reconnect" -msgstr "Automatycznie wznawiaj połączenie" - -#: ../vnc.html:252 -msgid "Reconnect Delay (ms):" -msgstr "Opóźnienie wznawiania (ms):" - -#: ../vnc.html:258 -msgid "Logging:" -msgstr "Poziom logowania:" - -#: ../vnc.html:270 -msgid "Disconnect" -msgstr "Rozłącz" - -#: ../vnc.html:289 -msgid "Connect" -msgstr "Połącz" - -#: ../vnc.html:299 -msgid "Password:" -msgstr "Hasło:" - -#: ../vnc.html:313 -msgid "Cancel" -msgstr "Anuluj" - -#: ../vnc.html:329 -msgid "Canvas not supported." -msgstr "Element Canvas nie jest wspierany." - -#~ msgid "Disconnect timeout" -#~ msgstr "Timeout rozłączenia" - -#~ msgid "Local Downscaling" -#~ msgstr "Downscaling lokalny" - -#~ msgid "Local Cursor" -#~ msgstr "Lokalny kursor" - -#~ msgid "" -#~ "Forcing clipping mode since scrollbars aren't supported by IE in " -#~ "fullscreen" -#~ msgstr "" -#~ "Wymuszam clipping mode ponieważ paski przewijania nie są wspierane przez " -#~ "IE w trybie pełnoekranowym" - -#~ msgid "True Color" -#~ msgstr "True Color" - -#~ msgid "Style:" -#~ msgstr "Styl:" - -#~ msgid "default" -#~ msgstr "domyślny" - -#~ msgid "Apply" -#~ msgstr "Zapisz" - -#~ msgid "Connection" -#~ msgstr "Połączenie" - -#~ msgid "Token:" -#~ msgstr "Token:" - -#~ msgid "Send Password" -#~ msgstr "Wyślij Hasło" diff --git a/base/app/novnc/po/po2js b/base/app/novnc/po/po2js deleted file mode 100755 index e293ded..0000000 --- a/base/app/novnc/po/po2js +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env node -/* - * ps2js: gettext .po to noVNC .js converter - * Copyright (C) 2018 The noVNC Authors - * - * This program 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 3 of the License, or - * (at your option) any later version. - * - * This program 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 program. If not, see <http://www.gnu.org/licenses/>. - */ - -const getopt = require('node-getopt'); -const fs = require('fs'); -const po2json = require("po2json"); - -const opt = getopt.create([ - ['h', 'help', 'display this help'], -]).bindHelp().parseSystem(); - -if (opt.argv.length != 2) { - console.error("Incorrect number of arguments given"); - process.exit(1); -} - -const data = po2json.parseFileSync(opt.argv[0]); - -const bodyPart = Object.keys(data) - .filter(msgid => msgid !== "") - .filter(msgid => data[msgid][1] !== "") - .map((msgid) => { - const msgstr = data[msgid][1]; - return " " + JSON.stringify(msgid) + ": " + JSON.stringify(msgstr); - }).join(",\n"); - -const output = "{\n" + bodyPart + "\n}"; - -fs.writeFileSync(opt.argv[1], output); diff --git a/base/app/novnc/po/pt_BR.po b/base/app/novnc/po/pt_BR.po deleted file mode 100644 index 77951ae..0000000 --- a/base/app/novnc/po/pt_BR.po +++ /dev/null @@ -1,299 +0,0 @@ -# Portuguese translations for noVNC package. -# Copyright (C) 2021 The noVNC Authors -# This file is distributed under the same license as the noVNC package. -# <liddack@outlook.com>, 2021. -# -msgid "" -msgstr "" -"Project-Id-Version: noVNC 1.2.0\n" -"Report-Msgid-Bugs-To: novnc@googlegroups.com\n" -"POT-Creation-Date: 2021-03-15 21:55-0300\n" -"PO-Revision-Date: 2021-03-15 22:09-0300\n" -"Last-Translator: <liddack@outlook.com>\n" -"Language-Team: Brazilian Portuguese\n" -"Language: pt_BR\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" -"X-Generator: Poedit 2.4.1\n" - -#: ../app/ui.js:400 -msgid "Connecting..." -msgstr "Conectando..." - -#: ../app/ui.js:407 -msgid "Disconnecting..." -msgstr "Desconectando..." - -#: ../app/ui.js:413 -msgid "Reconnecting..." -msgstr "Reconectando..." - -#: ../app/ui.js:418 -msgid "Internal error" -msgstr "Erro interno" - -#: ../app/ui.js:1009 -msgid "Must set host" -msgstr "É necessário definir o host" - -#: ../app/ui.js:1091 -msgid "Connected (encrypted) to " -msgstr "Conectado (com criptografia) a " - -#: ../app/ui.js:1093 -msgid "Connected (unencrypted) to " -msgstr "Conectado (sem criptografia) a " - -#: ../app/ui.js:1116 -msgid "Something went wrong, connection is closed" -msgstr "Algo deu errado. A conexão foi encerrada." - -#: ../app/ui.js:1119 -msgid "Failed to connect to server" -msgstr "Falha ao conectar-se ao servidor" - -#: ../app/ui.js:1129 -msgid "Disconnected" -msgstr "Desconectado" - -#: ../app/ui.js:1144 -msgid "New connection has been rejected with reason: " -msgstr "A nova conexão foi rejeitada pelo motivo: " - -#: ../app/ui.js:1147 -msgid "New connection has been rejected" -msgstr "A nova conexão foi rejeitada" - -#: ../app/ui.js:1182 -msgid "Credentials are required" -msgstr "Credenciais são obrigatórias" - -#: ../vnc.html:61 -msgid "noVNC encountered an error:" -msgstr "O noVNC encontrou um erro:" - -#: ../vnc.html:71 -msgid "Hide/Show the control bar" -msgstr "Esconder/mostrar a barra de controles" - -#: ../vnc.html:78 -msgid "Drag" -msgstr "Arrastar" - -#: ../vnc.html:78 -msgid "Move/Drag Viewport" -msgstr "Mover/arrastar a janela" - -#: ../vnc.html:84 -msgid "Keyboard" -msgstr "Teclado" - -#: ../vnc.html:84 -msgid "Show Keyboard" -msgstr "Mostrar teclado" - -#: ../vnc.html:89 -msgid "Extra keys" -msgstr "Teclas adicionais" - -#: ../vnc.html:89 -msgid "Show Extra Keys" -msgstr "Mostar teclas adicionais" - -#: ../vnc.html:94 -msgid "Ctrl" -msgstr "Ctrl" - -#: ../vnc.html:94 -msgid "Toggle Ctrl" -msgstr "Pressionar/soltar Ctrl" - -#: ../vnc.html:97 -msgid "Alt" -msgstr "Alt" - -#: ../vnc.html:97 -msgid "Toggle Alt" -msgstr "Pressionar/soltar Alt" - -#: ../vnc.html:100 -msgid "Toggle Windows" -msgstr "Pressionar/soltar Windows" - -#: ../vnc.html:100 -msgid "Windows" -msgstr "Windows" - -#: ../vnc.html:103 -msgid "Send Tab" -msgstr "Enviar Tab" - -#: ../vnc.html:103 -msgid "Tab" -msgstr "Tab" - -#: ../vnc.html:106 -msgid "Esc" -msgstr "Esc" - -#: ../vnc.html:106 -msgid "Send Escape" -msgstr "Enviar Esc" - -#: ../vnc.html:109 -msgid "Ctrl+Alt+Del" -msgstr "Ctrl+Alt+Del" - -#: ../vnc.html:109 -msgid "Send Ctrl-Alt-Del" -msgstr "Enviar Ctrl-Alt-Del" - -#: ../vnc.html:116 -msgid "Shutdown/Reboot" -msgstr "Desligar/reiniciar" - -#: ../vnc.html:116 -msgid "Shutdown/Reboot..." -msgstr "Desligar/reiniciar..." - -#: ../vnc.html:122 -msgid "Power" -msgstr "Ligar" - -#: ../vnc.html:124 -msgid "Shutdown" -msgstr "Desligar" - -#: ../vnc.html:125 -msgid "Reboot" -msgstr "Reiniciar" - -#: ../vnc.html:126 -msgid "Reset" -msgstr "Reiniciar (forçado)" - -#: ../vnc.html:131 ../vnc.html:137 -msgid "Clipboard" -msgstr "Área de transferência" - -#: ../vnc.html:141 -msgid "Clear" -msgstr "Limpar" - -#: ../vnc.html:147 -msgid "Fullscreen" -msgstr "Tela cheia" - -#: ../vnc.html:152 ../vnc.html:159 -msgid "Settings" -msgstr "Configurações" - -#: ../vnc.html:162 -msgid "Shared Mode" -msgstr "Modo compartilhado" - -#: ../vnc.html:165 -msgid "View Only" -msgstr "Apenas visualizar" - -#: ../vnc.html:169 -msgid "Clip to Window" -msgstr "Recortar à janela" - -#: ../vnc.html:172 -msgid "Scaling Mode:" -msgstr "Modo de dimensionamento:" - -#: ../vnc.html:174 -msgid "None" -msgstr "Nenhum" - -#: ../vnc.html:175 -msgid "Local Scaling" -msgstr "Local" - -#: ../vnc.html:176 -msgid "Remote Resizing" -msgstr "Remoto" - -#: ../vnc.html:181 -msgid "Advanced" -msgstr "Avançado" - -#: ../vnc.html:184 -msgid "Quality:" -msgstr "Qualidade:" - -#: ../vnc.html:188 -msgid "Compression level:" -msgstr "Nível de compressão:" - -#: ../vnc.html:193 -msgid "Repeater ID:" -msgstr "ID do repetidor:" - -#: ../vnc.html:197 -msgid "WebSocket" -msgstr "WebSocket" - -#: ../vnc.html:200 -msgid "Encrypt" -msgstr "Criptografar" - -#: ../vnc.html:203 -msgid "Host:" -msgstr "Host:" - -#: ../vnc.html:207 -msgid "Port:" -msgstr "Porta:" - -#: ../vnc.html:211 -msgid "Path:" -msgstr "Caminho:" - -#: ../vnc.html:218 -msgid "Automatic Reconnect" -msgstr "Reconexão automática" - -#: ../vnc.html:221 -msgid "Reconnect Delay (ms):" -msgstr "Atraso da reconexão (ms)" - -#: ../vnc.html:226 -msgid "Show Dot when No Cursor" -msgstr "Mostrar ponto quando não há cursor" - -#: ../vnc.html:231 -msgid "Logging:" -msgstr "Registros:" - -#: ../vnc.html:240 -msgid "Version:" -msgstr "Versão:" - -#: ../vnc.html:248 -msgid "Disconnect" -msgstr "Desconectar" - -#: ../vnc.html:267 -msgid "Connect" -msgstr "Conectar" - -#: ../vnc.html:277 -msgid "Username:" -msgstr "Nome de usuário:" - -#: ../vnc.html:281 -msgid "Password:" -msgstr "Senha:" - -#: ../vnc.html:285 -msgid "Send Credentials" -msgstr "Enviar credenciais" - -#: ../vnc.html:295 -msgid "Cancel" -msgstr "Cancelar" diff --git a/base/app/novnc/po/ru.po b/base/app/novnc/po/ru.po deleted file mode 100644 index 5a81bb0..0000000 --- a/base/app/novnc/po/ru.po +++ /dev/null @@ -1,302 +0,0 @@ -# Russian translations for noVNC package -# Русский перевод для пакета noVNC. -# Copyright (C) 2019 Dmitriy Shweew -# This file is distributed under the same license as the noVNC package. -# Dmitriy Shweew <shweew@it-advisor.ru>, 2019. -# -msgid "" -msgstr "" -"Project-Id-Version: noVNC 1.3.0\n" -"Report-Msgid-Bugs-To: novnc@googlegroups.com\n" -"POT-Creation-Date: 2021-08-27 16:03+0200\n" -"PO-Revision-Date: 2021-09-09 10:29+0400\n" -"Last-Translator: Nia Remez <nia.remez@cendio.com>\n" -"Language-Team: Russian\n" -"Language: ru\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" -"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" -"X-Generator: Poedit 2.2.1\n" -"X-Poedit-Flags-xgettext: --add-comments\n" - -#: ../app/ui.js:400 -msgid "Connecting..." -msgstr "Подключение..." - -#: ../app/ui.js:407 -msgid "Disconnecting..." -msgstr "Отключение..." - -#: ../app/ui.js:413 -msgid "Reconnecting..." -msgstr "Переподключение..." - -#: ../app/ui.js:418 -msgid "Internal error" -msgstr "Внутренняя ошибка" - -#: ../app/ui.js:1009 -msgid "Must set host" -msgstr "Задайте имя сервера или IP" - -#: ../app/ui.js:1091 -msgid "Connected (encrypted) to " -msgstr "Подключено (с шифрованием) к " - -#: ../app/ui.js:1093 -msgid "Connected (unencrypted) to " -msgstr "Подключено (без шифрования) к " - -#: ../app/ui.js:1116 -msgid "Something went wrong, connection is closed" -msgstr "Что-то пошло не так, подключение разорвано" - -#: ../app/ui.js:1119 -msgid "Failed to connect to server" -msgstr "Ошибка подключения к серверу" - -#: ../app/ui.js:1129 -msgid "Disconnected" -msgstr "Отключено" - -#: ../app/ui.js:1144 -msgid "New connection has been rejected with reason: " -msgstr "Новое соединение отклонено по причине: " - -#: ../app/ui.js:1147 -msgid "New connection has been rejected" -msgstr "Новое соединение отклонено" - -#: ../app/ui.js:1182 -msgid "Credentials are required" -msgstr "Требуются учетные данные" - -#: ../vnc.html:61 -msgid "noVNC encountered an error:" -msgstr "Ошибка noVNC: " - -#: ../vnc.html:71 -msgid "Hide/Show the control bar" -msgstr "Скрыть/Показать контрольную панель" - -#: ../vnc.html:78 -msgid "Drag" -msgstr "Переместить" - -#: ../vnc.html:78 -msgid "Move/Drag Viewport" -msgstr "Переместить окно" - -#: ../vnc.html:84 -msgid "Keyboard" -msgstr "Клавиатура" - -#: ../vnc.html:84 -msgid "Show Keyboard" -msgstr "Показать клавиатуру" - -#: ../vnc.html:89 -msgid "Extra keys" -msgstr "Дополнительные Кнопки" - -#: ../vnc.html:89 -msgid "Show Extra Keys" -msgstr "Показать Дополнительные Кнопки" - -#: ../vnc.html:94 -msgid "Ctrl" -msgstr "Ctrl" - -#: ../vnc.html:94 -msgid "Toggle Ctrl" -msgstr "Переключение нажатия Ctrl" - -#: ../vnc.html:97 -msgid "Alt" -msgstr "Alt" - -#: ../vnc.html:97 -msgid "Toggle Alt" -msgstr "Переключение нажатия Alt" - -#: ../vnc.html:100 -msgid "Toggle Windows" -msgstr "Переключение вкладок" - -#: ../vnc.html:100 -msgid "Windows" -msgstr "Вкладка" - -#: ../vnc.html:103 -msgid "Send Tab" -msgstr "Передать нажатие Tab" - -#: ../vnc.html:103 -msgid "Tab" -msgstr "Tab" - -#: ../vnc.html:106 -msgid "Esc" -msgstr "Esc" - -#: ../vnc.html:106 -msgid "Send Escape" -msgstr "Передать нажатие Escape" - -#: ../vnc.html:109 -msgid "Ctrl+Alt+Del" -msgstr "Ctrl+Alt+Del" - -#: ../vnc.html:109 -msgid "Send Ctrl-Alt-Del" -msgstr "Передать нажатие Ctrl-Alt-Del" - -#: ../vnc.html:116 -msgid "Shutdown/Reboot" -msgstr "Выключить/Перезагрузить" - -#: ../vnc.html:116 -msgid "Shutdown/Reboot..." -msgstr "Выключить/Перезагрузить..." - -#: ../vnc.html:122 -msgid "Power" -msgstr "Питание" - -#: ../vnc.html:124 -msgid "Shutdown" -msgstr "Выключить" - -#: ../vnc.html:125 -msgid "Reboot" -msgstr "Перезагрузить" - -#: ../vnc.html:126 -msgid "Reset" -msgstr "Сброс" - -#: ../vnc.html:131 ../vnc.html:137 -msgid "Clipboard" -msgstr "Буфер обмена" - -#: ../vnc.html:141 -msgid "Clear" -msgstr "Очистить" - -#: ../vnc.html:147 -msgid "Fullscreen" -msgstr "Во весь экран" - -#: ../vnc.html:152 ../vnc.html:159 -msgid "Settings" -msgstr "Настройки" - -#: ../vnc.html:162 -msgid "Shared Mode" -msgstr "Общий режим" - -#: ../vnc.html:165 -msgid "View Only" -msgstr "Только Просмотр" - -#: ../vnc.html:169 -msgid "Clip to Window" -msgstr "В окно" - -#: ../vnc.html:172 -msgid "Scaling Mode:" -msgstr "Масштаб:" - -#: ../vnc.html:174 -msgid "None" -msgstr "Нет" - -#: ../vnc.html:175 -msgid "Local Scaling" -msgstr "Локльный масштаб" - -#: ../vnc.html:176 -msgid "Remote Resizing" -msgstr "Удаленная перенастройка размера" - -#: ../vnc.html:181 -msgid "Advanced" -msgstr "Дополнительно" - -#: ../vnc.html:184 -msgid "Quality:" -msgstr "Качество" - -#: ../vnc.html:188 -msgid "Compression level:" -msgstr "Уровень Сжатия" - -#: ../vnc.html:193 -msgid "Repeater ID:" -msgstr "Идентификатор ID:" - -#: ../vnc.html:197 -msgid "WebSocket" -msgstr "WebSocket" - -#: ../vnc.html:200 -msgid "Encrypt" -msgstr "Шифрование" - -#: ../vnc.html:203 -msgid "Host:" -msgstr "Сервер:" - -#: ../vnc.html:207 -msgid "Port:" -msgstr "Порт:" - -#: ../vnc.html:211 -msgid "Path:" -msgstr "Путь:" - -#: ../vnc.html:218 -msgid "Automatic Reconnect" -msgstr "Автоматическое переподключение" - -#: ../vnc.html:221 -msgid "Reconnect Delay (ms):" -msgstr "Задержка переподключения (мс):" - -#: ../vnc.html:226 -msgid "Show Dot when No Cursor" -msgstr "Показать точку вместо курсора" - -#: ../vnc.html:231 -msgid "Logging:" -msgstr "Лог:" - -#: ../vnc.html:240 -msgid "Version:" -msgstr "Версия" - -#: ../vnc.html:248 -msgid "Disconnect" -msgstr "Отключение" - -#: ../vnc.html:267 -msgid "Connect" -msgstr "Подключение" - -#: ../vnc.html:277 -msgid "Username:" -msgstr "Имя Пользователя" - -#: ../vnc.html:281 -msgid "Password:" -msgstr "Пароль:" - -#: ../vnc.html:285 -msgid "Send Credentials" -msgstr "Передача Учетных Данных" - -#: ../vnc.html:295 -msgid "Cancel" -msgstr "Выход" diff --git a/base/app/novnc/po/sv.po b/base/app/novnc/po/sv.po deleted file mode 100644 index 85c4e30..0000000 --- a/base/app/novnc/po/sv.po +++ /dev/null @@ -1,349 +0,0 @@ -# Swedish translations for noVNC package -# Svenska översättningar för paketet noVNC. -# Copyright (C) 2020 The noVNC Authors -# This file is distributed under the same license as the noVNC package. -# Samuel Mannehed <samuel@cendio.se>, 2020. -# -msgid "" -msgstr "" -"Project-Id-Version: noVNC 1.3.0\n" -"Report-Msgid-Bugs-To: novnc@googlegroups.com\n" -"POT-Creation-Date: 2024-06-03 14:10+0200\n" -"PO-Revision-Date: 2024-06-18 13:52+0200\n" -"Last-Translator: Pierre Ossman <ossman@cendio.se>\n" -"Language-Team: none\n" -"Language: sv\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 3.4.4\n" - -#: ../app/ui.js:69 -msgid "" -"Running without HTTPS is not recommended, crashes or other issues are likely." -msgstr "" -"Det är ej rekommenderat att köra utan HTTPS, krascher och andra problem är " -"troliga." - -#: ../app/ui.js:410 -msgid "Connecting..." -msgstr "Ansluter..." - -#: ../app/ui.js:417 -msgid "Disconnecting..." -msgstr "Kopplar ner..." - -#: ../app/ui.js:423 -msgid "Reconnecting..." -msgstr "Återansluter..." - -#: ../app/ui.js:428 -msgid "Internal error" -msgstr "Internt fel" - -#: ../app/ui.js:1026 -msgid "Must set host" -msgstr "Du måste specifiera en värd" - -#: ../app/ui.js:1052 -msgid "Failed to connect to server: " -msgstr "Misslyckades att ansluta till servern: " - -#: ../app/ui.js:1118 -msgid "Connected (encrypted) to " -msgstr "Ansluten (krypterat) till " - -#: ../app/ui.js:1120 -msgid "Connected (unencrypted) to " -msgstr "Ansluten (okrypterat) till " - -#: ../app/ui.js:1143 -msgid "Something went wrong, connection is closed" -msgstr "Något gick fel, anslutningen avslutades" - -#: ../app/ui.js:1146 -msgid "Failed to connect to server" -msgstr "Misslyckades att ansluta till servern" - -#: ../app/ui.js:1158 -msgid "Disconnected" -msgstr "Frånkopplad" - -#: ../app/ui.js:1173 -msgid "New connection has been rejected with reason: " -msgstr "Ny anslutning har blivit nekad med följande skäl: " - -#: ../app/ui.js:1176 -msgid "New connection has been rejected" -msgstr "Ny anslutning har blivit nekad" - -#: ../app/ui.js:1242 -msgid "Credentials are required" -msgstr "Användaruppgifter krävs" - -#: ../vnc.html:55 -msgid "noVNC encountered an error:" -msgstr "noVNC stötte på ett problem:" - -#: ../vnc.html:65 -msgid "Hide/Show the control bar" -msgstr "Göm/Visa kontrollbaren" - -#: ../vnc.html:74 -msgid "Drag" -msgstr "Dra" - -#: ../vnc.html:74 -msgid "Move/Drag Viewport" -msgstr "Flytta/Dra Vyn" - -#: ../vnc.html:80 -msgid "Keyboard" -msgstr "Tangentbord" - -#: ../vnc.html:80 -msgid "Show Keyboard" -msgstr "Visa Tangentbord" - -#: ../vnc.html:85 -msgid "Extra keys" -msgstr "Extraknappar" - -#: ../vnc.html:85 -msgid "Show Extra Keys" -msgstr "Visa Extraknappar" - -#: ../vnc.html:90 -msgid "Ctrl" -msgstr "Ctrl" - -#: ../vnc.html:90 -msgid "Toggle Ctrl" -msgstr "Växla Ctrl" - -#: ../vnc.html:93 -msgid "Alt" -msgstr "Alt" - -#: ../vnc.html:93 -msgid "Toggle Alt" -msgstr "Växla Alt" - -#: ../vnc.html:96 -msgid "Toggle Windows" -msgstr "Växla Windows" - -#: ../vnc.html:96 -msgid "Windows" -msgstr "Windows" - -#: ../vnc.html:99 -msgid "Send Tab" -msgstr "Skicka Tab" - -#: ../vnc.html:99 -msgid "Tab" -msgstr "Tab" - -#: ../vnc.html:102 -msgid "Esc" -msgstr "Esc" - -#: ../vnc.html:102 -msgid "Send Escape" -msgstr "Skicka Escape" - -#: ../vnc.html:105 -msgid "Ctrl+Alt+Del" -msgstr "Ctrl+Alt+Del" - -#: ../vnc.html:105 -msgid "Send Ctrl-Alt-Del" -msgstr "Skicka Ctrl-Alt-Del" - -#: ../vnc.html:112 -msgid "Shutdown/Reboot" -msgstr "Stäng av/Boota om" - -#: ../vnc.html:112 -msgid "Shutdown/Reboot..." -msgstr "Stäng av/Boota om..." - -#: ../vnc.html:118 -msgid "Power" -msgstr "Ström" - -#: ../vnc.html:120 -msgid "Shutdown" -msgstr "Stäng av" - -#: ../vnc.html:121 -msgid "Reboot" -msgstr "Boota om" - -#: ../vnc.html:122 -msgid "Reset" -msgstr "Återställ" - -#: ../vnc.html:127 ../vnc.html:133 -msgid "Clipboard" -msgstr "Urklipp" - -#: ../vnc.html:135 -msgid "Edit clipboard content in the textarea below." -msgstr "Redigera urklippets innehåll i fältet nedan." - -#: ../vnc.html:143 -msgid "Full Screen" -msgstr "Fullskärm" - -#: ../vnc.html:148 ../vnc.html:154 -msgid "Settings" -msgstr "Inställningar" - -#: ../vnc.html:158 -msgid "Shared Mode" -msgstr "Delat Läge" - -#: ../vnc.html:161 -msgid "View Only" -msgstr "Endast Visning" - -#: ../vnc.html:165 -msgid "Clip to Window" -msgstr "Begränsa till Fönster" - -#: ../vnc.html:168 -msgid "Scaling Mode:" -msgstr "Skalningsläge:" - -#: ../vnc.html:170 -msgid "None" -msgstr "Ingen" - -#: ../vnc.html:171 -msgid "Local Scaling" -msgstr "Lokal Skalning" - -#: ../vnc.html:172 -msgid "Remote Resizing" -msgstr "Ändra Storlek" - -#: ../vnc.html:177 -msgid "Advanced" -msgstr "Avancerat" - -#: ../vnc.html:180 -msgid "Quality:" -msgstr "Kvalitet:" - -#: ../vnc.html:184 -msgid "Compression level:" -msgstr "Kompressionsnivå:" - -#: ../vnc.html:189 -msgid "Repeater ID:" -msgstr "Repeater-ID:" - -#: ../vnc.html:193 -msgid "WebSocket" -msgstr "WebSocket" - -#: ../vnc.html:196 -msgid "Encrypt" -msgstr "Kryptera" - -#: ../vnc.html:199 -msgid "Host:" -msgstr "Värd:" - -#: ../vnc.html:203 -msgid "Port:" -msgstr "Port:" - -#: ../vnc.html:207 -msgid "Path:" -msgstr "Sökväg:" - -#: ../vnc.html:214 -msgid "Automatic Reconnect" -msgstr "Automatisk Återanslutning" - -#: ../vnc.html:217 -msgid "Reconnect Delay (ms):" -msgstr "Fördröjning (ms):" - -#: ../vnc.html:222 -msgid "Show Dot when No Cursor" -msgstr "Visa prick när ingen muspekare finns" - -#: ../vnc.html:227 -msgid "Logging:" -msgstr "Loggning:" - -#: ../vnc.html:236 -msgid "Version:" -msgstr "Version:" - -#: ../vnc.html:244 -msgid "Disconnect" -msgstr "Koppla från" - -#: ../vnc.html:267 -msgid "Connect" -msgstr "Anslut" - -#: ../vnc.html:276 -msgid "Server identity" -msgstr "Server-identitet" - -#: ../vnc.html:279 -msgid "The server has provided the following identifying information:" -msgstr "Servern har gett följande identifierande information:" - -#: ../vnc.html:283 -msgid "Fingerprint:" -msgstr "Fingeravtryck:" - -#: ../vnc.html:286 -msgid "" -"Please verify that the information is correct and press \"Approve\". " -"Otherwise press \"Reject\"." -msgstr "" -"Kontrollera att informationen är korrekt och tryck sedan \"Godkänn\". Tryck " -"annars \"Neka\"." - -#: ../vnc.html:291 -msgid "Approve" -msgstr "Godkänn" - -#: ../vnc.html:292 -msgid "Reject" -msgstr "Neka" - -#: ../vnc.html:300 -msgid "Credentials" -msgstr "Användaruppgifter" - -#: ../vnc.html:304 -msgid "Username:" -msgstr "Användarnamn:" - -#: ../vnc.html:308 -msgid "Password:" -msgstr "Lösenord:" - -#: ../vnc.html:312 -msgid "Send Credentials" -msgstr "Skicka Användaruppgifter" - -#: ../vnc.html:321 -msgid "Cancel" -msgstr "Avbryt" - -#~ msgid "HTTPS is required for full functionality" -#~ msgstr "HTTPS krävs för full funktionalitet" - -#~ msgid "Clear" -#~ msgstr "Rensa" diff --git a/base/app/novnc/po/tr.po b/base/app/novnc/po/tr.po deleted file mode 100644 index 8b5c181..0000000 --- a/base/app/novnc/po/tr.po +++ /dev/null @@ -1,288 +0,0 @@ -# Turkish translations for noVNC package -# Turkish translation for noVNC. -# Copyright (C) 2018 The noVNC Authors -# This file is distributed under the same license as the noVNC package. -# Ömer ÇAKMAK <farukomercakmak@gmail.com>, 2018. -# -msgid "" -msgstr "" -"Project-Id-Version: noVNC 0.6.1\n" -"Report-Msgid-Bugs-To: novnc@googlegroups.com\n" -"POT-Creation-Date: 2017-11-24 07:16+0000\n" -"PO-Revision-Date: 2018-01-05 19:07+0300\n" -"Last-Translator: Ömer ÇAKMAK <farukomercakmak@gmail.com>\n" -"Language-Team: Türkçe <gnome-turk@gnome.org>\n" -"Language: tr\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Gtranslator 2.91.7\n" - -#: ../app/ui.js:404 -msgid "Connecting..." -msgstr "Bağlanıyor..." - -#: ../app/ui.js:411 -msgid "Disconnecting..." -msgstr "Bağlantı kesiliyor..." - -#: ../app/ui.js:417 -msgid "Reconnecting..." -msgstr "Yeniden bağlantı kuruluyor..." - -#: ../app/ui.js:422 -msgid "Internal error" -msgstr "İç hata" - -#: ../app/ui.js:1019 -msgid "Must set host" -msgstr "Sunucuyu kur" - -#: ../app/ui.js:1099 -msgid "Connected (encrypted) to " -msgstr "Bağlı (şifrelenmiş)" - -#: ../app/ui.js:1101 -msgid "Connected (unencrypted) to " -msgstr "Bağlandı (şifrelenmemiş)" - -#: ../app/ui.js:1119 -msgid "Something went wrong, connection is closed" -msgstr "Bir şeyler ters gitti, bağlantı kesildi" - -#: ../app/ui.js:1129 -msgid "Disconnected" -msgstr "Bağlantı kesildi" - -#: ../app/ui.js:1142 -msgid "New connection has been rejected with reason: " -msgstr "Bağlantı aşağıdaki nedenlerden dolayı reddedildi: " - -#: ../app/ui.js:1145 -msgid "New connection has been rejected" -msgstr "Bağlantı reddedildi" - -#: ../app/ui.js:1166 -msgid "Password is required" -msgstr "Şifre gerekli" - -#: ../vnc.html:89 -msgid "noVNC encountered an error:" -msgstr "Bir hata oluştu:" - -#: ../vnc.html:99 -msgid "Hide/Show the control bar" -msgstr "Denetim masasını Gizle/Göster" - -#: ../vnc.html:106 -msgid "Move/Drag Viewport" -msgstr "Görünümü Taşı/Sürükle" - -#: ../vnc.html:106 -msgid "viewport drag" -msgstr "Görüntü penceresini sürükle" - -#: ../vnc.html:112 ../vnc.html:115 ../vnc.html:118 ../vnc.html:121 -msgid "Active Mouse Button" -msgstr "Aktif Fare Düğmesi" - -#: ../vnc.html:112 -msgid "No mousebutton" -msgstr "Fare düğmesi yok" - -#: ../vnc.html:115 -msgid "Left mousebutton" -msgstr "Farenin sol düğmesi" - -#: ../vnc.html:118 -msgid "Middle mousebutton" -msgstr "Farenin orta düğmesi" - -#: ../vnc.html:121 -msgid "Right mousebutton" -msgstr "Farenin sağ düğmesi" - -#: ../vnc.html:124 -msgid "Keyboard" -msgstr "Klavye" - -#: ../vnc.html:124 -msgid "Show Keyboard" -msgstr "Klavye Düzenini Göster" - -#: ../vnc.html:131 -msgid "Extra keys" -msgstr "Ekstra tuşlar" - -#: ../vnc.html:131 -msgid "Show Extra Keys" -msgstr "Ekstra tuşları göster" - -#: ../vnc.html:136 -msgid "Ctrl" -msgstr "Ctrl" - -#: ../vnc.html:136 -msgid "Toggle Ctrl" -msgstr "Ctrl Değiştir " - -#: ../vnc.html:139 -msgid "Alt" -msgstr "Alt" - -#: ../vnc.html:139 -msgid "Toggle Alt" -msgstr "Alt Değiştir" - -#: ../vnc.html:142 -msgid "Send Tab" -msgstr "Sekme Gönder" - -#: ../vnc.html:142 -msgid "Tab" -msgstr "Sekme" - -#: ../vnc.html:145 -msgid "Esc" -msgstr "Esc" - -#: ../vnc.html:145 -msgid "Send Escape" -msgstr "Boşluk Gönder" - -#: ../vnc.html:148 -msgid "Ctrl+Alt+Del" -msgstr "Ctrl + Alt + Del" - -#: ../vnc.html:148 -msgid "Send Ctrl-Alt-Del" -msgstr "Ctrl-Alt-Del Gönder" - -#: ../vnc.html:156 -msgid "Shutdown/Reboot" -msgstr "Kapat/Yeniden Başlat" - -#: ../vnc.html:156 -msgid "Shutdown/Reboot..." -msgstr "Kapat/Yeniden Başlat..." - -#: ../vnc.html:162 -msgid "Power" -msgstr "Güç" - -#: ../vnc.html:164 -msgid "Shutdown" -msgstr "Kapat" - -#: ../vnc.html:165 -msgid "Reboot" -msgstr "Yeniden Başlat" - -#: ../vnc.html:166 -msgid "Reset" -msgstr "Sıfırla" - -#: ../vnc.html:171 ../vnc.html:177 -msgid "Clipboard" -msgstr "Pano" - -#: ../vnc.html:181 -msgid "Clear" -msgstr "Temizle" - -#: ../vnc.html:187 -msgid "Fullscreen" -msgstr "Tam Ekran" - -#: ../vnc.html:192 ../vnc.html:199 -msgid "Settings" -msgstr "Ayarlar" - -#: ../vnc.html:202 -msgid "Shared Mode" -msgstr "Paylaşım Modu" - -#: ../vnc.html:205 -msgid "View Only" -msgstr "Sadece Görüntüle" - -#: ../vnc.html:209 -msgid "Clip to Window" -msgstr "Pencereye Tıkla" - -#: ../vnc.html:212 -msgid "Scaling Mode:" -msgstr "Ölçekleme Modu:" - -#: ../vnc.html:214 -msgid "None" -msgstr "Bilinmeyen" - -#: ../vnc.html:215 -msgid "Local Scaling" -msgstr "Yerel Ölçeklendirme" - -#: ../vnc.html:216 -msgid "Remote Resizing" -msgstr "Uzaktan Yeniden Boyutlandırma" - -#: ../vnc.html:221 -msgid "Advanced" -msgstr "Gelişmiş" - -#: ../vnc.html:224 -msgid "Repeater ID:" -msgstr "Tekralayıcı ID:" - -#: ../vnc.html:228 -msgid "WebSocket" -msgstr "WebSocket" - -#: ../vnc.html:231 -msgid "Encrypt" -msgstr "Şifrele" - -#: ../vnc.html:234 -msgid "Host:" -msgstr "Ana makine:" - -#: ../vnc.html:238 -msgid "Port:" -msgstr "Port:" - -#: ../vnc.html:242 -msgid "Path:" -msgstr "Yol:" - -#: ../vnc.html:249 -msgid "Automatic Reconnect" -msgstr "Otomatik Yeniden Bağlan" - -#: ../vnc.html:252 -msgid "Reconnect Delay (ms):" -msgstr "Yeniden Bağlanma Süreci (ms):" - -#: ../vnc.html:258 -msgid "Logging:" -msgstr "Giriş yapılıyor:" - -#: ../vnc.html:270 -msgid "Disconnect" -msgstr "Bağlantıyı Kes" - -#: ../vnc.html:289 -msgid "Connect" -msgstr "Bağlan" - -#: ../vnc.html:299 -msgid "Password:" -msgstr "Parola:" - -#: ../vnc.html:313 -msgid "Cancel" -msgstr "Vazgeç" - -#: ../vnc.html:329 -msgid "Canvas not supported." -msgstr "Tuval desteklenmiyor." diff --git a/base/app/novnc/po/xgettext-html b/base/app/novnc/po/xgettext-html deleted file mode 100755 index ae53461..0000000 --- a/base/app/novnc/po/xgettext-html +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/env node -/* - * xgettext-html: HTML gettext parser - * Copyright (C) 2018 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - */ - -const getopt = require('node-getopt'); -const jsdom = require("jsdom"); -const fs = require("fs"); - -const opt = getopt.create([ - ['o', 'output=FILE', 'write output to specified file'], - ['h', 'help', 'display this help'], -]).bindHelp().parseSystem(); - -const strings = {}; - -function addString(str, location) { - // We assume surrounding whitespace, and whitespace around line - // breaks, is just for source formatting - str = str.split("\n").map(s => s.trim()).join(" ").trim(); - - if (str.length == 0) { - return; - } - - if (strings[str] === undefined) { - strings[str] = {}; - } - strings[str][location] = null; -} - -// See https://html.spec.whatwg.org/multipage/dom.html#attr-translate -function process(elem, locator, enabled) { - function isAnyOf(searchElement, items) { - return items.indexOf(searchElement) !== -1; - } - - if (elem.hasAttribute("translate")) { - if (isAnyOf(elem.getAttribute("translate"), ["", "yes"])) { - enabled = true; - } else if (isAnyOf(elem.getAttribute("translate"), ["no"])) { - enabled = false; - } - } - - if (enabled) { - if (elem.hasAttribute("abbr") && - elem.tagName === "TH") { - addString(elem.getAttribute("abbr"), locator(elem)); - } - if (elem.hasAttribute("alt") && - isAnyOf(elem.tagName, ["AREA", "IMG", "INPUT"])) { - addString(elem.getAttribute("alt"), locator(elem)); - } - if (elem.hasAttribute("download") && - isAnyOf(elem.tagName, ["A", "AREA"])) { - addString(elem.getAttribute("download"), locator(elem)); - } - if (elem.hasAttribute("label") && - isAnyOf(elem.tagName, ["MENUITEM", "MENU", "OPTGROUP", - "OPTION", "TRACK"])) { - addString(elem.getAttribute("label"), locator(elem)); - } - if (elem.hasAttribute("placeholder") && - isAnyOf(elem.tagName in ["INPUT", "TEXTAREA"])) { - addString(elem.getAttribute("placeholder"), locator(elem)); - } - if (elem.hasAttribute("title")) { - addString(elem.getAttribute("title"), locator(elem)); - } - if (elem.hasAttribute("value") && - elem.tagName === "INPUT" && - isAnyOf(elem.getAttribute("type"), ["reset", "button", "submit"])) { - addString(elem.getAttribute("value"), locator(elem)); - } - } - - for (let i = 0; i < elem.childNodes.length; i++) { - let node = elem.childNodes[i]; - if (node.nodeType === node.ELEMENT_NODE) { - process(node, locator, enabled); - } else if (node.nodeType === node.TEXT_NODE && enabled) { - addString(node.data, locator(node)); - } - } -} - -for (let i = 0; i < opt.argv.length; i++) { - const fn = opt.argv[i]; - const file = fs.readFileSync(fn, "utf8"); - const dom = new jsdom.JSDOM(file, { includeNodeLocations: true }); - const body = dom.window.document.body; - - let locator = (elem) => { - const offset = dom.nodeLocation(elem).startOffset; - const line = file.slice(0, offset).split("\n").length; - return fn + ":" + line; - }; - - process(body, locator, true); -} - -let output = ""; - -for (let str in strings) { - output += "#:"; - for (location in strings[str]) { - output += " " + location; - } - output += "\n"; - - output += "msgid " + JSON.stringify(str) + "\n"; - output += "msgstr \"\"\n"; - output += "\n"; -} - -fs.writeFileSync(opt.options.output, output); diff --git a/base/app/novnc/po/zh_CN.po b/base/app/novnc/po/zh_CN.po deleted file mode 100644 index caae285..0000000 --- a/base/app/novnc/po/zh_CN.po +++ /dev/null @@ -1,284 +0,0 @@ -# Simplified Chinese translations for noVNC package. -# Copyright (C) 2020 The noVNC Authors -# This file is distributed under the same license as the noVNC package. -# Peter Dave Hello <hsu@peterdavehello.org>, 2018. -# -msgid "" -msgstr "" -"Project-Id-Version: noVNC 1.1.0\n" -"Report-Msgid-Bugs-To: novnc@googlegroups.com\n" -"POT-Creation-Date: 2018-01-10 00:53+0800\n" -"PO-Revision-Date: 2020-01-02 13:19+0800\n" -"Last-Translator: CUI Wei <ghostplant@qq.com>\n" -"Language: zh_CN\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - -#: ../app/ui.js:430 -msgid "Connecting..." -msgstr "连接中..." - -#: ../app/ui.js:438 -msgid "Connected (encrypted) to " -msgstr "已连接(已加密)到" - -#: ../app/ui.js:440 -msgid "Connected (unencrypted) to " -msgstr "已连接(未加密)到" - -#: ../app/ui.js:446 -msgid "Disconnecting..." -msgstr "正在断开连接..." - -#: ../app/ui.js:450 -msgid "Disconnected" -msgstr "已断开连接" - -#: ../app/ui.js:1052 ../core/rfb.js:248 -msgid "Must set host" -msgstr "必须设置主机" - -#: ../app/ui.js:1101 -msgid "Reconnecting..." -msgstr "重新连接中..." - -#: ../app/ui.js:1140 -msgid "Password is required" -msgstr "请提供密码" - -#: ../core/rfb.js:548 -msgid "Disconnect timeout" -msgstr "超时断开" - -#: ../vnc.html:89 -msgid "noVNC encountered an error:" -msgstr "noVNC 遇到一个错误:" - -#: ../vnc.html:99 -msgid "Hide/Show the control bar" -msgstr "显示/隐藏控制栏" - -#: ../vnc.html:106 -msgid "Move/Drag Viewport" -msgstr "移动/拖动窗口" - -#: ../vnc.html:106 -msgid "viewport drag" -msgstr "窗口拖动" - -#: ../vnc.html:112 ../vnc.html:115 ../vnc.html:118 ../vnc.html:121 -msgid "Active Mouse Button" -msgstr "启动鼠标按键" - -#: ../vnc.html:112 -msgid "No mousebutton" -msgstr "禁用鼠标按键" - -#: ../vnc.html:115 -msgid "Left mousebutton" -msgstr "鼠标左键" - -#: ../vnc.html:118 -msgid "Middle mousebutton" -msgstr "鼠标中键" - -#: ../vnc.html:121 -msgid "Right mousebutton" -msgstr "鼠标右键" - -#: ../vnc.html:124 -msgid "Keyboard" -msgstr "键盘" - -#: ../vnc.html:124 -msgid "Show Keyboard" -msgstr "显示键盘" - -#: ../vnc.html:131 -msgid "Extra keys" -msgstr "额外按键" - -#: ../vnc.html:131 -msgid "Show Extra Keys" -msgstr "显示额外按键" - -#: ../vnc.html:136 -msgid "Ctrl" -msgstr "Ctrl" - -#: ../vnc.html:136 -msgid "Toggle Ctrl" -msgstr "切换 Ctrl" - -#: ../vnc.html:136 -msgid "Edit clipboard content in the textarea below." -msgstr "在下面的文本区域中编辑剪贴板内容。" - -#: ../vnc.html:139 -msgid "Alt" -msgstr "Alt" - -#: ../vnc.html:139 -msgid "Toggle Alt" -msgstr "切换 Alt" - -#: ../vnc.html:142 -msgid "Send Tab" -msgstr "发送 Tab 键" - -#: ../vnc.html:142 -msgid "Tab" -msgstr "Tab" - -#: ../vnc.html:145 -msgid "Esc" -msgstr "Esc" - -#: ../vnc.html:145 -msgid "Send Escape" -msgstr "发送 Escape 键" - -#: ../vnc.html:148 -msgid "Ctrl+Alt+Del" -msgstr "Ctrl+Alt+Del" - -#: ../vnc.html:148 -msgid "Send Ctrl-Alt-Del" -msgstr "发送 Ctrl+Alt+Del 键" - -#: ../vnc.html:156 -msgid "Shutdown/Reboot" -msgstr "关机/重启" - -#: ../vnc.html:156 -msgid "Shutdown/Reboot..." -msgstr "关机/重启..." - -#: ../vnc.html:162 -msgid "Power" -msgstr "电源" - -#: ../vnc.html:164 -msgid "Shutdown" -msgstr "关机" - -#: ../vnc.html:165 -msgid "Reboot" -msgstr "重启" - -#: ../vnc.html:166 -msgid "Reset" -msgstr "重置" - -#: ../vnc.html:171 ../vnc.html:177 -msgid "Clipboard" -msgstr "剪贴板" - -#: ../vnc.html:181 -msgid "Clear" -msgstr "清除" - -#: ../vnc.html:187 -msgid "Fullscreen" -msgstr "全屏" - -#: ../vnc.html:192 ../vnc.html:199 -msgid "Settings" -msgstr "设置" - -#: ../vnc.html:200 -msgid "Encrypt" -msgstr "加密" - -#: ../vnc.html:202 -msgid "Shared Mode" -msgstr "分享模式" - -#: ../vnc.html:205 -msgid "View Only" -msgstr "仅查看" - -#: ../vnc.html:209 -msgid "Clip to Window" -msgstr "限制/裁切窗口大小" - -#: ../vnc.html:212 -msgid "Scaling Mode:" -msgstr "缩放模式:" - -#: ../vnc.html:214 -msgid "None" -msgstr "无" - -#: ../vnc.html:215 -msgid "Local Scaling" -msgstr "本地缩放" - -#: ../vnc.html:216 -msgid "Local Downscaling" -msgstr "降低本地尺寸" - -#: ../vnc.html:217 -msgid "Remote Resizing" -msgstr "远程调整大小" - -#: ../vnc.html:222 -msgid "Advanced" -msgstr "高级" - -#: ../vnc.html:225 -msgid "Local Cursor" -msgstr "本地光标" - -#: ../vnc.html:229 -msgid "Repeater ID:" -msgstr "中继站 ID" - -#: ../vnc.html:233 -msgid "WebSocket" -msgstr "WebSocket" - -#: ../vnc.html:239 -msgid "Host:" -msgstr "主机:" - -#: ../vnc.html:243 -msgid "Port:" -msgstr "端口:" - -#: ../vnc.html:247 -msgid "Path:" -msgstr "路径:" - -#: ../vnc.html:254 -msgid "Automatic Reconnect" -msgstr "自动重新连接" - -#: ../vnc.html:257 -msgid "Reconnect Delay (ms):" -msgstr "重新连接间隔 (ms):" - -#: ../vnc.html:263 -msgid "Logging:" -msgstr "日志级别:" - -#: ../vnc.html:275 -msgid "Disconnect" -msgstr "断开连接" - -#: ../vnc.html:294 -msgid "Connect" -msgstr "连接" - -#: ../vnc.html:304 -msgid "Password:" -msgstr "密码:" - -#: ../vnc.html:318 -msgid "Cancel" -msgstr "取消" - -#: ../vnc.html:334 -msgid "Canvas not supported." -msgstr "不支持 Canvas。" \ No newline at end of file diff --git a/base/app/novnc/po/zh_TW.po b/base/app/novnc/po/zh_TW.po deleted file mode 100644 index 9ddf550..0000000 --- a/base/app/novnc/po/zh_TW.po +++ /dev/null @@ -1,285 +0,0 @@ -# Traditional Chinese translations for noVNC package. -# Copyright (C) 2018 The noVNC Authors -# This file is distributed under the same license as the noVNC package. -# Peter Dave Hello <hsu@peterdavehello.org>, 2018. -# -msgid "" -msgstr "" -"Project-Id-Version: noVNC 1.0.0-testing.2\n" -"Report-Msgid-Bugs-To: novnc@googlegroups.com\n" -"POT-Creation-Date: 2018-01-10 00:53+0800\n" -"PO-Revision-Date: 2018-01-10 01:33+0800\n" -"Last-Translator: Peter Dave Hello <hsu@peterdavehello.org>\n" -"Language-Team: Peter Dave Hello <hsu@peterdavehello.org>\n" -"Language: zh\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - -#: ../app/ui.js:395 -msgid "Connecting..." -msgstr "連線中..." - -#: ../app/ui.js:402 -msgid "Disconnecting..." -msgstr "正在中斷連線..." - -#: ../app/ui.js:408 -msgid "Reconnecting..." -msgstr "重新連線中..." - -#: ../app/ui.js:413 -msgid "Internal error" -msgstr "內部錯誤" - -#: ../app/ui.js:1015 -msgid "Must set host" -msgstr "請提供主機資訊" - -#: ../app/ui.js:1097 -msgid "Connected (encrypted) to " -msgstr "已加密連線到" - -#: ../app/ui.js:1099 -msgid "Connected (unencrypted) to " -msgstr "未加密連線到" - -#: ../app/ui.js:1120 -msgid "Something went wrong, connection is closed" -msgstr "發生錯誤,連線已關閉" - -#: ../app/ui.js:1123 -msgid "Failed to connect to server" -msgstr "無法連線到伺服器" - -#: ../app/ui.js:1133 -msgid "Disconnected" -msgstr "連線已中斷" - -#: ../app/ui.js:1146 -msgid "New connection has been rejected with reason: " -msgstr "連線被拒絕,原因:" - -#: ../app/ui.js:1149 -msgid "New connection has been rejected" -msgstr "連線被拒絕" - -#: ../app/ui.js:1170 -msgid "Password is required" -msgstr "請提供密碼" - -#: ../vnc.html:89 -msgid "noVNC encountered an error:" -msgstr "noVNC 遇到一個錯誤:" - -#: ../vnc.html:99 -msgid "Hide/Show the control bar" -msgstr "顯示/隱藏控制列" - -#: ../vnc.html:106 -msgid "Move/Drag Viewport" -msgstr "拖放顯示範圍" - -#: ../vnc.html:106 -msgid "viewport drag" -msgstr "顯示範圍拖放" - -#: ../vnc.html:112 ../vnc.html:115 ../vnc.html:118 ../vnc.html:121 -msgid "Active Mouse Button" -msgstr "啟用滑鼠按鍵" - -#: ../vnc.html:112 -msgid "No mousebutton" -msgstr "無滑鼠按鍵" - -#: ../vnc.html:115 -msgid "Left mousebutton" -msgstr "滑鼠左鍵" - -#: ../vnc.html:118 -msgid "Middle mousebutton" -msgstr "滑鼠中鍵" - -#: ../vnc.html:121 -msgid "Right mousebutton" -msgstr "滑鼠右鍵" - -#: ../vnc.html:124 -msgid "Keyboard" -msgstr "鍵盤" - -#: ../vnc.html:124 -msgid "Show Keyboard" -msgstr "顯示鍵盤" - -#: ../vnc.html:131 -msgid "Extra keys" -msgstr "額外按鍵" - -#: ../vnc.html:131 -msgid "Show Extra Keys" -msgstr "顯示額外按鍵" - -#: ../vnc.html:136 -msgid "Ctrl" -msgstr "Ctrl" - -#: ../vnc.html:136 -msgid "Toggle Ctrl" -msgstr "切換 Ctrl" - -#: ../vnc.html:139 -msgid "Alt" -msgstr "Alt" - -#: ../vnc.html:139 -msgid "Toggle Alt" -msgstr "切換 Alt" - -#: ../vnc.html:142 -msgid "Send Tab" -msgstr "送出 Tab 鍵" - -#: ../vnc.html:142 -msgid "Tab" -msgstr "Tab" - -#: ../vnc.html:145 -msgid "Esc" -msgstr "Esc" - -#: ../vnc.html:145 -msgid "Send Escape" -msgstr "送出 Escape 鍵" - -#: ../vnc.html:148 -msgid "Ctrl+Alt+Del" -msgstr "Ctrl-Alt-Del" - -#: ../vnc.html:148 -msgid "Send Ctrl-Alt-Del" -msgstr "送出 Ctrl-Alt-Del 快捷鍵" - -#: ../vnc.html:156 -msgid "Shutdown/Reboot" -msgstr "關機/重新啟動" - -#: ../vnc.html:156 -msgid "Shutdown/Reboot..." -msgstr "關機/重新啟動..." - -#: ../vnc.html:162 -msgid "Power" -msgstr "電源" - -#: ../vnc.html:164 -msgid "Shutdown" -msgstr "關機" - -#: ../vnc.html:165 -msgid "Reboot" -msgstr "重新啟動" - -#: ../vnc.html:166 -msgid "Reset" -msgstr "重設" - -#: ../vnc.html:171 ../vnc.html:177 -msgid "Clipboard" -msgstr "剪貼簿" - -#: ../vnc.html:181 -msgid "Clear" -msgstr "清除" - -#: ../vnc.html:187 -msgid "Fullscreen" -msgstr "全螢幕" - -#: ../vnc.html:192 ../vnc.html:199 -msgid "Settings" -msgstr "設定" - -#: ../vnc.html:202 -msgid "Shared Mode" -msgstr "分享模式" - -#: ../vnc.html:205 -msgid "View Only" -msgstr "僅檢視" - -#: ../vnc.html:209 -msgid "Clip to Window" -msgstr "限制/裁切視窗大小" - -#: ../vnc.html:212 -msgid "Scaling Mode:" -msgstr "縮放模式:" - -#: ../vnc.html:214 -msgid "None" -msgstr "無" - -#: ../vnc.html:215 -msgid "Local Scaling" -msgstr "本機縮放" - -#: ../vnc.html:216 -msgid "Remote Resizing" -msgstr "遠端調整大小" - -#: ../vnc.html:221 -msgid "Advanced" -msgstr "進階" - -#: ../vnc.html:224 -msgid "Repeater ID:" -msgstr "中繼站 ID" - -#: ../vnc.html:228 -msgid "WebSocket" -msgstr "WebSocket" - -#: ../vnc.html:231 -msgid "Encrypt" -msgstr "加密" - -#: ../vnc.html:234 -msgid "Host:" -msgstr "主機:" - -#: ../vnc.html:238 -msgid "Port:" -msgstr "連接埠:" - -#: ../vnc.html:242 -msgid "Path:" -msgstr "路徑:" - -#: ../vnc.html:249 -msgid "Automatic Reconnect" -msgstr "自動重新連線" - -#: ../vnc.html:252 -msgid "Reconnect Delay (ms):" -msgstr "重新連線間隔 (ms):" - -#: ../vnc.html:258 -msgid "Logging:" -msgstr "日誌級別:" - -#: ../vnc.html:270 -msgid "Disconnect" -msgstr "中斷連線" - -#: ../vnc.html:289 -msgid "Connect" -msgstr "連線" - -#: ../vnc.html:299 -msgid "Password:" -msgstr "密碼:" - -#: ../vnc.html:313 -msgid "Cancel" -msgstr "取消" diff --git a/base/app/novnc/snap/hooks/configure b/base/app/novnc/snap/hooks/configure deleted file mode 100644 index ff4f804..0000000 --- a/base/app/novnc/snap/hooks/configure +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -e - -snapctl restart novnc.novncsvc diff --git a/base/app/novnc/snap/local/svc_wrapper.sh b/base/app/novnc/snap/local/svc_wrapper.sh deleted file mode 100755 index 77db539..0000000 --- a/base/app/novnc/snap/local/svc_wrapper.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash - -# `snapctl get services` returns a JSON array, example: -#{ -#"n6801": { -# "listen": 6801, -# "vnc": "localhost:5901" -#}, -#"n6802": { -# "listen": 6802, -# "vnc": "localhost:5902" -#} -#} -snapctl get services | jq -c '.[]' | while read service; do # for each service the user sepcified.. - # get the important data for the service (listen port, VNC host:port) - listen_port="$(echo $service | jq --raw-output '.listen')" - vnc_host_port="$(echo $service | jq --raw-output '.vnc')" # --raw-output removes any quotation marks from the output - - # check whether those values are valid - expr "$listen_port" : '^[0-9]\+$' > /dev/null - listen_port_valid=$? - if [ ! $listen_port_valid ] || [ -z "$vnc_host_port" ]; then - # invalid values mean the service is disabled, do nothing except for printing a message (logged in /var/log/system or systemd journal) - echo "novnc: not starting service ${service} with listen_port ${listen_port} and vnc_host_port ${vnc_host_port}" - else - # start (and fork with '&') the service using the specified listen port and VNC host:port - $SNAP/novnc_proxy --listen $listen_port --vnc $vnc_host_port & - fi -done diff --git a/base/app/novnc/snap/snapcraft.yaml b/base/app/novnc/snap/snapcraft.yaml deleted file mode 100644 index 82d52de..0000000 --- a/base/app/novnc/snap/snapcraft.yaml +++ /dev/null @@ -1,61 +0,0 @@ -name: novnc -base: core22 # the base snap is the execution environment for this snap -version: git -summary: Open Source VNC client using HTML5 (WebSockets, Canvas) -description: | - Open Source VNC client using HTML5 (WebSockets, Canvas). - noVNC is both a VNC client JavaScript library as well as an - application built on top of that library. noVNC runs well in any - modern browser including mobile browsers (iOS and Android). - -grade: stable -confinement: strict - -parts: - novnc: - source: . - plugin: dump - organize: - utils/novnc_proxy: / - stage: - - vnc.html - - app - - core/**/*.js - - vendor/**/*.js - - novnc_proxy - - novnc-deps: - plugin: nil - stage-packages: - - bash - - svc-script: - source: snap/local - plugin: dump - stage: - - svc_wrapper.sh - - svc-script-deps: - plugin: nil - stage-packages: - - bash - - jq - - websockify: - source: https://github.com/novnc/websockify/archive/v0.12.0.tar.gz - plugin: python - stage-packages: - - python3-numpy - -hooks: - configure: - plugs: [network, network-bind] - -apps: - novnc: - command: ./novnc_proxy - plugs: [network, network-bind] - novncsvc: - command: ./svc_wrapper.sh - daemon: forking - plugs: [network, network-bind] diff --git a/base/app/novnc/tests/assertions.js b/base/app/novnc/tests/assertions.js deleted file mode 100644 index 739f637..0000000 --- a/base/app/novnc/tests/assertions.js +++ /dev/null @@ -1,96 +0,0 @@ -// noVNC specific assertions -chai.use(function (_chai, utils) { - function _equal(a, b) { - return a === b; - } - _chai.Assertion.addMethod('displayed', function (targetData, cmp=_equal) { - const obj = this._obj; - const ctx = obj._target.getContext('2d'); - const data = ctx.getImageData(0, 0, obj._target.width, obj._target.height).data; - const len = data.length; - new chai.Assertion(len).to.be.equal(targetData.length, "unexpected display size"); - let same = true; - for (let i = 0; i < len; i++) { - if (!cmp(data[i], targetData[i])) { - same = false; - break; - } - } - if (!same) { - // eslint-disable-next-line no-console - console.log("expected data: %o, actual data: %o", targetData, data); - } - this.assert(same, - "expected #{this} to have displayed the image #{exp}, but instead it displayed #{act}", - "expected #{this} not to have displayed the image #{act}", - targetData, - data); - }); - - _chai.Assertion.addMethod('sent', function (targetData) { - const obj = this._obj; - const data = obj._websocket._getSentData(); - let same = true; - if (data.length != targetData.length) { - same = false; - } else { - for (let i = 0; i < data.length; i++) { - if (data[i] != targetData[i]) { - same = false; - break; - } - } - } - if (!same) { - // eslint-disable-next-line no-console - console.log("expected data: %o, actual data: %o", targetData, data); - } - this.assert(same, - "expected #{this} to have sent the data #{exp}, but it actually sent #{act}", - "expected #{this} not to have sent the data #{act}", - Array.prototype.slice.call(targetData), - Array.prototype.slice.call(data)); - }); - - _chai.Assertion.addProperty('array', function () { - utils.flag(this, 'array', true); - }); - - _chai.Assertion.overwriteMethod('equal', function (_super) { - return function assertArrayEqual(target) { - if (utils.flag(this, 'array')) { - const obj = this._obj; - - let same = true; - - if (utils.flag(this, 'deep')) { - for (let i = 0; i < obj.length; i++) { - if (!utils.eql(obj[i], target[i])) { - same = false; - break; - } - } - - this.assert(same, - "expected #{this} to have elements deeply equal to #{exp}", - "expected #{this} not to have elements deeply equal to #{exp}", - Array.prototype.slice.call(target)); - } else { - for (let i = 0; i < obj.length; i++) { - if (obj[i] != target[i]) { - same = false; - break; - } - } - - this.assert(same, - "expected #{this} to have elements equal to #{exp}", - "expected #{this} not to have elements equal to #{exp}", - Array.prototype.slice.call(target)); - } - } else { - _super.apply(this, arguments); - } - }; - }); -}); diff --git a/base/app/novnc/tests/fake.websocket.js b/base/app/novnc/tests/fake.websocket.js deleted file mode 100644 index d273fe0..0000000 --- a/base/app/novnc/tests/fake.websocket.js +++ /dev/null @@ -1,92 +0,0 @@ -import Base64 from '../core/base64.js'; - -export default class FakeWebSocket { - constructor(uri, protocols) { - this.url = uri; - this.binaryType = "arraybuffer"; - this.extensions = ""; - - this.onerror = null; - this.onmessage = null; - this.onopen = null; - - if (!protocols || typeof protocols === 'string') { - this.protocol = protocols; - } else { - this.protocol = protocols[0]; - } - - this._sendQueue = new Uint8Array(20000); - - this.readyState = FakeWebSocket.CONNECTING; - this.bufferedAmount = 0; - - this._isFake = true; - } - - close(code, reason) { - this.readyState = FakeWebSocket.CLOSED; - if (this.onclose) { - this.onclose(new CloseEvent("close", { 'code': code, 'reason': reason, 'wasClean': true })); - } - } - - send(data) { - if (this.protocol == 'base64') { - data = Base64.decode(data); - } else { - data = new Uint8Array(data); - } - this._sendQueue.set(data, this.bufferedAmount); - this.bufferedAmount += data.length; - } - - _getSentData() { - const res = this._sendQueue.slice(0, this.bufferedAmount); - this.bufferedAmount = 0; - return res; - } - - _open() { - this.readyState = FakeWebSocket.OPEN; - if (this.onopen) { - this.onopen(new Event('open')); - } - } - - _receiveData(data) { - if (data.length < 4096) { - // Break apart the data to expose bugs where we assume data is - // neatly packaged - for (let i = 0;i < data.length;i++) { - let buf = data.slice(i, i+1); - this.onmessage(new MessageEvent("message", { 'data': buf.buffer })); - } - } else { - this.onmessage(new MessageEvent("message", { 'data': data.buffer })); - } - } -} - -FakeWebSocket.OPEN = WebSocket.OPEN; -FakeWebSocket.CONNECTING = WebSocket.CONNECTING; -FakeWebSocket.CLOSING = WebSocket.CLOSING; -FakeWebSocket.CLOSED = WebSocket.CLOSED; - -FakeWebSocket._isFake = true; - -FakeWebSocket.replace = () => { - if (!WebSocket._isFake) { - const realVersion = WebSocket; - // eslint-disable-next-line no-global-assign - WebSocket = FakeWebSocket; - FakeWebSocket._realVersion = realVersion; - } -}; - -FakeWebSocket.restore = () => { - if (WebSocket._isFake) { - // eslint-disable-next-line no-global-assign - WebSocket = WebSocket._realVersion; - } -}; diff --git a/base/app/novnc/tests/playback-ui.js b/base/app/novnc/tests/playback-ui.js deleted file mode 100644 index b1f263e..0000000 --- a/base/app/novnc/tests/playback-ui.js +++ /dev/null @@ -1,218 +0,0 @@ -/* global VNC_frame_data, VNC_frame_encoding */ - -import * as WebUtil from '../app/webutil.js'; -import RecordingPlayer from './playback.js'; -import Base64 from '../core/base64.js'; - -let frames = null; - -function message(str) { - const cell = document.getElementById('messages'); - cell.textContent += str + "\n"; - cell.scrollTop = cell.scrollHeight; -} - -function loadFile() { - const fname = WebUtil.getQueryVar('data', null); - - if (!fname) { - return Promise.reject("Must specify data=FOO in query string."); - } - - message("Loading " + fname + "..."); - - return new Promise((resolve, reject) => { - const script = document.createElement("script"); - script.onload = resolve; - script.onerror = () => { reject("Failed to load " + fname); }; - document.body.appendChild(script); - script.src = "../recordings/" + fname; - }); -} - -function enableUI() { - const iterations = WebUtil.getQueryVar('iterations', 3); - document.getElementById('iterations').value = iterations; - - const mode = WebUtil.getQueryVar('mode', 3); - if (mode === 'realtime') { - document.getElementById('mode2').checked = true; - } else { - document.getElementById('mode1').checked = true; - } - - /* eslint-disable-next-line camelcase */ - message("Loaded " + VNC_frame_data.length + " frames"); - - const startButton = document.getElementById('startButton'); - startButton.disabled = false; - startButton.addEventListener('click', start); - - message("Converting..."); - - /* eslint-disable-next-line camelcase */ - frames = VNC_frame_data; - - let encoding; - - /* eslint-disable camelcase */ - if (window.VNC_frame_encoding) { - // Only present in older recordings - encoding = VNC_frame_encoding; - /* eslint-enable camelcase */ - } else { - let frame = frames[0]; - let start = frame.indexOf('{', 1) + 1; - if (frame.slice(start, start+4) === 'UkZC') { - encoding = 'base64'; - } else { - encoding = 'binary'; - } - } - - for (let i = 0;i < frames.length;i++) { - let frame = frames[i]; - - if (frame === "EOF") { - frames.splice(i); - break; - } - - let dataIdx = frame.indexOf('{', 1) + 1; - - let time = parseInt(frame.slice(1, dataIdx - 1)); - - let u8; - if (encoding === 'base64') { - u8 = Base64.decode(frame.slice(dataIdx)); - } else { - u8 = new Uint8Array(frame.length - dataIdx); - for (let j = 0; j < frame.length - dataIdx; j++) { - u8[j] = frame.charCodeAt(dataIdx + j); - } - } - - frames[i] = { fromClient: frame[0] === '}', - timestamp: time, - data: u8 }; - } - - message("Ready"); -} - -class IterationPlayer { - constructor(iterations, frames) { - this._iterations = iterations; - - this._iteration = undefined; - this._player = undefined; - - this._startTime = undefined; - - this._frames = frames; - - this._state = 'running'; - - this.onfinish = () => {}; - this.oniterationfinish = () => {}; - this.rfbdisconnected = () => {}; - } - - start(realtime) { - this._iteration = 0; - this._startTime = (new Date()).getTime(); - - this._realtime = realtime; - - this._nextIteration(); - } - - _nextIteration() { - const player = new RecordingPlayer(this._frames, this._disconnected.bind(this)); - player.onfinish = this._iterationFinish.bind(this); - - if (this._state !== 'running') { return; } - - this._iteration++; - if (this._iteration > this._iterations) { - this._finish(); - return; - } - - player.run(this._realtime, false); - } - - _finish() { - const endTime = (new Date()).getTime(); - const totalDuration = endTime - this._startTime; - - const evt = new CustomEvent('finish', - { detail: - { duration: totalDuration, - iterations: this._iterations } } ); - this.onfinish(evt); - } - - _iterationFinish(duration) { - const evt = new CustomEvent('iterationfinish', - { detail: - { duration: duration, - number: this._iteration } } ); - this.oniterationfinish(evt); - - this._nextIteration(); - } - - _disconnected(clean, frame) { - if (!clean) { - this._state = 'failed'; - } - - const evt = new CustomEvent('rfbdisconnected', - { detail: - { clean: clean, - frame: frame, - iteration: this._iteration } } ); - this.onrfbdisconnected(evt); - } -} - -function start() { - document.getElementById('startButton').value = "Running"; - document.getElementById('startButton').disabled = true; - - const iterations = document.getElementById('iterations').value; - - let realtime; - - if (document.getElementById('mode1').checked) { - message(`Starting performance playback (fullspeed) [${iterations} iteration(s)]`); - realtime = false; - } else { - message(`Starting realtime playback [${iterations} iteration(s)]`); - realtime = true; - } - - const player = new IterationPlayer(iterations, frames); - player.oniterationfinish = (evt) => { - message(`Iteration ${evt.detail.number} took ${evt.detail.duration}ms`); - }; - player.onrfbdisconnected = (evt) => { - if (!evt.detail.clean) { - message(`noVNC sent disconnected during iteration ${evt.detail.iteration} frame ${evt.detail.frame}`); - - document.getElementById('startButton').disabled = false; - document.getElementById('startButton').value = "Start"; - } - }; - player.onfinish = (evt) => { - const iterTime = parseInt(evt.detail.duration / evt.detail.iterations, 10); - message(`${evt.detail.iterations} iterations took ${evt.detail.duration}ms (average ${iterTime}ms / iteration)`); - - document.getElementById('startButton').disabled = false; - document.getElementById('startButton').value = "Start"; - }; - player.start(realtime); -} - -loadFile().then(enableUI).catch(e => message("Error loading recording: " + e)); diff --git a/base/app/novnc/tests/playback.js b/base/app/novnc/tests/playback.js deleted file mode 100644 index 955df0e..0000000 --- a/base/app/novnc/tests/playback.js +++ /dev/null @@ -1,171 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2018 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - */ - -import RFB from '../core/rfb.js'; -import * as Log from '../core/util/logging.js'; - -// Immediate polyfill -if (window.setImmediate === undefined) { - let _immediateIdCounter = 1; - const _immediateFuncs = {}; - - window.setImmediate = (func) => { - const index = _immediateIdCounter++; - _immediateFuncs[index] = func; - window.postMessage("noVNC immediate trigger:" + index, "*"); - return index; - }; - - window.clearImmediate = (id) => { - _immediateFuncs[id]; - }; - - window.addEventListener("message", (event) => { - if ((typeof event.data !== "string") || - (event.data.indexOf("noVNC immediate trigger:") !== 0)) { - return; - } - - const index = event.data.slice("noVNC immediate trigger:".length); - - const callback = _immediateFuncs[index]; - if (callback === undefined) { - return; - } - - delete _immediateFuncs[index]; - - callback(); - }); -} - -class FakeWebSocket { - constructor() { - this.binaryType = "arraybuffer"; - this.protocol = ""; - this.readyState = "open"; - - this.onerror = () => {}; - this.onmessage = () => {}; - this.onopen = () => {}; - } - - send() { - } - - close() { - } -} - -export default class RecordingPlayer { - constructor(frames, disconnected) { - this._frames = frames; - - this._disconnected = disconnected; - - this._rfb = undefined; - this._frameLength = this._frames.length; - - this._frameIndex = 0; - this._startTime = undefined; - this._realtime = true; - this._trafficManagement = true; - - this._running = false; - - this.onfinish = () => {}; - } - - run(realtime, trafficManagement) { - // initialize a new RFB - this._ws = new FakeWebSocket(); - this._rfb = new RFB(document.getElementById('VNC_screen'), this._ws); - this._rfb.viewOnly = true; - this._rfb.addEventListener("disconnect", - this._handleDisconnect.bind(this)); - this._rfb.addEventListener("credentialsrequired", - this._handleCredentials.bind(this)); - - // reset the frame index and timer - this._frameIndex = 0; - this._startTime = (new Date()).getTime(); - - this._realtime = realtime; - this._trafficManagement = (trafficManagement === undefined) ? !realtime : trafficManagement; - - this._running = true; - this._queueNextPacket(); - } - - _queueNextPacket() { - if (!this._running) { return; } - - let frame = this._frames[this._frameIndex]; - - // skip send frames - while (this._frameIndex < this._frameLength && frame.fromClient) { - this._frameIndex++; - frame = this._frames[this._frameIndex]; - } - - if (this._frameIndex >= this._frameLength) { - Log.Debug('Finished, no more frames'); - this._finish(); - return; - } - - if (this._realtime) { - const toffset = (new Date()).getTime() - this._startTime; - let delay = frame.timestamp - toffset; - if (delay < 1) delay = 1; - - setTimeout(this._doPacket.bind(this), delay); - } else { - setImmediate(this._doPacket.bind(this)); - } - } - - _doPacket() { - // Avoid having excessive queue buildup in non-realtime mode - if (this._trafficManagement && this._rfb._flushing) { - this._rfb.flush() - .then(() => { - this._doPacket(); - }); - return; - } - - const frame = this._frames[this._frameIndex]; - - this._ws.onmessage({'data': frame.data}); - this._frameIndex++; - - this._queueNextPacket(); - } - - _finish() { - if (this._rfb._display.pending()) { - this._rfb._display.flush() - .then(() => { this._finish(); }); - } else { - this._running = false; - this._ws.onclose({code: 1000, reason: ""}); - delete this._rfb; - this.onfinish((new Date()).getTime() - this._startTime); - } - } - - _handleDisconnect(evt) { - this._running = false; - this._disconnected(evt.detail.clean, this._frameIndex); - } - - _handleCredentials(evt) { - this._rfb.sendCredentials({"username": "Foo", - "password": "Bar", - "target": "Baz"}); - } -} diff --git a/base/app/novnc/tests/test.base64.js b/base/app/novnc/tests/test.base64.js deleted file mode 100644 index 04bd207..0000000 --- a/base/app/novnc/tests/test.base64.js +++ /dev/null @@ -1,33 +0,0 @@ -const expect = chai.expect; - -import Base64 from '../core/base64.js'; - -describe('Base64 Tools', function () { - "use strict"; - - const BIN_ARR = new Array(256); - for (let i = 0; i < 256; i++) { - BIN_ARR[i] = i; - } - - const B64_STR = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w=="; - - - describe('encode', function () { - it('should encode a binary string into Base64', function () { - const encoded = Base64.encode(BIN_ARR); - expect(encoded).to.equal(B64_STR); - }); - }); - - describe('decode', function () { - it('should decode a Base64 string into a normal string', function () { - const decoded = Base64.decode(B64_STR); - expect(decoded).to.deep.equal(BIN_ARR); - }); - - it('should throw an error if we have extra characters at the end of the string', function () { - expect(() => Base64.decode(B64_STR+'abcdef')).to.throw(Error); - }); - }); -}); diff --git a/base/app/novnc/tests/test.browser.js b/base/app/novnc/tests/test.browser.js deleted file mode 100644 index 1beeb48..0000000 --- a/base/app/novnc/tests/test.browser.js +++ /dev/null @@ -1,243 +0,0 @@ -const expect = chai.expect; - -import { isMac, isWindows, isIOS, isAndroid, isChromeOS, - isSafari, isFirefox, isChrome, isChromium, isOpera, isEdge, - isGecko, isWebKit, isBlink } from '../core/util/browser.js'; - -describe('OS detection', function () { - let origNavigator; - beforeEach(function () { - // window.navigator is a protected read-only property in many - // environments, so we need to redefine it whilst running these - // tests. - origNavigator = Object.getOwnPropertyDescriptor(window, "navigator"); - - Object.defineProperty(window, "navigator", {value: {}}); - }); - - afterEach(function () { - Object.defineProperty(window, "navigator", origNavigator); - }); - - it('should handle macOS', function () { - const platforms = [ - "MacIntel", - "MacPPC", - ]; - - navigator.userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 12_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6 Safari/605.1.15"; - platforms.forEach((platform) => { - navigator.platform = platform; - expect(isMac()).to.be.true; - expect(isWindows()).to.be.false; - expect(isIOS()).to.be.false; - expect(isAndroid()).to.be.false; - expect(isChromeOS()).to.be.false; - }); - }); - - it('should handle Windows', function () { - const platforms = [ - "Win32", - "Win64", - ]; - - navigator.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36"; - platforms.forEach((platform) => { - navigator.platform = platform; - expect(isMac()).to.be.false; - expect(isWindows()).to.be.true; - expect(isIOS()).to.be.false; - expect(isAndroid()).to.be.false; - expect(isChromeOS()).to.be.false; - }); - }); - - it('should handle iOS', function () { - const platforms = [ - "iPhone", - "iPod", - "iPad", - ]; - - navigator.userAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 16_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.1 Mobile/15E148 Safari/604.1"; - platforms.forEach((platform) => { - navigator.platform = platform; - expect(isMac()).to.be.false; - expect(isWindows()).to.be.false; - expect(isIOS()).to.be.true; - expect(isAndroid()).to.be.false; - expect(isChromeOS()).to.be.false; - }); - }); - - it('should handle Android', function () { - let userAgents = [ - "Mozilla/5.0 (Linux; Android 13; SM-G960U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.128 Mobile Safari/537.36", - "Mozilla/5.0 (Android 13; Mobile; LG-M255; rv:108.0) Gecko/108.0 Firefox/108.0", - ]; - - navigator.platform = "Linux x86_64"; - userAgents.forEach((ua) => { - navigator.userAgent = ua; - expect(isMac()).to.be.false; - expect(isWindows()).to.be.false; - expect(isIOS()).to.be.false; - expect(isAndroid()).to.be.true; - expect(isChromeOS()).to.be.false; - }); - }); - - it('should handle ChromeOS', function () { - let userAgents = [ - "Mozilla/5.0 (X11; CrOS x86_64 15183.59.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.75 Safari/537.36", - "Mozilla/5.0 (X11; CrOS aarch64 15183.59.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.75 Safari/537.36", - ]; - - navigator.platform = "Linux x86_64"; - userAgents.forEach((ua) => { - navigator.userAgent = ua; - expect(isMac()).to.be.false; - expect(isWindows()).to.be.false; - expect(isIOS()).to.be.false; - expect(isAndroid()).to.be.false; - expect(isChromeOS()).to.be.true; - }); - }); -}); - -describe('Browser detection', function () { - let origNavigator; - beforeEach(function () { - // window.navigator is a protected read-only property in many - // environments, so we need to redefine it whilst running these - // tests. - origNavigator = Object.getOwnPropertyDescriptor(window, "navigator"); - - Object.defineProperty(window, "navigator", {value: {}}); - }); - - afterEach(function () { - Object.defineProperty(window, "navigator", origNavigator); - }); - - it('should handle Chrome', function () { - navigator.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36"; - - expect(isSafari()).to.be.false; - expect(isFirefox()).to.be.false; - expect(isChrome()).to.be.true; - expect(isChromium()).to.be.false; - expect(isOpera()).to.be.false; - expect(isEdge()).to.be.false; - - expect(isGecko()).to.be.false; - expect(isWebKit()).to.be.false; - expect(isBlink()).to.be.true; - }); - - it('should handle Chromium', function () { - navigator.userAgent = "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Raspbian Chromium/74.0.3729.157 Chrome/74.0.3729.157 Safari/537.36"; - - expect(isSafari()).to.be.false; - expect(isFirefox()).to.be.false; - expect(isChrome()).to.be.false; - expect(isChromium()).to.be.true; - expect(isOpera()).to.be.false; - expect(isEdge()).to.be.false; - - expect(isGecko()).to.be.false; - expect(isWebKit()).to.be.false; - expect(isBlink()).to.be.true; - }); - - it('should handle Firefox', function () { - navigator.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0"; - - expect(isSafari()).to.be.false; - expect(isFirefox()).to.be.true; - expect(isChrome()).to.be.false; - expect(isChromium()).to.be.false; - expect(isOpera()).to.be.false; - expect(isEdge()).to.be.false; - - expect(isGecko()).to.be.true; - expect(isWebKit()).to.be.false; - expect(isBlink()).to.be.false; - }); - - it('should handle Seamonkey', function () { - navigator.userAgent = "Mozilla/5.0 (Windows NT 6.1; rv:36.0) Gecko/20100101 Firefox/36.0 Seamonkey/2.33.1"; - - expect(isSafari()).to.be.false; - expect(isFirefox()).to.be.false; - expect(isChrome()).to.be.false; - expect(isChromium()).to.be.false; - expect(isOpera()).to.be.false; - expect(isEdge()).to.be.false; - - expect(isGecko()).to.be.true; - expect(isWebKit()).to.be.false; - expect(isBlink()).to.be.false; - }); - - it('should handle Safari', function () { - navigator.userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 12_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6 Safari/605.1.15"; - - expect(isSafari()).to.be.true; - expect(isFirefox()).to.be.false; - expect(isChrome()).to.be.false; - expect(isChromium()).to.be.false; - expect(isOpera()).to.be.false; - expect(isEdge()).to.be.false; - - expect(isGecko()).to.be.false; - expect(isWebKit()).to.be.true; - expect(isBlink()).to.be.false; - }); - - it('should handle Edge', function () { - navigator.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 Edg/106.0.1370.34"; - - expect(isSafari()).to.be.false; - expect(isFirefox()).to.be.false; - expect(isChrome()).to.be.false; - expect(isChromium()).to.be.false; - expect(isOpera()).to.be.false; - expect(isEdge()).to.be.true; - - expect(isGecko()).to.be.false; - expect(isWebKit()).to.be.false; - expect(isBlink()).to.be.true; - }); - - it('should handle Opera', function () { - navigator.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 OPR/91.0.4516.20"; - - expect(isSafari()).to.be.false; - expect(isFirefox()).to.be.false; - expect(isChrome()).to.be.false; - expect(isChromium()).to.be.false; - expect(isOpera()).to.be.true; - expect(isEdge()).to.be.false; - - expect(isGecko()).to.be.false; - expect(isWebKit()).to.be.false; - expect(isBlink()).to.be.true; - }); - - it('should handle Epiphany', function () { - navigator.userAgent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.0 Safari/605.1.15 Epiphany/605.1.15"; - - expect(isSafari()).to.be.false; - expect(isFirefox()).to.be.false; - expect(isChrome()).to.be.false; - expect(isChromium()).to.be.false; - expect(isOpera()).to.be.false; - expect(isEdge()).to.be.false; - - expect(isGecko()).to.be.false; - expect(isWebKit()).to.be.true; - expect(isBlink()).to.be.false; - }); -}); diff --git a/base/app/novnc/tests/test.copyrect.js b/base/app/novnc/tests/test.copyrect.js deleted file mode 100644 index a10cddc..0000000 --- a/base/app/novnc/tests/test.copyrect.js +++ /dev/null @@ -1,92 +0,0 @@ -const expect = chai.expect; - -import Websock from '../core/websock.js'; -import Display from '../core/display.js'; - -import CopyRectDecoder from '../core/decoders/copyrect.js'; - -import FakeWebSocket from './fake.websocket.js'; - -function testDecodeRect(decoder, x, y, width, height, data, display, depth) { - let sock; - let done = false; - - sock = new Websock; - sock.open("ws://example.com"); - - sock.on('message', () => { - done = decoder.decodeRect(x, y, width, height, sock, display, depth); - }); - - // Empty messages are filtered at multiple layers, so we need to - // do a direct call - if (data.length === 0) { - done = decoder.decodeRect(x, y, width, height, sock, display, depth); - } else { - sock._websocket._receiveData(new Uint8Array(data)); - } - - display.flip(); - - return done; -} - -describe('CopyRect Decoder', function () { - let decoder; - let display; - - before(FakeWebSocket.replace); - after(FakeWebSocket.restore); - - beforeEach(function () { - decoder = new CopyRectDecoder(); - display = new Display(document.createElement('canvas')); - display.resize(4, 4); - }); - - it('should handle the CopyRect encoding', function () { - // seed some initial data to copy - display.fillRect(0, 0, 4, 4, [ 0x11, 0x22, 0x33 ]); - display.fillRect(0, 0, 2, 2, [ 0x00, 0x00, 0xff ]); - display.fillRect(2, 0, 2, 2, [ 0x00, 0xff, 0x00 ]); - - let done; - done = testDecodeRect(decoder, 0, 2, 2, 2, - [0x00, 0x02, 0x00, 0x00], - display, 24); - expect(done).to.be.true; - done = testDecodeRect(decoder, 2, 2, 2, 2, - [0x00, 0x00, 0x00, 0x00], - display, 24); - expect(done).to.be.true; - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 - ]); - - expect(display).to.have.displayed(targetData); - }); - - it('should handle empty rects', function () { - display.fillRect(0, 0, 4, 4, [ 0x00, 0x00, 0xff ]); - display.fillRect(2, 0, 2, 2, [ 0x00, 0xff, 0x00 ]); - display.fillRect(0, 2, 2, 2, [ 0x00, 0xff, 0x00 ]); - - let done = testDecodeRect(decoder, 1, 2, 0, 0, - [0x00, 0x00, 0x00, 0x00], - display, 24); - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 - ]); - - expect(done).to.be.true; - expect(display).to.have.displayed(targetData); - }); -}); diff --git a/base/app/novnc/tests/test.deflator.js b/base/app/novnc/tests/test.deflator.js deleted file mode 100644 index a7e972e..0000000 --- a/base/app/novnc/tests/test.deflator.js +++ /dev/null @@ -1,81 +0,0 @@ -const expect = chai.expect; - -import { inflateInit, inflate } from "../vendor/pako/lib/zlib/inflate.js"; -import ZStream from "../vendor/pako/lib/zlib/zstream.js"; -import Deflator from "../core/deflator.js"; - -function _inflator(compText, expected) { - let strm = new ZStream(); - let chunkSize = 1024 * 10 * 10; - strm.output = new Uint8Array(chunkSize); - - inflateInit(strm, 5); - - if (expected > chunkSize) { - chunkSize = expected; - strm.output = new Uint8Array(chunkSize); - } - - /* eslint-disable camelcase */ - strm.input = compText; - strm.avail_in = strm.input.length; - strm.next_in = 0; - - strm.next_out = 0; - strm.avail_out = expected.length; - /* eslint-enable camelcase */ - - let ret = inflate(strm, 0); - - // Check that return code is not an error - expect(ret).to.be.greaterThan(-1); - - return new Uint8Array(strm.output.buffer, 0, strm.next_out); -} - -describe('Deflate data', function () { - - it('should be able to deflate messages', function () { - let deflator = new Deflator(); - - let text = "123asdf"; - let preText = new Uint8Array(text.length); - for (let i = 0; i < preText.length; i++) { - preText[i] = text.charCodeAt(i); - } - - let compText = deflator.deflate(preText); - - let inflatedText = _inflator(compText, text.length); - expect(inflatedText).to.array.equal(preText); - - }); - - it('should be able to deflate large messages', function () { - let deflator = new Deflator(); - - /* Generate a big string with random characters. Used because - repetition of letters might be deflated more effectively than - random ones. */ - let text = ""; - let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - for (let i = 0; i < 300000; i++) { - text += characters.charAt(Math.floor(Math.random() * characters.length)); - } - - let preText = new Uint8Array(text.length); - for (let i = 0; i < preText.length; i++) { - preText[i] = text.charCodeAt(i); - } - - let compText = deflator.deflate(preText); - - //Check that the compressed size is expected size - expect(compText.length).to.be.greaterThan((1024 * 10 * 10) * 2); - - let inflatedText = _inflator(compText, text.length); - - expect(inflatedText).to.array.equal(preText); - - }); -}); diff --git a/base/app/novnc/tests/test.display.js b/base/app/novnc/tests/test.display.js deleted file mode 100644 index e6c0406..0000000 --- a/base/app/novnc/tests/test.display.js +++ /dev/null @@ -1,395 +0,0 @@ -const expect = chai.expect; - -import Base64 from '../core/base64.js'; -import Display from '../core/display.js'; - -describe('Display/Canvas Helper', function () { - const checkedData = new Uint8ClampedArray([ - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 - ]); - - const basicData = new Uint8ClampedArray([0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0xff, 0xff, 0xff, 255]); - - function makeImageCanvas(inputData, width, height) { - const canvas = document.createElement('canvas'); - canvas.width = width; - canvas.height = height; - const ctx = canvas.getContext('2d'); - const data = new ImageData(inputData, width, height); - ctx.putImageData(data, 0, 0); - return canvas; - } - - function makeImagePng(inputData, width, height) { - const canvas = makeImageCanvas(inputData, width, height); - const url = canvas.toDataURL(); - const data = url.split(",")[1]; - return Base64.decode(data); - } - - describe('viewport handling', function () { - let display; - beforeEach(function () { - display = new Display(document.createElement('canvas')); - display.clipViewport = true; - display.resize(5, 5); - display.viewportChangeSize(3, 3); - display.viewportChangePos(1, 1); - }); - - it('should take viewport location into consideration when drawing images', function () { - display.resize(4, 4); - display.viewportChangeSize(2, 2); - display.drawImage(makeImageCanvas(basicData, 4, 1), 1, 1); - display.flip(); - - const expected = new Uint8Array(16); - for (let i = 0; i < 8; i++) { expected[i] = basicData[i]; } - for (let i = 8; i < 16; i++) { expected[i] = 0; } - expect(display).to.have.displayed(expected); - }); - - it('should resize the target canvas when resizing the viewport', function () { - display.viewportChangeSize(2, 2); - expect(display._target.width).to.equal(2); - expect(display._target.height).to.equal(2); - }); - - it('should move the viewport if necessary', function () { - display.viewportChangeSize(5, 5); - expect(display.absX(0)).to.equal(0); - expect(display.absY(0)).to.equal(0); - expect(display._target.width).to.equal(5); - expect(display._target.height).to.equal(5); - }); - - it('should limit the viewport to the framebuffer size', function () { - display.viewportChangeSize(6, 6); - expect(display._target.width).to.equal(5); - expect(display._target.height).to.equal(5); - }); - - it('should redraw when moving the viewport', function () { - display.flip = sinon.spy(); - display.viewportChangePos(-1, 1); - expect(display.flip).to.have.been.calledOnce; - }); - - it('should redraw when resizing the viewport', function () { - display.flip = sinon.spy(); - display.viewportChangeSize(2, 2); - expect(display.flip).to.have.been.calledOnce; - }); - - it('should show the entire framebuffer when disabling the viewport', function () { - display.clipViewport = false; - expect(display.absX(0)).to.equal(0); - expect(display.absY(0)).to.equal(0); - expect(display._target.width).to.equal(5); - expect(display._target.height).to.equal(5); - }); - - it('should ignore viewport changes when the viewport is disabled', function () { - display.clipViewport = false; - display.viewportChangeSize(2, 2); - display.viewportChangePos(1, 1); - expect(display.absX(0)).to.equal(0); - expect(display.absY(0)).to.equal(0); - expect(display._target.width).to.equal(5); - expect(display._target.height).to.equal(5); - }); - - it('should show the entire framebuffer just after enabling the viewport', function () { - display.clipViewport = false; - display.clipViewport = true; - expect(display.absX(0)).to.equal(0); - expect(display.absY(0)).to.equal(0); - expect(display._target.width).to.equal(5); - expect(display._target.height).to.equal(5); - }); - }); - - describe('resizing', function () { - let display; - beforeEach(function () { - display = new Display(document.createElement('canvas')); - display.clipViewport = false; - display.resize(4, 4); - }); - - it('should change the size of the logical canvas', function () { - display.resize(5, 7); - expect(display._fbWidth).to.equal(5); - expect(display._fbHeight).to.equal(7); - }); - - it('should keep the framebuffer data', function () { - display.fillRect(0, 0, 4, 4, [0xff, 0, 0]); - display.resize(2, 2); - display.flip(); - const expected = []; - for (let i = 0; i < 4 * 2*2; i += 4) { - expected[i] = 0xff; - expected[i+1] = expected[i+2] = 0; - expected[i+3] = 0xff; - } - expect(display).to.have.displayed(new Uint8Array(expected)); - }); - - describe('viewport', function () { - beforeEach(function () { - display.clipViewport = true; - display.viewportChangeSize(3, 3); - display.viewportChangePos(1, 1); - }); - - it('should keep the viewport position and size if possible', function () { - display.resize(6, 6); - expect(display.absX(0)).to.equal(1); - expect(display.absY(0)).to.equal(1); - expect(display._target.width).to.equal(3); - expect(display._target.height).to.equal(3); - }); - - it('should move the viewport if necessary', function () { - display.resize(3, 3); - expect(display.absX(0)).to.equal(0); - expect(display.absY(0)).to.equal(0); - expect(display._target.width).to.equal(3); - expect(display._target.height).to.equal(3); - }); - - it('should shrink the viewport if necessary', function () { - display.resize(2, 2); - expect(display.absX(0)).to.equal(0); - expect(display.absY(0)).to.equal(0); - expect(display._target.width).to.equal(2); - expect(display._target.height).to.equal(2); - }); - }); - }); - - describe('rescaling', function () { - let display; - let canvas; - - beforeEach(function () { - canvas = document.createElement('canvas'); - display = new Display(canvas); - display.clipViewport = true; - display.resize(4, 4); - display.viewportChangeSize(3, 3); - display.viewportChangePos(1, 1); - document.body.appendChild(canvas); - }); - - afterEach(function () { - document.body.removeChild(canvas); - }); - - it('should not change the bitmap size of the canvas', function () { - display.scale = 2.0; - expect(canvas.width).to.equal(3); - expect(canvas.height).to.equal(3); - }); - - it('should change the effective rendered size of the canvas', function () { - display.scale = 2.0; - expect(canvas.clientWidth).to.equal(6); - expect(canvas.clientHeight).to.equal(6); - }); - - it('should not change when resizing', function () { - display.scale = 2.0; - display.resize(5, 5); - expect(display.scale).to.equal(2.0); - expect(canvas.width).to.equal(3); - expect(canvas.height).to.equal(3); - expect(canvas.clientWidth).to.equal(6); - expect(canvas.clientHeight).to.equal(6); - }); - }); - - describe('autoscaling', function () { - let display; - let canvas; - - beforeEach(function () { - canvas = document.createElement('canvas'); - display = new Display(canvas); - display.clipViewport = true; - display.resize(4, 3); - display.viewportChangeSize(4, 3); - document.body.appendChild(canvas); - }); - - afterEach(function () { - document.body.removeChild(canvas); - }); - - it('should preserve aspect ratio while autoscaling', function () { - display.autoscale(16, 9); - expect(canvas.clientWidth / canvas.clientHeight).to.equal(4 / 3); - }); - - it('should use width to determine scale when the current aspect ratio is wider than the target', function () { - display.autoscale(9, 16); - expect(display.absX(9)).to.equal(4); - expect(display.absY(18)).to.equal(8); - expect(canvas.clientWidth).to.equal(9); - expect(canvas.clientHeight).to.equal(7); // round 9 / (4 / 3) - }); - - it('should use height to determine scale when the current aspect ratio is taller than the target', function () { - display.autoscale(16, 9); - expect(display.absX(9)).to.equal(3); - expect(display.absY(18)).to.equal(6); - expect(canvas.clientWidth).to.equal(12); // 16 * (4 / 3) - expect(canvas.clientHeight).to.equal(9); - - }); - - it('should not change the bitmap size of the canvas', function () { - display.autoscale(16, 9); - expect(canvas.width).to.equal(4); - expect(canvas.height).to.equal(3); - }); - }); - - describe('drawing', function () { - - // TODO(directxman12): improve the tests for each of the drawing functions to cover more than just the - // basic cases - let display; - beforeEach(function () { - display = new Display(document.createElement('canvas')); - display.resize(4, 4); - }); - - it('should not draw directly on the target canvas', function () { - display.fillRect(0, 0, 4, 4, [0xff, 0, 0]); - display.flip(); - display.fillRect(0, 0, 4, 4, [0, 0xff, 0]); - const expected = []; - for (let i = 0; i < 4 * display._fbWidth * display._fbHeight; i += 4) { - expected[i] = 0xff; - expected[i+1] = expected[i+2] = 0; - expected[i+3] = 0xff; - } - expect(display).to.have.displayed(new Uint8Array(expected)); - }); - - it('should support filling a rectangle with particular color via #fillRect', function () { - display.fillRect(0, 0, 4, 4, [0, 0xff, 0]); - display.fillRect(0, 0, 2, 2, [0, 0, 0xff]); - display.fillRect(2, 2, 2, 2, [0, 0, 0xff]); - display.flip(); - expect(display).to.have.displayed(checkedData); - }); - - it('should support copying an portion of the canvas via #copyImage', function () { - display.fillRect(0, 0, 4, 4, [0, 0xff, 0]); - display.fillRect(0, 0, 2, 2, [0, 0, 0xff]); - display.copyImage(0, 0, 2, 2, 2, 2); - display.flip(); - expect(display).to.have.displayed(checkedData); - }); - - it('should support drawing images via #imageRect', async function () { - display.imageRect(0, 0, 4, 4, "image/png", makeImagePng(checkedData, 4, 4)); - display.flip(); - await display.flush(); - expect(display).to.have.displayed(checkedData); - }); - - it('should support blit images with true color via #blitImage', function () { - display.blitImage(0, 0, 4, 4, checkedData, 0); - display.flip(); - expect(display).to.have.displayed(checkedData); - }); - - it('should support drawing an image object via #drawImage', function () { - const img = makeImageCanvas(checkedData, 4, 4); - display.drawImage(img, 0, 0); - display.flip(); - expect(display).to.have.displayed(checkedData); - }); - }); - - describe('the render queue processor', function () { - let display; - beforeEach(function () { - display = new Display(document.createElement('canvas')); - display.resize(4, 4); - sinon.spy(display, '_scanRenderQ'); - }); - - it('should try to process an item when it is pushed on, if nothing else is on the queue', function () { - display._renderQPush({ type: 'noop' }); // does nothing - expect(display._scanRenderQ).to.have.been.calledOnce; - }); - - it('should not try to process an item when it is pushed on if we are waiting for other items', function () { - display._renderQ.length = 2; - display._renderQPush({ type: 'noop' }); - expect(display._scanRenderQ).to.not.have.been.called; - }); - - it('should wait until an image is loaded to attempt to draw it and the rest of the queue', function () { - const img = { complete: false, width: 4, height: 4, addEventListener: sinon.spy() }; - display._renderQ = [{ type: 'img', x: 3, y: 4, width: 4, height: 4, img: img }, - { type: 'fill', x: 1, y: 2, width: 3, height: 4, color: 5 }]; - display.drawImage = sinon.spy(); - display.fillRect = sinon.spy(); - - display._scanRenderQ(); - expect(display.drawImage).to.not.have.been.called; - expect(display.fillRect).to.not.have.been.called; - expect(img.addEventListener).to.have.been.calledOnce; - - display._renderQ[0].img.complete = true; - display._scanRenderQ(); - expect(display.drawImage).to.have.been.calledOnce; - expect(display.fillRect).to.have.been.calledOnce; - expect(img.addEventListener).to.have.been.calledOnce; - }); - - it('should resolve promise when queue is flushed', async function () { - display.fillRect(0, 0, 4, 4, [0, 0xff, 0]); - let promise = display.flush(); - expect(promise).to.be.an.instanceOf(Promise); - await promise; - }); - - it('should draw a blit image on type "blit"', function () { - display.blitImage = sinon.spy(); - display._renderQPush({ type: 'blit', x: 3, y: 4, width: 5, height: 6, data: [7, 8, 9] }); - expect(display.blitImage).to.have.been.calledOnce; - expect(display.blitImage).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9], 0); - }); - - it('should copy a region on type "copy"', function () { - display.copyImage = sinon.spy(); - display._renderQPush({ type: 'copy', x: 3, y: 4, width: 5, height: 6, oldX: 7, oldY: 8 }); - expect(display.copyImage).to.have.been.calledOnce; - expect(display.copyImage).to.have.been.calledWith(7, 8, 3, 4, 5, 6); - }); - - it('should fill a rect with a given color on type "fill"', function () { - display.fillRect = sinon.spy(); - display._renderQPush({ type: 'fill', x: 3, y: 4, width: 5, height: 6, color: [7, 8, 9]}); - expect(display.fillRect).to.have.been.calledOnce; - expect(display.fillRect).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9]); - }); - - it('should draw an image from an image object on type "img" (if complete)', function () { - display.drawImage = sinon.spy(); - display._renderQPush({ type: 'img', x: 3, y: 4, img: { complete: true } }); - expect(display.drawImage).to.have.been.calledOnce; - expect(display.drawImage).to.have.been.calledWith({ complete: true }, 3, 4); - }); - }); -}); diff --git a/base/app/novnc/tests/test.gesturehandler.js b/base/app/novnc/tests/test.gesturehandler.js deleted file mode 100644 index 73356be..0000000 --- a/base/app/novnc/tests/test.gesturehandler.js +++ /dev/null @@ -1,1026 +0,0 @@ -const expect = chai.expect; - -import EventTargetMixin from '../core/util/eventtarget.js'; - -import GestureHandler from '../core/input/gesturehandler.js'; - -class DummyTarget extends EventTargetMixin { -} - -describe('Gesture handler', function () { - let target, handler; - let gestures; - let clock; - let touches; - - before(function () { - clock = sinon.useFakeTimers(); - }); - - after(function () { - clock.restore(); - }); - - beforeEach(function () { - target = new DummyTarget(); - gestures = sinon.spy(); - target.addEventListener('gesturestart', gestures); - target.addEventListener('gesturemove', gestures); - target.addEventListener('gestureend', gestures); - touches = []; - handler = new GestureHandler(); - handler.attach(target); - }); - - afterEach(function () { - if (handler) { - handler.detach(); - } - target = null; - gestures = null; - }); - - function touchStart(id, x, y) { - let touch = { identifier: id, - clientX: x, clientY: y }; - touches.push(touch); - let ev = { type: 'touchstart', - touches: touches, - targetTouches: touches, - changedTouches: [ touch ], - stopPropagation: sinon.spy(), - preventDefault: sinon.spy() }; - target.dispatchEvent(ev); - } - - function touchMove(id, x, y) { - let touch = touches.find(t => t.identifier === id); - touch.clientX = x; - touch.clientY = y; - let ev = { type: 'touchmove', - touches: touches, - targetTouches: touches, - changedTouches: [ touch ], - stopPropagation: sinon.spy(), - preventDefault: sinon.spy() }; - target.dispatchEvent(ev); - } - - function touchEnd(id) { - let idx = touches.findIndex(t => t.identifier === id); - let touch = touches.splice(idx, 1)[0]; - let ev = { type: 'touchend', - touches: touches, - targetTouches: touches, - changedTouches: [ touch ], - stopPropagation: sinon.spy(), - preventDefault: sinon.spy() }; - target.dispatchEvent(ev); - } - - describe('Single finger tap', function () { - it('should handle single finger tap', function () { - touchStart(1, 20.0, 30.0); - - expect(gestures).to.not.have.been.called; - - touchEnd(1); - - expect(gestures).to.have.been.calledTwice; - - expect(gestures.firstCall).to.have.been.calledWith( - sinon.match({ type: 'gesturestart', - detail: { type: 'onetap', - clientX: 20.0, - clientY: 30.0 } })); - - expect(gestures.secondCall).to.have.been.calledWith( - sinon.match({ type: 'gestureend', - detail: { type: 'onetap', - clientX: 20.0, - clientY: 30.0 } })); - }); - }); - - describe('Two finger tap', function () { - it('should handle two finger tap', function () { - touchStart(1, 20.0, 30.0); - touchStart(2, 30.0, 50.0); - - expect(gestures).to.not.have.been.called; - - touchEnd(1); - - expect(gestures).to.not.have.been.called; - - touchEnd(2); - - expect(gestures).to.have.been.calledTwice; - - expect(gestures.firstCall).to.have.been.calledWith( - sinon.match({ type: 'gesturestart', - detail: { type: 'twotap', - clientX: 25.0, - clientY: 40.0 } })); - - expect(gestures.secondCall).to.have.been.calledWith( - sinon.match({ type: 'gestureend', - detail: { type: 'twotap', - clientX: 25.0, - clientY: 40.0 } })); - }); - - it('should ignore slow starting two finger tap', function () { - touchStart(1, 20.0, 30.0); - - clock.tick(500); - - touchStart(2, 30.0, 50.0); - touchEnd(1); - touchEnd(2); - - expect(gestures).to.not.have.been.called; - }); - - it('should ignore slow ending two finger tap', function () { - touchStart(1, 20.0, 30.0); - touchStart(2, 30.0, 50.0); - touchEnd(1); - - clock.tick(500); - - touchEnd(2); - - expect(gestures).to.not.have.been.called; - }); - - it('should ignore slow two finger tap', function () { - touchStart(1, 20.0, 30.0); - touchStart(2, 30.0, 50.0); - - clock.tick(1500); - - touchEnd(1); - touchEnd(2); - - expect(gestures).to.not.have.been.called; - }); - }); - - describe('Three finger tap', function () { - it('should handle three finger tap', function () { - touchStart(1, 20.0, 30.0); - touchStart(2, 30.0, 50.0); - touchStart(3, 40.0, 40.0); - - expect(gestures).to.not.have.been.called; - - touchEnd(1); - - expect(gestures).to.not.have.been.called; - - touchEnd(2); - - expect(gestures).to.not.have.been.called; - - touchEnd(3); - - expect(gestures).to.have.been.calledTwice; - - expect(gestures.firstCall).to.have.been.calledWith( - sinon.match({ type: 'gesturestart', - detail: { type: 'threetap', - clientX: 30.0, - clientY: 40.0 } })); - - expect(gestures.secondCall).to.have.been.calledWith( - sinon.match({ type: 'gestureend', - detail: { type: 'threetap', - clientX: 30.0, - clientY: 40.0 } })); - }); - - it('should ignore slow starting three finger tap', function () { - touchStart(1, 20.0, 30.0); - touchStart(2, 30.0, 50.0); - - clock.tick(500); - - touchStart(3, 40.0, 40.0); - touchEnd(1); - touchEnd(2); - touchEnd(3); - - expect(gestures).to.not.have.been.called; - }); - - it('should ignore slow ending three finger tap', function () { - touchStart(1, 20.0, 30.0); - touchStart(2, 30.0, 50.0); - touchStart(3, 40.0, 40.0); - touchEnd(1); - touchEnd(2); - - clock.tick(500); - - touchEnd(3); - - expect(gestures).to.not.have.been.called; - }); - - it('should ignore three finger drag', function () { - touchStart(1, 20.0, 30.0); - touchStart(2, 30.0, 50.0); - touchStart(3, 40.0, 40.0); - - touchMove(1, 120.0, 130.0); - touchMove(2, 130.0, 150.0); - touchMove(3, 140.0, 140.0); - - touchEnd(1); - touchEnd(2); - touchEnd(3); - - expect(gestures).to.not.have.been.called; - }); - - it('should ignore slow three finger tap', function () { - touchStart(1, 20.0, 30.0); - touchStart(2, 30.0, 50.0); - touchStart(3, 40.0, 40.0); - - clock.tick(1500); - - touchEnd(1); - touchEnd(2); - touchEnd(3); - - expect(gestures).to.not.have.been.called; - }); - }); - - describe('Single finger drag', function () { - it('should handle horizontal single finger drag', function () { - touchStart(1, 20.0, 30.0); - - expect(gestures).to.not.have.been.called; - - touchMove(1, 40.0, 30.0); - - expect(gestures).to.not.have.been.called; - - touchMove(1, 80.0, 30.0); - - expect(gestures).to.have.been.calledTwice; - - expect(gestures.firstCall).to.have.been.calledWith( - sinon.match({ type: 'gesturestart', - detail: { type: 'drag', - clientX: 20.0, - clientY: 30.0 } })); - - expect(gestures.secondCall).to.have.been.calledWith( - sinon.match({ type: 'gesturemove', - detail: { type: 'drag', - clientX: 80.0, - clientY: 30.0 } })); - - gestures.resetHistory(); - - touchEnd(1); - - expect(gestures).to.have.been.calledOnceWith( - sinon.match({ type: 'gestureend', - detail: { type: 'drag', - clientX: 80.0, - clientY: 30.0 } })); - }); - - it('should handle vertical single finger drag', function () { - touchStart(1, 20.0, 30.0); - - expect(gestures).to.not.have.been.called; - - touchMove(1, 20.0, 50.0); - - expect(gestures).to.not.have.been.called; - - touchMove(1, 20.0, 90.0); - - expect(gestures).to.have.been.calledTwice; - - expect(gestures.firstCall).to.have.been.calledWith( - sinon.match({ type: 'gesturestart', - detail: { type: 'drag', - clientX: 20.0, - clientY: 30.0 } })); - - expect(gestures.secondCall).to.have.been.calledWith( - sinon.match({ type: 'gesturemove', - detail: { type: 'drag', - clientX: 20.0, - clientY: 90.0 } })); - - gestures.resetHistory(); - - touchEnd(1); - - expect(gestures).to.have.been.calledOnceWith( - sinon.match({ type: 'gestureend', - detail: { type: 'drag', - clientX: 20.0, - clientY: 90.0 } })); - }); - - it('should handle diagonal single finger drag', function () { - touchStart(1, 120.0, 130.0); - - expect(gestures).to.not.have.been.called; - - touchMove(1, 90.0, 100.0); - - expect(gestures).to.not.have.been.called; - - touchMove(1, 60.0, 70.0); - - expect(gestures).to.have.been.calledTwice; - - expect(gestures.firstCall).to.have.been.calledWith( - sinon.match({ type: 'gesturestart', - detail: { type: 'drag', - clientX: 120.0, - clientY: 130.0 } })); - - expect(gestures.secondCall).to.have.been.calledWith( - sinon.match({ type: 'gesturemove', - detail: { type: 'drag', - clientX: 60.0, - clientY: 70.0 } })); - - gestures.resetHistory(); - - touchEnd(1); - - expect(gestures).to.have.been.calledOnceWith( - sinon.match({ type: 'gestureend', - detail: { type: 'drag', - clientX: 60.0, - clientY: 70.0 } })); - }); - }); - - describe('Long press', function () { - it('should handle long press', function () { - touchStart(1, 20.0, 30.0); - - expect(gestures).to.not.have.been.called; - - clock.tick(1500); - - expect(gestures).to.have.been.calledOnceWith( - sinon.match({ type: 'gesturestart', - detail: { type: 'longpress', - clientX: 20.0, - clientY: 30.0 } })); - - gestures.resetHistory(); - - touchEnd(1); - - expect(gestures).to.have.been.calledOnceWith( - sinon.match({ type: 'gestureend', - detail: { type: 'longpress', - clientX: 20.0, - clientY: 30.0 } })); - }); - - it('should handle long press drag', function () { - touchStart(1, 20.0, 30.0); - - expect(gestures).to.not.have.been.called; - - clock.tick(1500); - - expect(gestures).to.have.been.calledOnceWith( - sinon.match({ type: 'gesturestart', - detail: { type: 'longpress', - clientX: 20.0, - clientY: 30.0 } })); - - gestures.resetHistory(); - - touchMove(1, 120.0, 50.0); - - expect(gestures).to.have.been.calledOnceWith( - sinon.match({ type: 'gesturemove', - detail: { type: 'longpress', - clientX: 120.0, - clientY: 50.0 } })); - - gestures.resetHistory(); - - touchEnd(1); - - expect(gestures).to.have.been.calledOnceWith( - sinon.match({ type: 'gestureend', - detail: { type: 'longpress', - clientX: 120.0, - clientY: 50.0 } })); - }); - }); - - describe('Two finger drag', function () { - it('should handle fast and distinct horizontal two finger drag', function () { - touchStart(1, 20.0, 30.0); - touchStart(2, 30.0, 30.0); - - expect(gestures).to.not.have.been.called; - - touchMove(1, 40.0, 30.0); - touchMove(2, 50.0, 30.0); - - expect(gestures).to.not.have.been.called; - - touchMove(2, 90.0, 30.0); - touchMove(1, 80.0, 30.0); - - expect(gestures).to.have.been.calledTwice; - - expect(gestures.firstCall).to.have.been.calledWith( - sinon.match({ type: 'gesturestart', - detail: { type: 'twodrag', - clientX: 25.0, - clientY: 30.0, - magnitudeX: 0.0, - magnitudeY: 0.0 } })); - - expect(gestures.secondCall).to.have.been.calledWith( - sinon.match({ type: 'gesturemove', - detail: { type: 'twodrag', - clientX: 25.0, - clientY: 30.0, - magnitudeX: 60.0, - magnitudeY: 0.0 } })); - - gestures.resetHistory(); - - touchEnd(1); - - expect(gestures).to.have.been.calledOnceWith( - sinon.match({ type: 'gestureend', - detail: { type: 'twodrag', - clientX: 25.0, - clientY: 30.0, - magnitudeX: 60.0, - magnitudeY: 0.0 } })); - }); - - it('should handle fast and distinct vertical two finger drag', function () { - touchStart(1, 20.0, 30.0); - touchStart(2, 30.0, 30.0); - - expect(gestures).to.not.have.been.called; - - touchMove(1, 20.0, 100.0); - touchMove(2, 30.0, 40.0); - - expect(gestures).to.not.have.been.called; - - touchMove(2, 30.0, 90.0); - - expect(gestures).to.have.been.calledTwice; - - expect(gestures.firstCall).to.have.been.calledWith( - sinon.match({ type: 'gesturestart', - detail: { type: 'twodrag', - clientX: 25.0, - clientY: 30.0, - magnitudeX: 0.0, - magnitudeY: 0.0 } })); - - expect(gestures.secondCall).to.have.been.calledWith( - sinon.match({ type: 'gesturemove', - detail: { type: 'twodrag', - clientX: 25.0, - clientY: 30.0, - magnitudeX: 0.0, - magnitudeY: 65.0 } })); - - gestures.resetHistory(); - - touchEnd(1); - - expect(gestures).to.have.been.calledOnceWith( - sinon.match({ type: 'gestureend', - detail: { type: 'twodrag', - clientX: 25.0, - clientY: 30.0, - magnitudeX: 0.0, - magnitudeY: 65.0 } })); - }); - - it('should handle fast and distinct diagonal two finger drag', function () { - touchStart(1, 120.0, 130.0); - touchStart(2, 130.0, 130.0); - - expect(gestures).to.not.have.been.called; - - touchMove(1, 80.0, 90.0); - touchMove(2, 100.0, 130.0); - - expect(gestures).to.not.have.been.called; - - touchMove(2, 60.0, 70.0); - - expect(gestures).to.have.been.calledTwice; - - expect(gestures.firstCall).to.have.been.calledWith( - sinon.match({ type: 'gesturestart', - detail: { type: 'twodrag', - clientX: 125.0, - clientY: 130.0, - magnitudeX: 0.0, - magnitudeY: 0.0 } })); - - expect(gestures.secondCall).to.have.been.calledWith( - sinon.match({ type: 'gesturemove', - detail: { type: 'twodrag', - clientX: 125.0, - clientY: 130.0, - magnitudeX: -55.0, - magnitudeY: -50.0 } })); - - gestures.resetHistory(); - - touchEnd(1); - - expect(gestures).to.have.been.calledOnceWith( - sinon.match({ type: 'gestureend', - detail: { type: 'twodrag', - clientX: 125.0, - clientY: 130.0, - magnitudeX: -55.0, - magnitudeY: -50.0 } })); - }); - - it('should ignore fast almost two finger dragging', function () { - touchStart(1, 20.0, 30.0); - touchStart(2, 30.0, 30.0); - touchMove(1, 80.0, 30.0); - touchMove(2, 70.0, 30.0); - touchEnd(1); - touchEnd(2); - - expect(gestures).to.not.have.been.called; - - clock.tick(1500); - - expect(gestures).to.not.have.been.called; - }); - - it('should handle slow horizontal two finger drag', function () { - touchStart(1, 50.0, 40.0); - touchStart(2, 60.0, 40.0); - touchMove(1, 80.0, 40.0); - touchMove(2, 110.0, 40.0); - - expect(gestures).to.not.have.been.called; - - clock.tick(60); - - expect(gestures).to.have.been.calledTwice; - - expect(gestures.firstCall).to.have.been.calledWith( - sinon.match({ type: 'gesturestart', - detail: { type: 'twodrag', - clientX: 55.0, - clientY: 40.0, - magnitudeX: 0.0, - magnitudeY: 0.0 } })); - - expect(gestures.secondCall).to.have.been.calledWith( - sinon.match({ type: 'gesturemove', - detail: { type: 'twodrag', - clientX: 55.0, - clientY: 40.0, - magnitudeX: 40.0, - magnitudeY: 0.0 } })); - }); - - it('should handle slow vertical two finger drag', function () { - touchStart(1, 40.0, 40.0); - touchStart(2, 40.0, 60.0); - touchMove(2, 40.0, 80.0); - touchMove(1, 40.0, 100.0); - - expect(gestures).to.not.have.been.called; - - clock.tick(60); - - expect(gestures).to.have.been.calledTwice; - - expect(gestures.firstCall).to.have.been.calledWith( - sinon.match({ type: 'gesturestart', - detail: { type: 'twodrag', - clientX: 40.0, - clientY: 50.0, - magnitudeX: 0.0, - magnitudeY: 0.0 } })); - - expect(gestures.secondCall).to.have.been.calledWith( - sinon.match({ type: 'gesturemove', - detail: { type: 'twodrag', - clientX: 40.0, - clientY: 50.0, - magnitudeX: 0.0, - magnitudeY: 40.0 } })); - }); - - it('should handle slow diagonal two finger drag', function () { - touchStart(1, 50.0, 40.0); - touchStart(2, 40.0, 60.0); - touchMove(1, 70.0, 60.0); - touchMove(2, 90.0, 110.0); - - expect(gestures).to.not.have.been.called; - - clock.tick(60); - - expect(gestures).to.have.been.calledTwice; - - expect(gestures.firstCall).to.have.been.calledWith( - sinon.match({ type: 'gesturestart', - detail: { type: 'twodrag', - clientX: 45.0, - clientY: 50.0, - magnitudeX: 0.0, - magnitudeY: 0.0 } })); - - expect(gestures.secondCall).to.have.been.calledWith( - sinon.match({ type: 'gesturemove', - detail: { type: 'twodrag', - clientX: 45.0, - clientY: 50.0, - magnitudeX: 35.0, - magnitudeY: 35.0 } })); - }); - - it('should ignore too slow two finger drag', function () { - touchStart(1, 20.0, 30.0); - - clock.tick(500); - - touchStart(2, 30.0, 30.0); - touchMove(1, 40.0, 30.0); - touchMove(2, 50.0, 30.0); - touchMove(1, 80.0, 30.0); - - expect(gestures).to.not.have.been.called; - }); - }); - - describe('Pinch', function () { - it('should handle pinching distinctly and fast inwards', function () { - touchStart(1, 0.0, 0.0); - touchStart(2, 130.0, 130.0); - - expect(gestures).to.not.have.been.called; - - touchMove(1, 50.0, 40.0); - touchMove(2, 100.0, 130.0); - - expect(gestures).to.not.have.been.called; - - touchMove(2, 60.0, 70.0); - - expect(gestures).to.have.been.calledTwice; - - expect(gestures.firstCall).to.have.been.calledWith( - sinon.match({ type: 'gesturestart', - detail: { type: 'pinch', - clientX: 65.0, - clientY: 65.0, - magnitudeX: 130.0, - magnitudeY: 130.0 } })); - - expect(gestures.secondCall).to.have.been.calledWith( - sinon.match({ type: 'gesturemove', - detail: { type: 'pinch', - clientX: 65.0, - clientY: 65.0, - magnitudeX: 10.0, - magnitudeY: 30.0 } })); - - gestures.resetHistory(); - - touchEnd(1); - - expect(gestures).to.have.been.calledOnceWith( - sinon.match({ type: 'gestureend', - detail: { type: 'pinch', - clientX: 65.0, - clientY: 65.0, - magnitudeX: 10.0, - magnitudeY: 30.0 } })); - }); - - it('should handle pinching fast and distinctly outwards', function () { - touchStart(1, 100.0, 100.0); - touchStart(2, 110.0, 100.0); - - expect(gestures).to.not.have.been.called; - - touchMove(1, 130.0, 70.0); - touchMove(2, 0.0, 200.0); - - expect(gestures).to.not.have.been.called; - - touchMove(1, 180.0, 20.0); - - expect(gestures).to.have.been.calledTwice; - - expect(gestures.firstCall).to.have.been.calledWith( - sinon.match({ type: 'gesturestart', - detail: { type: 'pinch', - clientX: 105.0, - clientY: 100.0, - magnitudeX: 10.0, - magnitudeY: 0.0 } })); - - expect(gestures.secondCall).to.have.been.calledWith( - sinon.match({ type: 'gesturemove', - detail: { type: 'pinch', - clientX: 105.0, - clientY: 100.0, - magnitudeX: 180.0, - magnitudeY: 180.0 } })); - - gestures.resetHistory(); - - touchEnd(1); - - expect(gestures).to.have.been.calledOnceWith( - sinon.match({ type: 'gestureend', - detail: { type: 'pinch', - clientX: 105.0, - clientY: 100.0, - magnitudeX: 180.0, - magnitudeY: 180.0 } })); - }); - - it('should ignore fast almost pinching', function () { - touchStart(1, 20.0, 30.0); - touchStart(2, 130.0, 130.0); - touchMove(1, 80.0, 70.0); - touchEnd(1); - touchEnd(2); - - expect(gestures).to.not.have.been.called; - - clock.tick(1500); - - expect(gestures).to.not.have.been.called; - }); - - it('should handle pinching inwards slowly', function () { - touchStart(1, 0.0, 0.0); - touchStart(2, 130.0, 130.0); - touchMove(1, 50.0, 40.0); - touchMove(2, 100.0, 130.0); - - expect(gestures).to.not.have.been.called; - - clock.tick(60); - - expect(gestures).to.have.been.calledTwice; - - expect(gestures.firstCall).to.have.been.calledWith( - sinon.match({ type: 'gesturestart', - detail: { type: 'pinch', - clientX: 65.0, - clientY: 65.0, - magnitudeX: 130.0, - magnitudeY: 130.0 } })); - - expect(gestures.secondCall).to.have.been.calledWith( - sinon.match({ type: 'gesturemove', - detail: { type: 'pinch', - clientX: 65.0, - clientY: 65.0, - magnitudeX: 50.0, - magnitudeY: 90.0 } })); - }); - - it('should handle pinching outwards slowly', function () { - touchStart(1, 100.0, 130.0); - touchStart(2, 110.0, 130.0); - touchMove(2, 200.0, 130.0); - - expect(gestures).to.not.have.been.called; - - clock.tick(60); - - expect(gestures).to.have.been.calledTwice; - - expect(gestures.firstCall).to.have.been.calledWith( - sinon.match({ type: 'gesturestart', - detail: { type: 'pinch', - clientX: 105.0, - clientY: 130.0, - magnitudeX: 10.0, - magnitudeY: 0.0 } })); - - expect(gestures.secondCall).to.have.been.calledWith( - sinon.match({ type: 'gesturemove', - detail: { type: 'pinch', - clientX: 105.0, - clientY: 130.0, - magnitudeX: 100.0, - magnitudeY: 0.0 } })); - }); - - it('should ignore pinching too slowly', function () { - touchStart(1, 0.0, 0.0); - - clock.tick(500); - - touchStart(2, 130.0, 130.0); - touchMove(2, 100.0, 130.0); - touchMove(1, 50.0, 40.0); - - expect(gestures).to.not.have.been.called; - }); - }); - - describe('Ignoring', function () { - it('should ignore extra touches during gesture', function () { - touchStart(1, 20.0, 30.0); - touchMove(1, 40.0, 30.0); - touchMove(1, 80.0, 30.0); - - expect(gestures).to.have.been.calledTwice; - - expect(gestures.firstCall).to.have.been.calledWith( - sinon.match({ type: 'gesturestart', - detail: { type: 'drag' } })); - expect(gestures.secondCall).to.have.been.calledWith( - sinon.match({ type: 'gesturemove', - detail: { type: 'drag' } })); - - gestures.resetHistory(); - - touchStart(2, 10.0, 10.0); - - expect(gestures).to.not.have.been.called; - - touchMove(1, 100.0, 50.0); - - expect(gestures).to.have.been.calledOnceWith( - sinon.match({ type: 'gesturemove', - detail: { type: 'drag', - clientX: 100.0, - clientY: 50.0 } })); - - gestures.resetHistory(); - - touchEnd(1); - - expect(gestures).to.have.been.calledOnceWith( - sinon.match({ type: 'gestureend', - detail: { type: 'drag', - clientX: 100.0, - clientY: 50.0 } })); - }); - - it('should ignore extra touches when waiting for gesture to end', function () { - touchStart(1, 20.0, 30.0); - touchStart(2, 30.0, 30.0); - touchMove(1, 40.0, 30.0); - touchMove(2, 90.0, 30.0); - touchMove(1, 80.0, 30.0); - - expect(gestures).to.have.been.calledTwice; - - expect(gestures.firstCall).to.have.been.calledWith( - sinon.match({ type: 'gesturestart', - detail: { type: 'twodrag' } })); - expect(gestures.secondCall).to.have.been.calledWith( - sinon.match({ type: 'gesturemove', - detail: { type: 'twodrag' } })); - - gestures.resetHistory(); - - touchEnd(1); - - expect(gestures).to.have.been.calledOnceWith( - sinon.match({ type: 'gestureend', - detail: { type: 'twodrag' } })); - - gestures.resetHistory(); - - touchStart(3, 10.0, 10.0); - touchEnd(3); - - expect(gestures).to.not.have.been.called; - }); - - it('should ignore extra touches after gesture', function () { - touchStart(1, 20.0, 30.0); - touchMove(1, 40.0, 30.0); - touchMove(1, 80.0, 30.0); - - expect(gestures).to.have.been.calledTwice; - - expect(gestures.firstCall).to.have.been.calledWith( - sinon.match({ type: 'gesturestart', - detail: { type: 'drag' } })); - expect(gestures.secondCall).to.have.been.calledWith( - sinon.match({ type: 'gesturemove', - detail: { type: 'drag' } })); - - gestures.resetHistory(); - - touchStart(2, 10.0, 10.0); - - expect(gestures).to.not.have.been.called; - - touchMove(1, 100.0, 50.0); - - expect(gestures).to.have.been.calledOnceWith( - sinon.match({ type: 'gesturemove', - detail: { type: 'drag' } })); - - gestures.resetHistory(); - - touchEnd(1); - - expect(gestures).to.have.been.calledOnceWith( - sinon.match({ type: 'gestureend', - detail: { type: 'drag' } })); - - gestures.resetHistory(); - - touchEnd(2); - - expect(gestures).to.not.have.been.called; - - // Check that everything is reseted after trailing ignores are released - - touchStart(3, 20.0, 30.0); - touchEnd(3); - - expect(gestures).to.have.been.calledTwice; - - expect(gestures.firstCall).to.have.been.calledWith( - sinon.match({ type: 'gesturestart', - detail: { type: 'onetap' } })); - expect(gestures.secondCall).to.have.been.calledWith( - sinon.match({ type: 'gestureend', - detail: { type: 'onetap' } })); - }); - - it('should properly reset after a gesture', function () { - touchStart(1, 20.0, 30.0); - - expect(gestures).to.not.have.been.called; - - touchEnd(1); - - expect(gestures).to.have.been.calledTwice; - - expect(gestures.firstCall).to.have.been.calledWith( - sinon.match({ type: 'gesturestart', - detail: { type: 'onetap', - clientX: 20.0, - clientY: 30.0 } })); - - expect(gestures.secondCall).to.have.been.calledWith( - sinon.match({ type: 'gestureend', - detail: { type: 'onetap', - clientX: 20.0, - clientY: 30.0 } })); - - gestures.resetHistory(); - - touchStart(2, 70.0, 80.0); - - expect(gestures).to.not.have.been.called; - - touchEnd(2); - - expect(gestures).to.have.been.calledTwice; - - expect(gestures.firstCall).to.have.been.calledWith( - sinon.match({ type: 'gesturestart', - detail: { type: 'onetap', - clientX: 70.0, - clientY: 80.0 } })); - - expect(gestures.secondCall).to.have.been.calledWith( - sinon.match({ type: 'gestureend', - detail: { type: 'onetap', - clientX: 70.0, - clientY: 80.0 } })); - }); - }); -}); diff --git a/base/app/novnc/tests/test.helper.js b/base/app/novnc/tests/test.helper.js deleted file mode 100644 index 9995973..0000000 --- a/base/app/novnc/tests/test.helper.js +++ /dev/null @@ -1,209 +0,0 @@ -const expect = chai.expect; - -import keysyms from '../core/input/keysymdef.js'; -import * as KeyboardUtil from "../core/input/util.js"; - -describe('Helpers', function () { - "use strict"; - - describe('keysyms.lookup', function () { - it('should map ASCII characters to keysyms', function () { - expect(keysyms.lookup('a'.charCodeAt())).to.be.equal(0x61); - expect(keysyms.lookup('A'.charCodeAt())).to.be.equal(0x41); - }); - it('should map Latin-1 characters to keysyms', function () { - expect(keysyms.lookup('ø'.charCodeAt())).to.be.equal(0xf8); - - expect(keysyms.lookup('é'.charCodeAt())).to.be.equal(0xe9); - }); - it('should map characters that are in Windows-1252 but not in Latin-1 to keysyms', function () { - expect(keysyms.lookup('Š'.charCodeAt())).to.be.equal(0x01a9); - }); - it('should map characters which aren\'t in Latin1 *or* Windows-1252 to keysyms', function () { - expect(keysyms.lookup('ũ'.charCodeAt())).to.be.equal(0x03fd); - }); - it('should map unknown codepoints to the Unicode range', function () { - expect(keysyms.lookup('\n'.charCodeAt())).to.be.equal(0x100000a); - expect(keysyms.lookup('\u262D'.charCodeAt())).to.be.equal(0x100262d); - }); - // This requires very recent versions of most browsers... skipping for now - it.skip('should map UCS-4 codepoints to the Unicode range', function () { - //expect(keysyms.lookup('\u{1F686}'.codePointAt())).to.be.equal(0x101f686); - }); - }); - - describe('getKeycode', function () { - it('should pass through proper code', function () { - expect(KeyboardUtil.getKeycode({code: 'Semicolon'})).to.be.equal('Semicolon'); - }); - it('should map legacy values', function () { - expect(KeyboardUtil.getKeycode({code: ''})).to.be.equal('Unidentified'); - expect(KeyboardUtil.getKeycode({code: 'OSLeft'})).to.be.equal('MetaLeft'); - }); - it('should map keyCode to code when possible', function () { - expect(KeyboardUtil.getKeycode({keyCode: 0x14})).to.be.equal('CapsLock'); - expect(KeyboardUtil.getKeycode({keyCode: 0x5b})).to.be.equal('MetaLeft'); - expect(KeyboardUtil.getKeycode({keyCode: 0x35})).to.be.equal('Digit5'); - expect(KeyboardUtil.getKeycode({keyCode: 0x65})).to.be.equal('Numpad5'); - }); - it('should map keyCode left/right side', function () { - expect(KeyboardUtil.getKeycode({keyCode: 0x10, location: 1})).to.be.equal('ShiftLeft'); - expect(KeyboardUtil.getKeycode({keyCode: 0x10, location: 2})).to.be.equal('ShiftRight'); - expect(KeyboardUtil.getKeycode({keyCode: 0x11, location: 1})).to.be.equal('ControlLeft'); - expect(KeyboardUtil.getKeycode({keyCode: 0x11, location: 2})).to.be.equal('ControlRight'); - }); - it('should map keyCode on numpad', function () { - expect(KeyboardUtil.getKeycode({keyCode: 0x0d, location: 0})).to.be.equal('Enter'); - expect(KeyboardUtil.getKeycode({keyCode: 0x0d, location: 3})).to.be.equal('NumpadEnter'); - expect(KeyboardUtil.getKeycode({keyCode: 0x23, location: 0})).to.be.equal('End'); - expect(KeyboardUtil.getKeycode({keyCode: 0x23, location: 3})).to.be.equal('Numpad1'); - }); - it('should return Unidentified when it cannot map the keyCode', function () { - expect(KeyboardUtil.getKeycode({keycode: 0x42})).to.be.equal('Unidentified'); - }); - - describe('Fix Meta on macOS', function () { - let origNavigator; - beforeEach(function () { - // window.navigator is a protected read-only property in many - // environments, so we need to redefine it whilst running these - // tests. - origNavigator = Object.getOwnPropertyDescriptor(window, "navigator"); - - Object.defineProperty(window, "navigator", {value: {}}); - window.navigator.platform = "Mac x86_64"; - }); - afterEach(function () { - Object.defineProperty(window, "navigator", origNavigator); - }); - - it('should respect ContextMenu on modern browser', function () { - expect(KeyboardUtil.getKeycode({code: 'ContextMenu', keyCode: 0x5d})).to.be.equal('ContextMenu'); - }); - it('should translate legacy ContextMenu to MetaRight', function () { - expect(KeyboardUtil.getKeycode({keyCode: 0x5d})).to.be.equal('MetaRight'); - }); - }); - }); - - describe('getKey', function () { - it('should prefer key', function () { - expect(KeyboardUtil.getKey({key: 'a', charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.be.equal('a'); - }); - it('should map legacy values', function () { - expect(KeyboardUtil.getKey({key: 'OS'})).to.be.equal('Meta'); - expect(KeyboardUtil.getKey({key: 'UIKeyInputLeftArrow'})).to.be.equal('ArrowLeft'); - }); - it('should handle broken Delete', function () { - expect(KeyboardUtil.getKey({key: '\x00', code: 'NumpadDecimal'})).to.be.equal('Delete'); - }); - it('should use code if no key', function () { - expect(KeyboardUtil.getKey({code: 'NumpadBackspace'})).to.be.equal('Backspace'); - }); - it('should not use code fallback for character keys', function () { - expect(KeyboardUtil.getKey({code: 'KeyA'})).to.be.equal('Unidentified'); - expect(KeyboardUtil.getKey({code: 'Digit1'})).to.be.equal('Unidentified'); - expect(KeyboardUtil.getKey({code: 'Period'})).to.be.equal('Unidentified'); - expect(KeyboardUtil.getKey({code: 'Numpad1'})).to.be.equal('Unidentified'); - }); - it('should use charCode if no key', function () { - expect(KeyboardUtil.getKey({charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.be.equal('Š'); - // Broken Oculus browser - expect(KeyboardUtil.getKey({charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43, key: 'Unidentified'})).to.be.equal('Š'); - }); - it('should return Unidentified when it cannot map the key', function () { - expect(KeyboardUtil.getKey({keycode: 0x42})).to.be.equal('Unidentified'); - }); - }); - - describe('getKeysym', function () { - describe('Non-character keys', function () { - it('should recognize the right keys', function () { - expect(KeyboardUtil.getKeysym({key: 'Enter'})).to.be.equal(0xFF0D); - expect(KeyboardUtil.getKeysym({key: 'Backspace'})).to.be.equal(0xFF08); - expect(KeyboardUtil.getKeysym({key: 'Tab'})).to.be.equal(0xFF09); - expect(KeyboardUtil.getKeysym({key: 'Shift'})).to.be.equal(0xFFE1); - expect(KeyboardUtil.getKeysym({key: 'Control'})).to.be.equal(0xFFE3); - expect(KeyboardUtil.getKeysym({key: 'Alt'})).to.be.equal(0xFFE9); - expect(KeyboardUtil.getKeysym({key: 'Meta'})).to.be.equal(0xFFEB); - expect(KeyboardUtil.getKeysym({key: 'Escape'})).to.be.equal(0xFF1B); - expect(KeyboardUtil.getKeysym({key: 'ArrowUp'})).to.be.equal(0xFF52); - }); - it('should map left/right side', function () { - expect(KeyboardUtil.getKeysym({key: 'Shift', location: 1})).to.be.equal(0xFFE1); - expect(KeyboardUtil.getKeysym({key: 'Shift', location: 2})).to.be.equal(0xFFE2); - expect(KeyboardUtil.getKeysym({key: 'Control', location: 1})).to.be.equal(0xFFE3); - expect(KeyboardUtil.getKeysym({key: 'Control', location: 2})).to.be.equal(0xFFE4); - }); - it('should handle AltGraph', function () { - expect(KeyboardUtil.getKeysym({code: 'AltRight', key: 'Alt', location: 2})).to.be.equal(0xFFEA); - expect(KeyboardUtil.getKeysym({code: 'AltRight', key: 'AltGraph', location: 2})).to.be.equal(0xFE03); - }); - it('should handle Windows key with incorrect location', function () { - expect(KeyboardUtil.getKeysym({key: 'Meta', location: 0})).to.be.equal(0xFFEC); - }); - it('should handle Clear/NumLock key with incorrect location', function () { - this.skip(); // Broken because of Clear/NumLock override - expect(KeyboardUtil.getKeysym({key: 'Clear', code: 'NumLock', location: 3})).to.be.equal(0xFF0B); - }); - it('should handle Meta/Windows distinction', function () { - expect(KeyboardUtil.getKeysym({code: 'AltLeft', key: 'Meta', location: 1})).to.be.equal(0xFFE7); - expect(KeyboardUtil.getKeysym({code: 'AltRight', key: 'Meta', location: 2})).to.be.equal(0xFFE8); - expect(KeyboardUtil.getKeysym({code: 'MetaLeft', key: 'Meta', location: 1})).to.be.equal(0xFFEB); - expect(KeyboardUtil.getKeysym({code: 'MetaRight', key: 'Meta', location: 2})).to.be.equal(0xFFEC); - }); - it('should send NumLock even if key is Clear', function () { - expect(KeyboardUtil.getKeysym({key: 'Clear', code: 'NumLock'})).to.be.equal(0xFF7F); - }); - it('should return null for unknown keys', function () { - expect(KeyboardUtil.getKeysym({key: 'Semicolon'})).to.be.null; - expect(KeyboardUtil.getKeysym({key: 'BracketRight'})).to.be.null; - }); - it('should handle remappings', function () { - expect(KeyboardUtil.getKeysym({code: 'ControlLeft', key: 'Tab'})).to.be.equal(0xFF09); - }); - }); - - describe('Numpad', function () { - it('should handle Numpad numbers', function () { - expect(KeyboardUtil.getKeysym({code: 'Digit5', key: '5', location: 0})).to.be.equal(0x0035); - expect(KeyboardUtil.getKeysym({code: 'Numpad5', key: '5', location: 3})).to.be.equal(0xFFB5); - }); - it('should handle Numpad non-character keys', function () { - expect(KeyboardUtil.getKeysym({code: 'Home', key: 'Home', location: 0})).to.be.equal(0xFF50); - expect(KeyboardUtil.getKeysym({code: 'Numpad5', key: 'Home', location: 3})).to.be.equal(0xFF95); - expect(KeyboardUtil.getKeysym({code: 'Delete', key: 'Delete', location: 0})).to.be.equal(0xFFFF); - expect(KeyboardUtil.getKeysym({code: 'NumpadDecimal', key: 'Delete', location: 3})).to.be.equal(0xFF9F); - }); - it('should handle Numpad Decimal key', function () { - expect(KeyboardUtil.getKeysym({code: 'NumpadDecimal', key: '.', location: 3})).to.be.equal(0xFFAE); - expect(KeyboardUtil.getKeysym({code: 'NumpadDecimal', key: ',', location: 3})).to.be.equal(0xFFAC); - }); - }); - - describe('Japanese IM keys on Windows', function () { - let origNavigator; - beforeEach(function () { - // window.navigator is a protected read-only property in many - // environments, so we need to redefine it whilst running these - // tests. - origNavigator = Object.getOwnPropertyDescriptor(window, "navigator"); - - Object.defineProperty(window, "navigator", {value: {}}); - window.navigator.platform = "Windows"; - }); - - afterEach(function () { - Object.defineProperty(window, "navigator", origNavigator); - }); - - const keys = { 'Zenkaku': 0xff2a, 'Hankaku': 0xff2a, - 'Romaji': 0xff24, 'KanaMode': 0xff24 }; - for (let [key, keysym] of Object.entries(keys)) { - it(`should fake combined key for ${key} on Windows`, function () { - expect(KeyboardUtil.getKeysym({code: 'FakeIM', key: key})).to.be.equal(keysym); - }); - } - }); - }); -}); diff --git a/base/app/novnc/tests/test.hextile.js b/base/app/novnc/tests/test.hextile.js deleted file mode 100644 index cbe6f7b..0000000 --- a/base/app/novnc/tests/test.hextile.js +++ /dev/null @@ -1,242 +0,0 @@ -const expect = chai.expect; - -import Websock from '../core/websock.js'; -import Display from '../core/display.js'; - -import HextileDecoder from '../core/decoders/hextile.js'; - -import FakeWebSocket from './fake.websocket.js'; - -function testDecodeRect(decoder, x, y, width, height, data, display, depth) { - let sock; - let done = false; - - sock = new Websock; - sock.open("ws://example.com"); - - sock.on('message', () => { - done = decoder.decodeRect(x, y, width, height, sock, display, depth); - }); - - // Empty messages are filtered at multiple layers, so we need to - // do a direct call - if (data.length === 0) { - done = decoder.decodeRect(x, y, width, height, sock, display, depth); - } else { - sock._websocket._receiveData(new Uint8Array(data)); - } - - display.flip(); - - return done; -} - -function push32(arr, num) { - arr.push((num >> 24) & 0xFF, - (num >> 16) & 0xFF, - (num >> 8) & 0xFF, - num & 0xFF); -} - -describe('Hextile Decoder', function () { - let decoder; - let display; - - before(FakeWebSocket.replace); - after(FakeWebSocket.restore); - - beforeEach(function () { - decoder = new HextileDecoder(); - display = new Display(document.createElement('canvas')); - display.resize(4, 4); - }); - - it('should handle a tile with fg, bg specified, normal subrects', function () { - let data = []; - data.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects - push32(data, 0x00ff0000); // becomes 00ff0000 --> #00FF00 bg color - data.push(0x00); // becomes 0000ff00 --> #0000FF fg color - data.push(0x00); - data.push(0xff); - data.push(0x00); - data.push(2); // 2 subrects - data.push(0); // x: 0, y: 0 - data.push(1 | (1 << 4)); // width: 2, height: 2 - data.push(2 | (2 << 4)); // x: 2, y: 2 - data.push(1 | (1 << 4)); // width: 2, height: 2 - - let done = testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 - ]); - - expect(done).to.be.true; - expect(display).to.have.displayed(targetData); - }); - - it('should handle a raw tile', function () { - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 - ]); - - let data = []; - data.push(0x01); // raw - for (let i = 0; i < targetData.length; i += 4) { - data.push(targetData[i]); - data.push(targetData[i + 1]); - data.push(targetData[i + 2]); - // Last byte zero to test correct alpha handling - data.push(0); - } - - let done = testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); - - expect(done).to.be.true; - expect(display).to.have.displayed(targetData); - }); - - it('should handle a tile with only bg specified (solid bg)', function () { - let data = []; - data.push(0x02); - push32(data, 0x00ff0000); // becomes 00ff0000 --> #00FF00 bg color - - let done = testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); - - let expected = []; - for (let i = 0; i < 16; i++) { - push32(expected, 0x00ff00ff); - } - - expect(done).to.be.true; - expect(display).to.have.displayed(new Uint8Array(expected)); - }); - - it('should handle a tile with only bg specified and an empty frame afterwards', function () { - // set the width so we can have two tiles - display.resize(8, 4); - - let data = []; - - // send a bg frame - data.push(0x02); - push32(data, 0x00ff0000); // becomes 00ff0000 --> #00FF00 bg color - - // send an empty frame - data.push(0x00); - - let done = testDecodeRect(decoder, 0, 0, 32, 4, data, display, 24); - - let expected = []; - for (let i = 0; i < 16; i++) { - push32(expected, 0x00ff00ff); // rect 1: solid - } - for (let i = 0; i < 16; i++) { - push32(expected, 0x00ff00ff); // rect 2: same bkground color - } - - expect(done).to.be.true; - expect(display).to.have.displayed(new Uint8Array(expected)); - }); - - it('should handle a tile with bg and coloured subrects', function () { - let data = []; - data.push(0x02 | 0x08 | 0x10); // bg spec, anysubrects, colouredsubrects - push32(data, 0x00ff0000); // becomes 00ff0000 --> #00FF00 bg color - data.push(2); // 2 subrects - data.push(0x00); // becomes 0000ff00 --> #0000FF fg color - data.push(0x00); - data.push(0xff); - data.push(0x00); - data.push(0); // x: 0, y: 0 - data.push(1 | (1 << 4)); // width: 2, height: 2 - data.push(0x00); // becomes 0000ff00 --> #0000FF fg color - data.push(0x00); - data.push(0xff); - data.push(0x00); - data.push(2 | (2 << 4)); // x: 2, y: 2 - data.push(1 | (1 << 4)); // width: 2, height: 2 - - let done = testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 - ]); - - expect(done).to.be.true; - expect(display).to.have.displayed(targetData); - }); - - it('should carry over fg and bg colors from the previous tile if not specified', function () { - display.resize(4, 17); - - let data = []; - data.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects - push32(data, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color - data.push(0x00); // becomes 0000ffff --> #0000FF fg color - data.push(0x00); - data.push(0xff); - data.push(0xff); - data.push(8); // 8 subrects - for (let i = 0; i < 4; i++) { - data.push((0 << 4) | (i * 4)); // x: 0, y: i*4 - data.push(1 | (1 << 4)); // width: 2, height: 2 - data.push((2 << 4) | (i * 4 + 2)); // x: 2, y: i * 4 + 2 - data.push(1 | (1 << 4)); // width: 2, height: 2 - } - data.push(0x08); // anysubrects - data.push(1); // 1 subrect - data.push(0); // x: 0, y: 0 - data.push(1 | (1 << 4)); // width: 2, height: 2 - - let done = testDecodeRect(decoder, 0, 0, 4, 17, data, display, 24); - - let targetData = [ - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 - ]; - - let expected = []; - for (let i = 0; i < 4; i++) { - expected = expected.concat(targetData); - } - expected = expected.concat(targetData.slice(0, 16)); - - expect(done).to.be.true; - expect(display).to.have.displayed(new Uint8Array(expected)); - }); - - it('should fail on an invalid subencoding', function () { - let data = [45]; // an invalid subencoding - expect(() => testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24)).to.throw(); - }); - - it('should handle empty rects', function () { - display.fillRect(0, 0, 4, 4, [ 0x00, 0x00, 0xff ]); - display.fillRect(2, 0, 2, 2, [ 0x00, 0xff, 0x00 ]); - display.fillRect(0, 2, 2, 2, [ 0x00, 0xff, 0x00 ]); - - let done = testDecodeRect(decoder, 1, 2, 0, 0, [], display, 24); - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 - ]); - - expect(done).to.be.true; - expect(display).to.have.displayed(targetData); - }); -}); diff --git a/base/app/novnc/tests/test.inflator.js b/base/app/novnc/tests/test.inflator.js deleted file mode 100644 index 304e7a0..0000000 --- a/base/app/novnc/tests/test.inflator.js +++ /dev/null @@ -1,112 +0,0 @@ -const expect = chai.expect; - -import { deflateInit, deflate, Z_FULL_FLUSH } from "../vendor/pako/lib/zlib/deflate.js"; -import ZStream from "../vendor/pako/lib/zlib/zstream.js"; -import Inflator from "../core/inflator.js"; - -function _deflator(data) { - let strm = new ZStream(); - - deflateInit(strm, 5); - - /* eslint-disable camelcase */ - strm.input = data; - strm.avail_in = strm.input.length; - strm.next_in = 0; - /* eslint-enable camelcase */ - - let chunks = []; - let totalLen = 0; - while (strm.avail_in > 0) { - /* eslint-disable camelcase */ - strm.output = new Uint8Array(1024 * 10 * 10); - strm.avail_out = strm.output.length; - strm.next_out = 0; - /* eslint-enable camelcase */ - - let ret = deflate(strm, Z_FULL_FLUSH); - - // Check that return code is not an error - expect(ret).to.be.greaterThan(-1); - - let chunk = new Uint8Array(strm.output.buffer, 0, strm.next_out); - totalLen += chunk.length; - chunks.push(chunk); - } - - // Combine chunks into a single data - - let outData = new Uint8Array(totalLen); - let offset = 0; - - for (let i = 0; i < chunks.length; i++) { - outData.set(chunks[i], offset); - offset += chunks[i].length; - } - - return outData; -} - -describe('Inflate data', function () { - - it('should be able to inflate messages', function () { - let inflator = new Inflator(); - - let text = "123asdf"; - let preText = new Uint8Array(text.length); - for (let i = 0; i < preText.length; i++) { - preText[i] = text.charCodeAt(i); - } - - let compText = _deflator(preText); - - inflator.setInput(compText); - let inflatedText = inflator.inflate(preText.length); - - expect(inflatedText).to.array.equal(preText); - - }); - - it('should be able to inflate large messages', function () { - let inflator = new Inflator(); - - /* Generate a big string with random characters. Used because - repetition of letters might be deflated more effectively than - random ones. */ - let text = ""; - let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - for (let i = 0; i < 300000; i++) { - text += characters.charAt(Math.floor(Math.random() * characters.length)); - } - - let preText = new Uint8Array(text.length); - for (let i = 0; i < preText.length; i++) { - preText[i] = text.charCodeAt(i); - } - - let compText = _deflator(preText); - - //Check that the compressed size is expected size - expect(compText.length).to.be.greaterThan((1024 * 10 * 10) * 2); - - inflator.setInput(compText); - let inflatedText = inflator.inflate(preText.length); - - expect(inflatedText).to.array.equal(preText); - }); - - it('should throw an error on insufficient data', function () { - let inflator = new Inflator(); - - let text = "123asdf"; - let preText = new Uint8Array(text.length); - for (let i = 0; i < preText.length; i++) { - preText[i] = text.charCodeAt(i); - } - - let compText = _deflator(preText); - - inflator.setInput(compText); - expect(() => inflator.inflate(preText.length * 2)).to.throw(); - }); -}); diff --git a/base/app/novnc/tests/test.int.js b/base/app/novnc/tests/test.int.js deleted file mode 100644 index 084d68a..0000000 --- a/base/app/novnc/tests/test.int.js +++ /dev/null @@ -1,15 +0,0 @@ -const expect = chai.expect; - -import { toUnsigned32bit, toSigned32bit } from '../core/util/int.js'; - -describe('Integer casting', function () { - it('should cast unsigned to signed', function () { - let expected = 4294967286; - expect(toUnsigned32bit(-10)).to.equal(expected); - }); - - it('should cast signed to unsigned', function () { - let expected = -10; - expect(toSigned32bit(4294967286)).to.equal(expected); - }); -}); diff --git a/base/app/novnc/tests/test.jpeg.js b/base/app/novnc/tests/test.jpeg.js deleted file mode 100644 index 8dee489..0000000 --- a/base/app/novnc/tests/test.jpeg.js +++ /dev/null @@ -1,292 +0,0 @@ -const expect = chai.expect; - -import Websock from '../core/websock.js'; -import Display from '../core/display.js'; - -import JPEGDecoder from '../core/decoders/jpeg.js'; - -import FakeWebSocket from './fake.websocket.js'; - -function testDecodeRect(decoder, x, y, width, height, data, display, depth) { - let sock; - let done = false; - - sock = new Websock; - sock.open("ws://example.com"); - - sock.on('message', () => { - done = decoder.decodeRect(x, y, width, height, sock, display, depth); - }); - - // Empty messages are filtered at multiple layers, so we need to - // do a direct call - if (data.length === 0) { - done = decoder.decodeRect(x, y, width, height, sock, display, depth); - } else { - sock._websocket._receiveData(new Uint8Array(data)); - } - - display.flip(); - - return done; -} - -describe('JPEG Decoder', function () { - let decoder; - let display; - - before(FakeWebSocket.replace); - after(FakeWebSocket.restore); - - beforeEach(function () { - decoder = new JPEGDecoder(); - display = new Display(document.createElement('canvas')); - display.resize(4, 4); - }); - - it('should handle JPEG rects', async function () { - let data = [ - // JPEG data - 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, - 0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x01, 0x2c, - 0x01, 0x2c, 0x00, 0x42, 0xff, 0xdb, 0x00, 0x43, - 0x00, 0x03, 0x02, 0x02, 0x03, 0x02, 0x02, 0x03, - 0x03, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x05, - 0x08, 0x05, 0x05, 0x04, 0x04, 0x05, 0x0a, 0x07, - 0x07, 0x06, 0x08, 0x0c, 0x0a, 0x0c, 0x0c, 0x0b, - 0x0a, 0x0b, 0x0b, 0x0d, 0x0e, 0x12, 0x10, 0x0d, - 0x0e, 0x11, 0x0e, 0x0b, 0x0b, 0x10, 0x16, 0x10, - 0x11, 0x13, 0x14, 0x15, 0x15, 0x15, 0x0c, 0x0f, - 0x17, 0x18, 0x16, 0x14, 0x18, 0x12, 0x14, 0x15, - 0x14, 0xff, 0xdb, 0x00, 0x43, 0x01, 0x03, 0x04, - 0x04, 0x05, 0x04, 0x05, 0x09, 0x05, 0x05, 0x09, - 0x14, 0x0d, 0x0b, 0x0d, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xff, 0xc0, - 0x00, 0x11, 0x08, 0x00, 0x04, 0x00, 0x04, 0x03, - 0x01, 0x11, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, - 0x01, 0xff, 0xc4, 0x00, 0x1f, 0x00, 0x00, 0x01, - 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, - 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x10, 0x00, - 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, - 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, - 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, - 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, - 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, - 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, - 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, - 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, - 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, - 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, - 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, - 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, - 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, - 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, - 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, - 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, - 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, - 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, - 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, - 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, - 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, - 0xfa, 0xff, 0xc4, 0x00, 0x1f, 0x01, 0x00, 0x03, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, - 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x11, 0x00, - 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, - 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, - 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, - 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, - 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, - 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, - 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, - 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, - 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, - 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, - 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, - 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, - 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, - 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, - 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, - 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, - 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, - 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, - 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, - 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, - 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, - 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, - 0xfa, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, - 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0xf9, - 0xf7, 0xfb, 0x67, 0x56, 0xff, 0x00, 0x9f, 0xf8, - 0x3f, 0xf0, 0x51, 0xa7, 0xff, 0x00, 0xf2, 0x3d, - 0x7e, 0x6f, 0xfd, 0xab, 0x94, 0x7f, 0xd0, 0x9a, - 0x8f, 0xfe, 0x0d, 0xc7, 0x7f, 0xf3, 0x61, 0xfd, - 0xa7, 0xff, 0x00, 0x10, 0x77, 0x0d, 0xff, 0x00, - 0x43, 0xec, 0xcf, 0xff, 0x00, 0x0b, 0xab, 0x1f, - 0xff, 0xd9, - ]; - - let decodeDone = testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); - expect(decodeDone).to.be.true; - - let targetData = new Uint8Array([ - 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255 - ]); - - // Browsers have rounding errors, so we need an approximate - // comparing function - function almost(a, b) { - let diff = Math.abs(a - b); - return diff < 5; - } - - await display.flush(); - expect(display).to.have.displayed(targetData, almost); - }); - - it('should handle JPEG rects without Huffman and quantification tables', async function () { - let data1 = [ - // JPEG data - 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, - 0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x01, 0x2c, - 0x01, 0x2c, 0x00, 0x42, 0xff, 0xdb, 0x00, 0x43, - 0x00, 0x03, 0x02, 0x02, 0x03, 0x02, 0x02, 0x03, - 0x03, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x05, - 0x08, 0x05, 0x05, 0x04, 0x04, 0x05, 0x0a, 0x07, - 0x07, 0x06, 0x08, 0x0c, 0x0a, 0x0c, 0x0c, 0x0b, - 0x0a, 0x0b, 0x0b, 0x0d, 0x0e, 0x12, 0x10, 0x0d, - 0x0e, 0x11, 0x0e, 0x0b, 0x0b, 0x10, 0x16, 0x10, - 0x11, 0x13, 0x14, 0x15, 0x15, 0x15, 0x0c, 0x0f, - 0x17, 0x18, 0x16, 0x14, 0x18, 0x12, 0x14, 0x15, - 0x14, 0xff, 0xdb, 0x00, 0x43, 0x01, 0x03, 0x04, - 0x04, 0x05, 0x04, 0x05, 0x09, 0x05, 0x05, 0x09, - 0x14, 0x0d, 0x0b, 0x0d, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xff, 0xc0, - 0x00, 0x11, 0x08, 0x00, 0x04, 0x00, 0x04, 0x03, - 0x01, 0x11, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, - 0x01, 0xff, 0xc4, 0x00, 0x1f, 0x00, 0x00, 0x01, - 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, - 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x10, 0x00, - 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, - 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, - 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, - 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, - 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, - 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, - 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, - 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, - 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, - 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, - 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, - 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, - 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, - 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, - 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, - 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, - 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, - 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, - 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, - 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, - 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, - 0xfa, 0xff, 0xc4, 0x00, 0x1f, 0x01, 0x00, 0x03, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, - 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x11, 0x00, - 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, - 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, - 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, - 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, - 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, - 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, - 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, - 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, - 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, - 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, - 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, - 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, - 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, - 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, - 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, - 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, - 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, - 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, - 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, - 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, - 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, - 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, - 0xfa, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, - 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0xf9, - 0xf7, 0xfb, 0x67, 0x56, 0xff, 0x00, 0x9f, 0xf8, - 0x3f, 0xf0, 0x51, 0xa7, 0xff, 0x00, 0xf2, 0x3d, - 0x7e, 0x6f, 0xfd, 0xab, 0x94, 0x7f, 0xd0, 0x9a, - 0x8f, 0xfe, 0x0d, 0xc7, 0x7f, 0xf3, 0x61, 0xfd, - 0xa7, 0xff, 0x00, 0x10, 0x77, 0x0d, 0xff, 0x00, - 0x43, 0xec, 0xcf, 0xff, 0x00, 0x0b, 0xab, 0x1f, - 0xff, 0xd9, - ]; - - let decodeDone; - - decodeDone = testDecodeRect(decoder, 0, 0, 4, 4, data1, display, 24); - expect(decodeDone).to.be.true; - - display.fillRect(0, 0, 4, 4, [128, 128, 128, 255]); - - let data2 = [ - // JPEG data - 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, - 0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x01, 0x2c, - 0x01, 0x2c, 0x00, 0x73, 0xff, 0xc0, 0x00, 0x11, - 0x08, 0x00, 0x04, 0x00, 0x04, 0x03, 0x01, 0x11, - 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, - 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, - 0x03, 0x11, 0x00, 0x3f, 0x00, 0xf9, 0xf7, 0xfb, - 0x67, 0x56, 0xff, 0x00, 0x9f, 0xf8, 0x3f, 0xf0, - 0x51, 0xa7, 0xff, 0x00, 0xf2, 0x3d, 0x7e, 0x6f, - 0xfd, 0xab, 0x94, 0x7f, 0xd0, 0x9a, 0x8f, 0xfe, - 0x0d, 0xc7, 0x7f, 0xf3, 0x61, 0xfd, 0xa7, 0xff, - 0x00, 0x10, 0x77, 0x0d, 0xff, 0x00, 0x43, 0xec, - 0xcf, 0xff, 0x00, 0x0b, 0xab, 0x1f, 0xff, 0xd9, - ]; - - decodeDone = testDecodeRect(decoder, 0, 0, 4, 4, data2, display, 24); - expect(decodeDone).to.be.true; - - let targetData = new Uint8Array([ - 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255 - ]); - - // Browsers have rounding errors, so we need an approximate - // comparing function - function almost(a, b) { - let diff = Math.abs(a - b); - return diff < 5; - } - - await display.flush(); - expect(display).to.have.displayed(targetData, almost); - }); -}); diff --git a/base/app/novnc/tests/test.keyboard.js b/base/app/novnc/tests/test.keyboard.js deleted file mode 100644 index efc84c3..0000000 --- a/base/app/novnc/tests/test.keyboard.js +++ /dev/null @@ -1,591 +0,0 @@ -const expect = chai.expect; - -import Keyboard from '../core/input/keyboard.js'; - -describe('Key Event Handling', function () { - "use strict"; - - // The real KeyboardEvent constructor might not work everywhere we - // want to run these tests - function keyevent(typeArg, KeyboardEventInit) { - const e = { type: typeArg }; - for (let key in KeyboardEventInit) { - e[key] = KeyboardEventInit[key]; - } - e.stopPropagation = sinon.spy(); - e.preventDefault = sinon.spy(); - e.getModifierState = function (key) { - return e[key]; - }; - - return e; - } - - describe('Decode Keyboard Events', function () { - it('should decode keydown events', function (done) { - const kbd = new Keyboard(document); - kbd.onkeyevent = (keysym, code, down) => { - expect(keysym).to.be.equal(0x61); - expect(code).to.be.equal('KeyA'); - expect(down).to.be.equal(true); - done(); - }; - kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); - }); - it('should decode keyup events', function (done) { - let calls = 0; - const kbd = new Keyboard(document); - kbd.onkeyevent = (keysym, code, down) => { - expect(keysym).to.be.equal(0x61); - expect(code).to.be.equal('KeyA'); - if (calls++ === 1) { - expect(down).to.be.equal(false); - done(); - } - }; - kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); - kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'})); - }); - }); - - describe('Fake keyup', function () { - it('should fake keyup events for virtual keyboards', function (done) { - let count = 0; - const kbd = new Keyboard(document); - kbd.onkeyevent = (keysym, code, down) => { - switch (count++) { - case 0: - expect(keysym).to.be.equal(0x61); - expect(code).to.be.equal('Unidentified'); - expect(down).to.be.equal(true); - break; - case 1: - expect(keysym).to.be.equal(0x61); - expect(code).to.be.equal('Unidentified'); - expect(down).to.be.equal(false); - done(); - } - }; - kbd._handleKeyDown(keyevent('keydown', {code: 'Unidentified', key: 'a'})); - }); - }); - - describe('Track Key State', function () { - it('should send release using the same keysym as the press', function (done) { - const kbd = new Keyboard(document); - kbd.onkeyevent = (keysym, code, down) => { - expect(keysym).to.be.equal(0x61); - expect(code).to.be.equal('KeyA'); - if (!down) { - done(); - } - }; - kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); - kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'b'})); - }); - it('should send the same keysym for multiple presses', function () { - let count = 0; - const kbd = new Keyboard(document); - kbd.onkeyevent = (keysym, code, down) => { - expect(keysym).to.be.equal(0x61); - expect(code).to.be.equal('KeyA'); - expect(down).to.be.equal(true); - count++; - }; - kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); - kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'b'})); - expect(count).to.be.equal(2); - }); - it('should do nothing on keyup events if no keys are down', function () { - const kbd = new Keyboard(document); - kbd.onkeyevent = sinon.spy(); - kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'})); - expect(kbd.onkeyevent).to.not.have.been.called; - }); - - describe('Legacy Events', function () { - it('should track keys using keyCode if no code', function (done) { - const kbd = new Keyboard(document); - kbd.onkeyevent = (keysym, code, down) => { - expect(keysym).to.be.equal(0x61); - expect(code).to.be.equal('Platform65'); - if (!down) { - done(); - } - }; - kbd._handleKeyDown(keyevent('keydown', {keyCode: 65, key: 'a'})); - kbd._handleKeyUp(keyevent('keyup', {keyCode: 65, key: 'b'})); - }); - it('should ignore compositing code', function () { - const kbd = new Keyboard(document); - kbd.onkeyevent = (keysym, code, down) => { - expect(keysym).to.be.equal(0x61); - expect(code).to.be.equal('Unidentified'); - }; - kbd._handleKeyDown(keyevent('keydown', {keyCode: 229, key: 'a'})); - }); - it('should track keys using keyIdentifier if no code', function (done) { - const kbd = new Keyboard(document); - kbd.onkeyevent = (keysym, code, down) => { - expect(keysym).to.be.equal(0x61); - expect(code).to.be.equal('Platform65'); - if (!down) { - done(); - } - }; - kbd._handleKeyDown(keyevent('keydown', {keyIdentifier: 'U+0041', key: 'a'})); - kbd._handleKeyUp(keyevent('keyup', {keyIdentifier: 'U+0041', key: 'b'})); - }); - }); - }); - - describe('Shuffle modifiers on macOS', function () { - let origNavigator; - beforeEach(function () { - // window.navigator is a protected read-only property in many - // environments, so we need to redefine it whilst running these - // tests. - origNavigator = Object.getOwnPropertyDescriptor(window, "navigator"); - - Object.defineProperty(window, "navigator", {value: {}}); - window.navigator.platform = "Mac x86_64"; - }); - afterEach(function () { - Object.defineProperty(window, "navigator", origNavigator); - }); - - it('should change Alt to AltGraph', function () { - let count = 0; - const kbd = new Keyboard(document); - kbd.onkeyevent = (keysym, code, down) => { - switch (count++) { - case 0: - expect(keysym).to.be.equal(0xFF7E); - expect(code).to.be.equal('AltLeft'); - break; - case 1: - expect(keysym).to.be.equal(0xFE03); - expect(code).to.be.equal('AltRight'); - break; - } - }; - kbd._handleKeyDown(keyevent('keydown', {code: 'AltLeft', key: 'Alt', location: 1})); - kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2})); - expect(count).to.be.equal(2); - }); - it('should change left Super to Alt', function (done) { - const kbd = new Keyboard(document); - kbd.onkeyevent = (keysym, code, down) => { - expect(keysym).to.be.equal(0xFFE9); - expect(code).to.be.equal('MetaLeft'); - done(); - }; - kbd._handleKeyDown(keyevent('keydown', {code: 'MetaLeft', key: 'Meta', location: 1})); - }); - it('should change right Super to left Super', function (done) { - const kbd = new Keyboard(document); - kbd.onkeyevent = (keysym, code, down) => { - expect(keysym).to.be.equal(0xFFEB); - expect(code).to.be.equal('MetaRight'); - done(); - }; - kbd._handleKeyDown(keyevent('keydown', {code: 'MetaRight', key: 'Meta', location: 2})); - }); - }); - - describe('Meta key combination on iOS and macOS', function () { - let origNavigator; - beforeEach(function () { - // window.navigator is a protected read-only property in many - // environments, so we need to redefine it whilst running these - // tests. - origNavigator = Object.getOwnPropertyDescriptor(window, "navigator"); - - Object.defineProperty(window, "navigator", {value: {}}); - if (window.navigator.platform !== undefined) { - // Object.defineProperty() doesn't work properly in old - // versions of Chrome - this.skip(); - } - }); - - afterEach(function () { - if (origNavigator !== undefined) { - Object.defineProperty(window, "navigator", origNavigator); - } - }); - - it('should send keyup when meta key is pressed on iOS', function () { - window.navigator.platform = "iPad"; - const kbd = new Keyboard(document); - kbd.onkeyevent = sinon.spy(); - - kbd._handleKeyDown(keyevent('keydown', {code: 'MetaRight', key: 'Meta', location: 2, metaKey: true})); - expect(kbd.onkeyevent).to.have.been.calledOnce; - kbd.onkeyevent.resetHistory(); - - kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a', metaKey: true})); - expect(kbd.onkeyevent).to.have.been.calledTwice; - expect(kbd.onkeyevent).to.have.been.calledWith(0x61, "KeyA", true); - expect(kbd.onkeyevent).to.have.been.calledWith(0x61, "KeyA", false); - kbd.onkeyevent.resetHistory(); - - kbd._handleKeyUp(keyevent('keyup', {code: 'MetaRight', key: 'Meta', location: 2, metaKey: true})); - expect(kbd.onkeyevent).to.have.been.calledOnce; - }); - - it('should send keyup when meta key is pressed on macOS', function () { - window.navigator.platform = "Mac"; - const kbd = new Keyboard(document); - kbd.onkeyevent = sinon.spy(); - - kbd._handleKeyDown(keyevent('keydown', {code: 'MetaRight', key: 'Meta', location: 2, metaKey: true})); - expect(kbd.onkeyevent).to.have.been.calledOnce; - kbd.onkeyevent.resetHistory(); - - kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a', metaKey: true})); - expect(kbd.onkeyevent).to.have.been.calledTwice; - expect(kbd.onkeyevent).to.have.been.calledWith(0x61, "KeyA", true); - expect(kbd.onkeyevent).to.have.been.calledWith(0x61, "KeyA", false); - kbd.onkeyevent.resetHistory(); - - kbd._handleKeyUp(keyevent('keyup', {code: 'MetaRight', key: 'Meta', location: 2, metaKey: true})); - expect(kbd.onkeyevent).to.have.been.calledOnce; - }); - }); - - describe('Caps Lock on iOS and macOS', function () { - let origNavigator; - beforeEach(function () { - // window.navigator is a protected read-only property in many - // environments, so we need to redefine it whilst running these - // tests. - origNavigator = Object.getOwnPropertyDescriptor(window, "navigator"); - - Object.defineProperty(window, "navigator", {value: {}}); - }); - - afterEach(function () { - Object.defineProperty(window, "navigator", origNavigator); - }); - - it('should toggle caps lock on key press on iOS', function () { - window.navigator.platform = "iPad"; - const kbd = new Keyboard(document); - kbd.onkeyevent = sinon.spy(); - kbd._handleKeyDown(keyevent('keydown', {code: 'CapsLock', key: 'CapsLock'})); - - expect(kbd.onkeyevent).to.have.been.calledTwice; - expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true); - expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false); - }); - - it('should toggle caps lock on key press on mac', function () { - window.navigator.platform = "Mac"; - const kbd = new Keyboard(document); - kbd.onkeyevent = sinon.spy(); - kbd._handleKeyDown(keyevent('keydown', {code: 'CapsLock', key: 'CapsLock'})); - - expect(kbd.onkeyevent).to.have.been.calledTwice; - expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true); - expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false); - }); - - it('should toggle caps lock on key release on iOS', function () { - window.navigator.platform = "iPad"; - const kbd = new Keyboard(document); - kbd.onkeyevent = sinon.spy(); - kbd._handleKeyUp(keyevent('keyup', {code: 'CapsLock', key: 'CapsLock'})); - - expect(kbd.onkeyevent).to.have.been.calledTwice; - expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true); - expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false); - }); - - it('should toggle caps lock on key release on mac', function () { - window.navigator.platform = "Mac"; - const kbd = new Keyboard(document); - kbd.onkeyevent = sinon.spy(); - kbd._handleKeyUp(keyevent('keyup', {code: 'CapsLock', key: 'CapsLock'})); - - expect(kbd.onkeyevent).to.have.been.calledTwice; - expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true); - expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false); - }); - }); - - describe('Modifier status info', function () { - let origNavigator; - beforeEach(function () { - // window.navigator is a protected read-only property in many - // environments, so we need to redefine it whilst running these - // tests. - origNavigator = Object.getOwnPropertyDescriptor(window, "navigator"); - - Object.defineProperty(window, "navigator", {value: {}}); - }); - - afterEach(function () { - Object.defineProperty(window, "navigator", origNavigator); - }); - - it('should provide caps lock state', function () { - const kbd = new Keyboard(document); - kbd.onkeyevent = sinon.spy(); - kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'A', NumLock: false, CapsLock: true})); - - expect(kbd.onkeyevent).to.have.been.calledOnce; - expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0x41, "KeyA", true, false, true); - }); - - it('should provide num lock state', function () { - const kbd = new Keyboard(document); - kbd.onkeyevent = sinon.spy(); - kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'A', NumLock: true, CapsLock: false})); - - expect(kbd.onkeyevent).to.have.been.calledOnce; - expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0x41, "KeyA", true, true, false); - }); - - it('should have no num lock state on mac', function () { - window.navigator.platform = "Mac"; - const kbd = new Keyboard(document); - kbd.onkeyevent = sinon.spy(); - kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'A', NumLock: false, CapsLock: true})); - - expect(kbd.onkeyevent).to.have.been.calledOnce; - expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0x41, "KeyA", true, null, true); - }); - }); - - describe('Japanese IM keys on Windows', function () { - let origNavigator; - beforeEach(function () { - // window.navigator is a protected read-only property in many - // environments, so we need to redefine it whilst running these - // tests. - origNavigator = Object.getOwnPropertyDescriptor(window, "navigator"); - - Object.defineProperty(window, "navigator", {value: {}}); - window.navigator.platform = "Windows"; - }); - - afterEach(function () { - Object.defineProperty(window, "navigator", origNavigator); - }); - - const keys = { 'Zenkaku': 0xff2a, 'Hankaku': 0xff2a, - 'Alphanumeric': 0xff30, 'Katakana': 0xff26, - 'Hiragana': 0xff25, 'Romaji': 0xff24, - 'KanaMode': 0xff24 }; - for (let [key, keysym] of Object.entries(keys)) { - it(`should fake key release for ${key} on Windows`, function () { - let kbd = new Keyboard(document); - kbd.onkeyevent = sinon.spy(); - kbd._handleKeyDown(keyevent('keydown', {code: 'FakeIM', key: key})); - - expect(kbd.onkeyevent).to.have.been.calledTwice; - expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(keysym, "FakeIM", true); - expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(keysym, "FakeIM", false); - }); - } - }); - - describe('Escape AltGraph on Windows', function () { - let origNavigator; - beforeEach(function () { - // window.navigator is a protected read-only property in many - // environments, so we need to redefine it whilst running these - // tests. - origNavigator = Object.getOwnPropertyDescriptor(window, "navigator"); - - Object.defineProperty(window, "navigator", {value: {}}); - window.navigator.platform = "Windows x86_64"; - - this.clock = sinon.useFakeTimers(); - }); - afterEach(function () { - Object.defineProperty(window, "navigator", origNavigator); - if (this.clock !== undefined) { - this.clock.restore(); - } - }); - - it('should supress ControlLeft until it knows if it is AltGr', function () { - const kbd = new Keyboard(document); - kbd.onkeyevent = sinon.spy(); - kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1})); - expect(kbd.onkeyevent).to.not.have.been.called; - }); - - it('should not trigger on repeating ControlLeft', function () { - const kbd = new Keyboard(document); - kbd.onkeyevent = sinon.spy(); - kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1})); - kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1})); - expect(kbd.onkeyevent).to.have.been.calledTwice; - expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xffe3, "ControlLeft", true); - expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xffe3, "ControlLeft", true); - }); - - it('should not supress ControlRight', function () { - const kbd = new Keyboard(document); - kbd.onkeyevent = sinon.spy(); - kbd._handleKeyDown(keyevent('keydown', {code: 'ControlRight', key: 'Control', location: 2})); - expect(kbd.onkeyevent).to.have.been.calledOnce; - expect(kbd.onkeyevent).to.have.been.calledWith(0xffe4, "ControlRight", true); - }); - - it('should release ControlLeft after 100 ms', function () { - const kbd = new Keyboard(document); - kbd.onkeyevent = sinon.spy(); - kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1})); - expect(kbd.onkeyevent).to.not.have.been.called; - this.clock.tick(100); - expect(kbd.onkeyevent).to.have.been.calledOnce; - expect(kbd.onkeyevent).to.have.been.calledWith(0xffe3, "ControlLeft", true); - }); - - it('should release ControlLeft on other key press', function () { - const kbd = new Keyboard(document); - kbd.onkeyevent = sinon.spy(); - kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1})); - expect(kbd.onkeyevent).to.not.have.been.called; - kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); - expect(kbd.onkeyevent).to.have.been.calledTwice; - expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xffe3, "ControlLeft", true); - expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0x61, "KeyA", true); - - // Check that the timer is properly dead - kbd.onkeyevent.resetHistory(); - this.clock.tick(100); - expect(kbd.onkeyevent).to.not.have.been.called; - }); - - it('should release ControlLeft on other key release', function () { - const kbd = new Keyboard(document); - kbd.onkeyevent = sinon.spy(); - kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'})); - kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1})); - expect(kbd.onkeyevent).to.have.been.calledOnce; - expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0x61, "KeyA", true); - kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'})); - expect(kbd.onkeyevent).to.have.been.calledThrice; - expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xffe3, "ControlLeft", true); - expect(kbd.onkeyevent.thirdCall).to.have.been.calledWith(0x61, "KeyA", false); - - // Check that the timer is properly dead - kbd.onkeyevent.resetHistory(); - this.clock.tick(100); - expect(kbd.onkeyevent).to.not.have.been.called; - }); - - it('should generate AltGraph for quick Ctrl+Alt sequence', function () { - const kbd = new Keyboard(document); - kbd.onkeyevent = sinon.spy(); - kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1, timeStamp: Date.now()})); - this.clock.tick(20); - kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2, timeStamp: Date.now()})); - expect(kbd.onkeyevent).to.have.been.calledOnce; - expect(kbd.onkeyevent).to.have.been.calledWith(0xfe03, 'AltRight', true); - - // Check that the timer is properly dead - kbd.onkeyevent.resetHistory(); - this.clock.tick(100); - expect(kbd.onkeyevent).to.not.have.been.called; - }); - - it('should generate Ctrl, Alt for slow Ctrl+Alt sequence', function () { - const kbd = new Keyboard(document); - kbd.onkeyevent = sinon.spy(); - kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1, timeStamp: Date.now()})); - this.clock.tick(60); - kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2, timeStamp: Date.now()})); - expect(kbd.onkeyevent).to.have.been.calledTwice; - expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xffe3, "ControlLeft", true); - expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xffea, "AltRight", true); - - // Check that the timer is properly dead - kbd.onkeyevent.resetHistory(); - this.clock.tick(100); - expect(kbd.onkeyevent).to.not.have.been.called; - }); - - it('should pass through single Alt', function () { - const kbd = new Keyboard(document); - kbd.onkeyevent = sinon.spy(); - kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2})); - expect(kbd.onkeyevent).to.have.been.calledOnce; - expect(kbd.onkeyevent).to.have.been.calledWith(0xffea, 'AltRight', true); - }); - - it('should pass through single AltGr', function () { - const kbd = new Keyboard(document); - kbd.onkeyevent = sinon.spy(); - kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'AltGraph', location: 2})); - expect(kbd.onkeyevent).to.have.been.calledOnce; - expect(kbd.onkeyevent).to.have.been.calledWith(0xfe03, 'AltRight', true); - }); - }); - - describe('Missing Shift keyup on Windows', function () { - let origNavigator; - beforeEach(function () { - // window.navigator is a protected read-only property in many - // environments, so we need to redefine it whilst running these - // tests. - origNavigator = Object.getOwnPropertyDescriptor(window, "navigator"); - - Object.defineProperty(window, "navigator", {value: {}}); - window.navigator.platform = "Windows x86_64"; - - this.clock = sinon.useFakeTimers(); - }); - afterEach(function () { - Object.defineProperty(window, "navigator", origNavigator); - if (this.clock !== undefined) { - this.clock.restore(); - } - }); - - it('should fake a left Shift keyup', function () { - const kbd = new Keyboard(document); - kbd.onkeyevent = sinon.spy(); - - kbd._handleKeyDown(keyevent('keydown', {code: 'ShiftLeft', key: 'Shift', location: 1})); - expect(kbd.onkeyevent).to.have.been.calledOnce; - expect(kbd.onkeyevent).to.have.been.calledWith(0xffe1, 'ShiftLeft', true); - kbd.onkeyevent.resetHistory(); - - kbd._handleKeyDown(keyevent('keydown', {code: 'ShiftRight', key: 'Shift', location: 2})); - expect(kbd.onkeyevent).to.have.been.calledOnce; - expect(kbd.onkeyevent).to.have.been.calledWith(0xffe2, 'ShiftRight', true); - kbd.onkeyevent.resetHistory(); - - kbd._handleKeyUp(keyevent('keyup', {code: 'ShiftLeft', key: 'Shift', location: 1})); - expect(kbd.onkeyevent).to.have.been.calledTwice; - expect(kbd.onkeyevent).to.have.been.calledWith(0xffe2, 'ShiftRight', false); - expect(kbd.onkeyevent).to.have.been.calledWith(0xffe1, 'ShiftLeft', false); - }); - - it('should fake a right Shift keyup', function () { - const kbd = new Keyboard(document); - kbd.onkeyevent = sinon.spy(); - - kbd._handleKeyDown(keyevent('keydown', {code: 'ShiftLeft', key: 'Shift', location: 1})); - expect(kbd.onkeyevent).to.have.been.calledOnce; - expect(kbd.onkeyevent).to.have.been.calledWith(0xffe1, 'ShiftLeft', true); - kbd.onkeyevent.resetHistory(); - - kbd._handleKeyDown(keyevent('keydown', {code: 'ShiftRight', key: 'Shift', location: 2})); - expect(kbd.onkeyevent).to.have.been.calledOnce; - expect(kbd.onkeyevent).to.have.been.calledWith(0xffe2, 'ShiftRight', true); - kbd.onkeyevent.resetHistory(); - - kbd._handleKeyUp(keyevent('keyup', {code: 'ShiftRight', key: 'Shift', location: 2})); - expect(kbd.onkeyevent).to.have.been.calledTwice; - expect(kbd.onkeyevent).to.have.been.calledWith(0xffe2, 'ShiftRight', false); - expect(kbd.onkeyevent).to.have.been.calledWith(0xffe1, 'ShiftLeft', false); - }); - }); -}); diff --git a/base/app/novnc/tests/test.localization.js b/base/app/novnc/tests/test.localization.js deleted file mode 100644 index 916ff84..0000000 --- a/base/app/novnc/tests/test.localization.js +++ /dev/null @@ -1,146 +0,0 @@ -const expect = chai.expect; -import _, { Localizer, l10n } from '../app/localization.js'; - -describe('Localization', function () { - "use strict"; - - let origNavigator; - let fetch; - - beforeEach(function () { - // window.navigator is a protected read-only property in many - // environments, so we need to redefine it whilst running these - // tests. - origNavigator = Object.getOwnPropertyDescriptor(window, "navigator"); - - Object.defineProperty(window, "navigator", {value: {}}); - window.navigator.languages = []; - - fetch = sinon.stub(window, "fetch"); - fetch.resolves(new Response("{}")); - }); - afterEach(function () { - fetch.restore(); - - Object.defineProperty(window, "navigator", origNavigator); - }); - - describe('Singleton', function () { - it('should export a singleton object', function () { - expect(l10n).to.be.instanceOf(Localizer); - }); - it('should export a singleton translation function', async function () { - // FIXME: Can we use some spy instead? - window.navigator.languages = ["de"]; - fetch.resolves(new Response(JSON.stringify({ "Foobar": "gazonk" }))); - await l10n.setup(["de"]); - expect(_("Foobar")).to.equal("gazonk"); - }); - }); - - describe('language selection', function () { - it('should use English by default', function () { - let lclz = new Localizer(); - expect(lclz.language).to.equal('en'); - }); - it('should use English if no user language matches', async function () { - window.navigator.languages = ["nl", "de"]; - let lclz = new Localizer(); - await lclz.setup(["es", "fr"]); - expect(lclz.language).to.equal('en'); - }); - it('should fall back to generic English for other English', async function () { - window.navigator.languages = ["en-AU", "de"]; - let lclz = new Localizer(); - await lclz.setup(["de", "fr", "en-GB"]); - expect(lclz.language).to.equal('en'); - }); - it('should prefer specific English over generic', async function () { - window.navigator.languages = ["en-GB", "de"]; - let lclz = new Localizer(); - await lclz.setup(["de", "en-AU", "en-GB"]); - expect(lclz.language).to.equal('en-GB'); - }); - it('should use the most preferred user language', async function () { - window.navigator.languages = ["nl", "de", "fr"]; - let lclz = new Localizer(); - await lclz.setup(["es", "fr", "de"]); - expect(lclz.language).to.equal('de'); - }); - it('should prefer sub-languages languages', async function () { - window.navigator.languages = ["pt-BR"]; - let lclz = new Localizer(); - await lclz.setup(["pt", "pt-BR"]); - expect(lclz.language).to.equal('pt-BR'); - }); - it('should fall back to language "parents"', async function () { - window.navigator.languages = ["pt-BR"]; - let lclz = new Localizer(); - await lclz.setup(["fr", "pt", "de"]); - expect(lclz.language).to.equal('pt'); - }); - it('should not use specific language when user asks for a generic language', async function () { - window.navigator.languages = ["pt", "de"]; - let lclz = new Localizer(); - await lclz.setup(["fr", "pt-BR", "de"]); - expect(lclz.language).to.equal('de'); - }); - it('should handle underscore as a separator', async function () { - window.navigator.languages = ["pt-BR"]; - let lclz = new Localizer(); - await lclz.setup(["pt_BR"]); - expect(lclz.language).to.equal('pt_BR'); - }); - it('should handle difference in case', async function () { - window.navigator.languages = ["pt-br"]; - let lclz = new Localizer(); - await lclz.setup(["pt-BR"]); - expect(lclz.language).to.equal('pt-BR'); - }); - }); - - describe('Translation loading', function () { - it('should not fetch a translation for English', async function () { - window.navigator.languages = []; - let lclz = new Localizer(); - await lclz.setup([]); - expect(fetch).to.not.have.been.called; - }); - it('should fetch dictionary relative base URL', async function () { - window.navigator.languages = ["de", "fr"]; - fetch.resolves(new Response('{ "Foobar": "gazonk" }')); - let lclz = new Localizer(); - await lclz.setup(["ru", "fr"], "/some/path/"); - expect(fetch).to.have.been.calledOnceWith("/some/path/fr.json"); - expect(lclz.get("Foobar")).to.equal("gazonk"); - }); - it('should handle base URL without trailing slash', async function () { - window.navigator.languages = ["de", "fr"]; - fetch.resolves(new Response('{ "Foobar": "gazonk" }')); - let lclz = new Localizer(); - await lclz.setup(["ru", "fr"], "/some/path"); - expect(fetch).to.have.been.calledOnceWith("/some/path/fr.json"); - expect(lclz.get("Foobar")).to.equal("gazonk"); - }); - it('should handle current base URL', async function () { - window.navigator.languages = ["de", "fr"]; - fetch.resolves(new Response('{ "Foobar": "gazonk" }')); - let lclz = new Localizer(); - await lclz.setup(["ru", "fr"]); - expect(fetch).to.have.been.calledOnceWith("fr.json"); - expect(lclz.get("Foobar")).to.equal("gazonk"); - }); - it('should fail if dictionary cannot be found', async function () { - window.navigator.languages = ["de", "fr"]; - fetch.resolves(new Response('{}', { status: 404 })); - let lclz = new Localizer(); - let ok = false; - try { - await lclz.setup(["ru", "fr"], "/some/path/"); - } catch (e) { - ok = true; - } - expect(ok).to.be.true; - }); - }); -}); diff --git a/base/app/novnc/tests/test.raw.js b/base/app/novnc/tests/test.raw.js deleted file mode 100644 index 4a634cc..0000000 --- a/base/app/novnc/tests/test.raw.js +++ /dev/null @@ -1,154 +0,0 @@ -const expect = chai.expect; - -import Websock from '../core/websock.js'; -import Display from '../core/display.js'; - -import RawDecoder from '../core/decoders/raw.js'; - -import FakeWebSocket from './fake.websocket.js'; - -function testDecodeRect(decoder, x, y, width, height, data, display, depth) { - let sock; - let done = false; - - sock = new Websock; - sock.open("ws://example.com"); - - sock.on('message', () => { - done = decoder.decodeRect(x, y, width, height, sock, display, depth); - }); - - // Empty messages are filtered at multiple layers, so we need to - // do a direct call - if (data.length === 0) { - done = decoder.decodeRect(x, y, width, height, sock, display, depth); - } else { - sock._websocket._receiveData(new Uint8Array(data)); - } - - display.flip(); - - return done; -} - -describe('Raw Decoder', function () { - let decoder; - let display; - - before(FakeWebSocket.replace); - after(FakeWebSocket.restore); - - beforeEach(function () { - decoder = new RawDecoder(); - display = new Display(document.createElement('canvas')); - display.resize(4, 4); - }); - - it('should handle the Raw encoding', function () { - let done; - - done = testDecodeRect(decoder, 0, 0, 2, 2, - [0xff, 0x00, 0x00, 0, - 0x00, 0xff, 0x00, 0, - 0x00, 0xff, 0x00, 0, - 0xff, 0x00, 0x00, 0], - display, 24); - expect(done).to.be.true; - done = testDecodeRect(decoder, 2, 0, 2, 2, - [0x00, 0x00, 0xff, 0, - 0x00, 0x00, 0xff, 0, - 0x00, 0x00, 0xff, 0, - 0x00, 0x00, 0xff, 0], - display, 24); - expect(done).to.be.true; - done = testDecodeRect(decoder, 0, 2, 4, 1, - [0xee, 0x00, 0xff, 0, - 0x00, 0xee, 0xff, 0, - 0xaa, 0xee, 0xff, 0, - 0xab, 0xee, 0xff, 0], - display, 24); - expect(done).to.be.true; - done = testDecodeRect(decoder, 0, 3, 4, 1, - [0xee, 0x00, 0xff, 0, - 0x00, 0xee, 0xff, 0, - 0xaa, 0xee, 0xff, 0, - 0xab, 0xee, 0xff, 0], - display, 24); - expect(done).to.be.true; - - let targetData = new Uint8Array([ - 0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0xff, 0x00, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255, - 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255 - ]); - - expect(display).to.have.displayed(targetData); - }); - - it('should handle the Raw encoding in low colour mode', function () { - let done; - - done = testDecodeRect(decoder, 0, 0, 2, 2, - [0x30, 0x30, 0x30, 0x30], - display, 8); - expect(done).to.be.true; - done = testDecodeRect(decoder, 2, 0, 2, 2, - [0x0c, 0x0c, 0x0c, 0x0c], - display, 8); - expect(done).to.be.true; - done = testDecodeRect(decoder, 0, 2, 4, 1, - [0x0c, 0x0c, 0x30, 0x30], - display, 8); - expect(done).to.be.true; - done = testDecodeRect(decoder, 0, 3, 4, 1, - [0x0c, 0x0c, 0x30, 0x30], - display, 8); - expect(done).to.be.true; - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 - ]); - - expect(display).to.have.displayed(targetData); - }); - - it('should handle empty rects', function () { - display.fillRect(0, 0, 4, 4, [ 0x00, 0x00, 0xff ]); - display.fillRect(2, 0, 2, 2, [ 0x00, 0xff, 0x00 ]); - display.fillRect(0, 2, 2, 2, [ 0x00, 0xff, 0x00 ]); - - let done = testDecodeRect(decoder, 1, 2, 0, 0, [], display, 24); - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 - ]); - - expect(done).to.be.true; - expect(display).to.have.displayed(targetData); - }); - - it('should handle empty rects in low colour mode', function () { - display.fillRect(0, 0, 4, 4, [ 0x00, 0x00, 0xff ]); - display.fillRect(2, 0, 2, 2, [ 0x00, 0xff, 0x00 ]); - display.fillRect(0, 2, 2, 2, [ 0x00, 0xff, 0x00 ]); - - let done = testDecodeRect(decoder, 1, 2, 0, 0, [], display, 8); - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 - ]); - - expect(done).to.be.true; - expect(display).to.have.displayed(targetData); - }); -}); diff --git a/base/app/novnc/tests/test.rfb.js b/base/app/novnc/tests/test.rfb.js deleted file mode 100644 index 62b80ca..0000000 --- a/base/app/novnc/tests/test.rfb.js +++ /dev/null @@ -1,5217 +0,0 @@ -const expect = chai.expect; - -import RFB from '../core/rfb.js'; -import Websock from '../core/websock.js'; -import ZStream from "../vendor/pako/lib/zlib/zstream.js"; -import { deflateInit, deflate, Z_DEFAULT_COMPRESSION } from "../vendor/pako/lib/zlib/deflate.js"; -import { encodings } from '../core/encodings.js'; -import { toUnsigned32bit } from '../core/util/int.js'; -import { encodeUTF8 } from '../core/util/strings.js'; -import KeyTable from '../core/input/keysym.js'; -import legacyCrypto from '../core/crypto/crypto.js'; - -import FakeWebSocket from './fake.websocket.js'; - -function push8(arr, num) { - "use strict"; - arr.push(num & 0xFF); -} - -function push16(arr, num) { - "use strict"; - arr.push((num >> 8) & 0xFF, - num & 0xFF); -} - -function push32(arr, num) { - "use strict"; - arr.push((num >> 24) & 0xFF, - (num >> 16) & 0xFF, - (num >> 8) & 0xFF, - num & 0xFF); -} - -function pushString(arr, string) { - let utf8 = unescape(encodeURIComponent(string)); - for (let i = 0; i < utf8.length; i++) { - arr.push(utf8.charCodeAt(i)); - } -} - -function deflateWithSize(data) { - // Adds the size of the string in front before deflating - - let unCompData = []; - unCompData.push((data.length >> 24) & 0xFF, - (data.length >> 16) & 0xFF, - (data.length >> 8) & 0xFF, - (data.length & 0xFF)); - - for (let i = 0; i < data.length; i++) { - unCompData.push(data.charCodeAt(i)); - } - - let strm = new ZStream(); - let chunkSize = 1024 * 10 * 10; - strm.output = new Uint8Array(chunkSize); - deflateInit(strm, Z_DEFAULT_COMPRESSION); - - /* eslint-disable camelcase */ - strm.input = unCompData; - strm.avail_in = strm.input.length; - strm.next_in = 0; - strm.next_out = 0; - strm.avail_out = chunkSize; - /* eslint-enable camelcase */ - - deflate(strm, 3); - - return new Uint8Array(strm.output.buffer, 0, strm.next_out); -} - -describe('Remote Frame Buffer Protocol Client', function () { - let clock; - let raf; - let fakeResizeObserver = null; - const realObserver = window.ResizeObserver; - - // Since we are using fake timers we don't actually want - // to wait for the browser to observe the size change, - // that's why we use a fake ResizeObserver - class FakeResizeObserver { - constructor(handler) { - this.fire = handler; - fakeResizeObserver = this; - } - disconnect() {} - observe(target, options) {} - unobserve(target) {} - } - - before(FakeWebSocket.replace); - after(FakeWebSocket.restore); - - before(function () { - this.clock = clock = sinon.useFakeTimers(Date.now()); - // sinon doesn't support this yet - raf = window.requestAnimationFrame; - window.requestAnimationFrame = setTimeout; - // We must do this in a 'before' since it needs to be set before - // the RFB constructor, which runs in beforeEach further down - window.ResizeObserver = FakeResizeObserver; - // Use a single set of buffers instead of reallocating to - // speed up tests - const sock = new Websock(); - const _sQ = new Uint8Array(sock._sQbufferSize); - const rQ = new Uint8Array(sock._rQbufferSize); - - Websock.prototype._oldAllocateBuffers = Websock.prototype._allocateBuffers; - Websock.prototype._allocateBuffers = function () { - this._sQ = _sQ; - this._rQ = rQ; - }; - - // Avoiding printing the entire Websock buffer on errors - Websock.prototype.inspect = function () { return "[object Websock]"; }; - }); - - after(function () { - Websock.prototype._allocateBuffers = Websock.prototype._oldAllocateBuffers; - delete Websock.prototype.inspect; - this.clock.restore(); - window.requestAnimationFrame = raf; - window.ResizeObserver = realObserver; - }); - - let container; - let rfbs; - - beforeEach(function () { - // Create a container element for all RFB objects to attach to - container = document.createElement('div'); - container.style.width = "100%"; - container.style.height = "100%"; - document.body.appendChild(container); - - // And track all created RFB objects - rfbs = []; - }); - afterEach(function () { - // Make sure every created RFB object is properly cleaned up - // or they might affect subsequent tests - rfbs.forEach(function (rfb) { - rfb.disconnect(); - expect(rfb._disconnect).to.have.been.called; - }); - rfbs = []; - - document.body.removeChild(container); - container = null; - }); - - function makeRFB(url, options) { - url = url || 'wss://host:8675'; - const rfb = new RFB(container, url, options); - clock.tick(); - rfb._sock._websocket._open(); - rfb._rfbConnectionState = 'connected'; - sinon.spy(rfb, "_disconnect"); - rfbs.push(rfb); - return rfb; - } - - describe('Connecting/Disconnecting', function () { - describe('#RFB (constructor)', function () { - let open, attach; - beforeEach(function () { - open = sinon.spy(Websock.prototype, 'open'); - attach = sinon.spy(Websock.prototype, 'attach'); - }); - afterEach(function () { - open.restore(); - attach.restore(); - }); - - it('should actually connect to the websocket', function () { - new RFB(document.createElement('div'), 'ws://HOST:8675/PATH'); - expect(open).to.have.been.calledOnceWithExactly('ws://HOST:8675/PATH', []); - }); - - it('should pass on connection problems', function () { - open.restore(); - open = sinon.stub(Websock.prototype, 'open'); - open.throws(new Error('Failure')); - expect(() => new RFB(document.createElement('div'), 'ws://HOST:8675/PATH')).to.throw('Failure'); - }); - - it('should handle WebSocket/RTCDataChannel objects', function () { - let sock = new FakeWebSocket('ws://HOST:8675/PATH', []); - new RFB(document.createElement('div'), sock); - expect(open).to.not.have.been.called; - expect(attach).to.have.been.calledOnceWithExactly(sock); - }); - - it('should handle already open WebSocket/RTCDataChannel objects', function () { - let sock = new FakeWebSocket('ws://HOST:8675/PATH', []); - sock._open(); - const client = new RFB(document.createElement('div'), sock); - let callback = sinon.spy(); - client.addEventListener('disconnect', callback); - expect(open).to.not.have.been.called; - expect(attach).to.have.been.calledOnceWithExactly(sock); - // Check if it is ready for some data - sock._receiveData(new Uint8Array(['R', 'F', 'B', '0', '0', '3', '0', '0', '8'])); - expect(callback).to.not.have.been.called; - }); - - it('should refuse closed WebSocket/RTCDataChannel objects', function () { - let sock = new FakeWebSocket('ws://HOST:8675/PATH', []); - sock.readyState = WebSocket.CLOSED; - expect(() => new RFB(document.createElement('div'), sock)).to.throw(); - }); - - it('should pass on attach problems', function () { - attach.restore(); - attach = sinon.stub(Websock.prototype, 'attach'); - attach.throws(new Error('Failure')); - let sock = new FakeWebSocket('ws://HOST:8675/PATH', []); - expect(() => new RFB(document.createElement('div'), sock)).to.throw('Failure'); - }); - }); - - describe('#disconnect', function () { - let client; - let close; - - beforeEach(function () { - client = makeRFB(); - close = sinon.stub(Websock.prototype, "close"); - }); - afterEach(function () { - close.restore(); - }); - - it('should start closing WebSocket', function () { - let callback = sinon.spy(); - client.addEventListener('disconnect', callback); - client.disconnect(); - expect(close).to.have.been.calledOnceWithExactly(); - expect(callback).to.not.have.been.called; - }); - - it('should send disconnect event', function () { - let callback = sinon.spy(); - client.addEventListener('disconnect', callback); - client.disconnect(); - close.thisValues[0]._eventHandlers.close(new CloseEvent("close", { 'code': 1000, 'reason': "", 'wasClean': true })); - expect(callback).to.have.been.calledOnce; - expect(callback.args[0][0].detail.clean).to.be.true; - }); - - it('should force disconnect if disconnecting takes too long', function () { - let callback = sinon.spy(); - client.addEventListener('disconnect', callback); - client.disconnect(); - this.clock.tick(3 * 1000); - expect(callback).to.have.been.calledOnce; - expect(callback.args[0][0].detail.clean).to.be.true; - }); - - it('should not fail if disconnect completes before timeout', function () { - let callback = sinon.spy(); - client.addEventListener('disconnect', callback); - client.disconnect(); - client._updateConnectionState('disconnecting'); - this.clock.tick(3 * 1000 / 2); - close.thisValues[0]._eventHandlers.close(new CloseEvent("close", { 'code': 1000, 'reason': "", 'wasClean': true })); - this.clock.tick(3 * 1000 / 2 + 1); - expect(callback).to.have.been.calledOnce; - expect(callback.args[0][0].detail.clean).to.be.true; - }); - - it('should unregister error event handler', function () { - sinon.spy(client._sock, 'off'); - client.disconnect(); - expect(client._sock.off).to.have.been.calledWith('error'); - }); - - it('should unregister message event handler', function () { - sinon.spy(client._sock, 'off'); - client.disconnect(); - expect(client._sock.off).to.have.been.calledWith('message'); - }); - - it('should unregister open event handler', function () { - sinon.spy(client._sock, 'off'); - client.disconnect(); - expect(client._sock.off).to.have.been.calledWith('open'); - }); - }); - }); - - describe('Public API Basic Behavior', function () { - let client; - beforeEach(function () { - client = makeRFB(); - }); - - describe('#sendCtrlAlDel', function () { - it('should sent ctrl[down]-alt[down]-del[down] then del[up]-alt[up]-ctrl[up]', function () { - let esock = new Websock(); - let ews = new FakeWebSocket(); - ews._open(); - esock.attach(ews); - RFB.messages.keyEvent(esock, 0xFFE3, 1); - RFB.messages.keyEvent(esock, 0xFFE9, 1); - RFB.messages.keyEvent(esock, 0xFFFF, 1); - RFB.messages.keyEvent(esock, 0xFFFF, 0); - RFB.messages.keyEvent(esock, 0xFFE9, 0); - RFB.messages.keyEvent(esock, 0xFFE3, 0); - let expected = ews._getSentData(); - - client.sendCtrlAltDel(); - - expect(client._sock).to.have.sent(expected); - }); - - it('should not send the keys if we are not in a normal state', function () { - client._rfbConnectionState = "connecting"; - client.sendCtrlAltDel(); - expect(client._sock).to.have.sent(new Uint8Array([])); - }); - - it('should not send the keys if we are set as view_only', function () { - client._viewOnly = true; - client.sendCtrlAltDel(); - expect(client._sock).to.have.sent(new Uint8Array([])); - }); - }); - - describe('#sendKey', function () { - it('should send a single key with the given code and state (down = true)', function () { - let esock = new Websock(); - let ews = new FakeWebSocket(); - ews._open(); - esock.attach(ews); - RFB.messages.keyEvent(esock, 123, 1); - let expected = ews._getSentData(); - - client.sendKey(123, 'Key123', true); - - expect(client._sock).to.have.sent(expected); - }); - - it('should send both a down and up event if the state is not specified', function () { - let esock = new Websock(); - let ews = new FakeWebSocket(); - ews._open(); - esock.attach(ews); - RFB.messages.keyEvent(esock, 123, 1); - RFB.messages.keyEvent(esock, 123, 0); - let expected = ews._getSentData(); - - client.sendKey(123, 'Key123'); - - expect(client._sock).to.have.sent(expected); - }); - - it('should not send the key if we are not in a normal state', function () { - client._rfbConnectionState = "connecting"; - client.sendKey(123, 'Key123'); - expect(client._sock).to.have.sent(new Uint8Array([])); - }); - - it('should not send the key if we are set as view_only', function () { - client._viewOnly = true; - client.sendKey(123, 'Key123'); - expect(client._sock).to.have.sent(new Uint8Array([])); - }); - - it('should send QEMU extended events if supported', function () { - client._qemuExtKeyEventSupported = true; - let esock = new Websock(); - let ews = new FakeWebSocket(); - ews._open(); - esock.attach(ews); - RFB.messages.QEMUExtendedKeyEvent(esock, 0x20, true, 0x0039); - let expected = ews._getSentData(); - - client.sendKey(0x20, 'Space', true); - - expect(client._sock).to.have.sent(expected); - }); - - it('should not send QEMU extended events if unknown key code', function () { - client._qemuExtKeyEventSupported = true; - let esock = new Websock(); - let ews = new FakeWebSocket(); - ews._open(); - esock.attach(ews); - RFB.messages.keyEvent(esock, 123, 1); - let expected = ews._getSentData(); - - client.sendKey(123, 'FooBar', true); - - expect(client._sock).to.have.sent(expected); - }); - }); - - describe('#focus', function () { - it('should move focus to canvas object', function () { - sinon.spy(client._canvas, "focus"); - client.focus(); - expect(client._canvas.focus).to.have.been.calledOnce; - }); - - it('should include focus options', function () { - sinon.spy(client._canvas, "focus"); - client.focus({ foobar: 12, gazonk: true }); - expect(client._canvas.focus).to.have.been.calledOnce; - expect(client._canvas.focus).to.have.been.calledWith({ foobar: 12, gazonk: true}); - }); - }); - - describe('#blur', function () { - it('should remove focus from canvas object', function () { - sinon.spy(client._canvas, "blur"); - client.blur(); - expect(client._canvas.blur).to.have.been.calledOnce; - }); - }); - - describe('#clipboardPasteFrom', function () { - describe('Clipboard update handling', function () { - beforeEach(function () { - sinon.spy(RFB.messages, 'clientCutText'); - sinon.spy(RFB.messages, 'extendedClipboardNotify'); - }); - - afterEach(function () { - RFB.messages.clientCutText.restore(); - RFB.messages.extendedClipboardNotify.restore(); - }); - - it('should send the given text in an clipboard update', function () { - client.clipboardPasteFrom('abc'); - - expect(RFB.messages.clientCutText).to.have.been.calledOnce; - expect(RFB.messages.clientCutText).to.have.been.calledWith(client._sock, - new Uint8Array([97, 98, 99])); - }); - - it('should mask unsupported characters', function () { - client.clipboardPasteFrom('abc€'); - - expect(RFB.messages.clientCutText).to.have.been.calledOnce; - expect(RFB.messages.clientCutText).to.have.been.calledWith(client._sock, - new Uint8Array([97, 98, 99, 63])); - }); - - it('should mask characters, not UTF-16 code points', function () { - client.clipboardPasteFrom('😂'); - - expect(RFB.messages.clientCutText).to.have.been.calledOnce; - expect(RFB.messages.clientCutText).to.have.been.calledWith(client._sock, - new Uint8Array([63])); - }); - - it('should send an notify if extended clipboard is supported by server', function () { - // Send our capabilities - let data = [3, 0, 0, 0]; - const flags = [0x1F, 0x00, 0x00, 0x01]; - let fileSizes = [0x00, 0x00, 0x00, 0x1E]; - - push32(data, toUnsigned32bit(-8)); - data = data.concat(flags); - data = data.concat(fileSizes); - client._sock._websocket._receiveData(new Uint8Array(data)); - - client.clipboardPasteFrom('extended test'); - expect(RFB.messages.extendedClipboardNotify).to.have.been.calledOnce; - }); - }); - - it('should flush multiple times for large clipboards', function () { - sinon.spy(client._sock, 'flush'); - let longText = ""; - for (let i = 0; i < client._sock._sQbufferSize + 100; i++) { - longText += 'a'; - } - client.clipboardPasteFrom(longText); - expect(client._sock.flush).to.have.been.calledTwice; - }); - - it('should not send the text if we are not in a normal state', function () { - sinon.spy(client._sock, 'flush'); - client._rfbConnectionState = "connecting"; - client.clipboardPasteFrom('abc'); - expect(client._sock.flush).to.not.have.been.called; - }); - }); - - describe("XVP operations", function () { - beforeEach(function () { - client._rfbXvpVer = 1; - }); - - it('should send the shutdown signal on #machineShutdown', function () { - client.machineShutdown(); - expect(client._sock).to.have.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x02])); - }); - - it('should send the reboot signal on #machineReboot', function () { - client.machineReboot(); - expect(client._sock).to.have.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x03])); - }); - - it('should send the reset signal on #machineReset', function () { - client.machineReset(); - expect(client._sock).to.have.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x04])); - }); - - it('should not send XVP operations with higher versions than we support', function () { - sinon.spy(client._sock, 'flush'); - client._xvpOp(2, 7); - expect(client._sock.flush).to.not.have.been.called; - }); - }); - }); - - describe('Clipping', function () { - let client; - - beforeEach(function () { - client = makeRFB(); - container.style.width = '70px'; - container.style.height = '80px'; - client.clipViewport = true; - }); - - it('should update display clip state when changing the property', function () { - const spy = sinon.spy(client._display, "clipViewport", ["set"]); - - client.clipViewport = false; - expect(spy.set).to.have.been.calledOnce; - expect(spy.set).to.have.been.calledWith(false); - spy.set.resetHistory(); - - client.clipViewport = true; - expect(spy.set).to.have.been.calledOnce; - expect(spy.set).to.have.been.calledWith(true); - }); - - it('should update the viewport when the container size changes', function () { - sinon.spy(client._display, "viewportChangeSize"); - - container.style.width = '40px'; - container.style.height = '50px'; - fakeResizeObserver.fire(); - clock.tick(1000); - - expect(client._display.viewportChangeSize).to.have.been.calledOnce; - expect(client._display.viewportChangeSize).to.have.been.calledWith(40, 50); - }); - - it('should update the viewport when the remote session resizes', function () { - // Simple ExtendedDesktopSize FBU message - const incoming = [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xcc, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, - 0x00, 0x00, 0x00, 0x00 ]; - - sinon.spy(client._display, "viewportChangeSize"); - - client._sock._websocket._receiveData(new Uint8Array(incoming)); - // The resize will cause scrollbars on the container, this causes a - // resize observation in the browsers - fakeResizeObserver.fire(); - clock.tick(1000); - - // FIXME: Display implicitly calls viewportChangeSize() when - // resizing the framebuffer, hence calledTwice. - expect(client._display.viewportChangeSize).to.have.been.calledTwice; - expect(client._display.viewportChangeSize).to.have.been.calledWith(70, 80); - }); - - it('should not update the viewport if not clipping', function () { - client.clipViewport = false; - sinon.spy(client._display, "viewportChangeSize"); - - container.style.width = '40px'; - container.style.height = '50px'; - fakeResizeObserver.fire(); - clock.tick(1000); - - expect(client._display.viewportChangeSize).to.not.have.been.called; - }); - - it('should not update the viewport if scaling', function () { - client.scaleViewport = true; - sinon.spy(client._display, "viewportChangeSize"); - - container.style.width = '40px'; - container.style.height = '50px'; - fakeResizeObserver.fire(); - clock.tick(1000); - - expect(client._display.viewportChangeSize).to.not.have.been.called; - }); - - describe('Clipping and remote resize', function () { - beforeEach(function () { - // Given a remote (100, 100) larger than the container (70x80), - client._resize(100, 100); - client._supportsSetDesktopSize = true; - client.resizeSession = true; - sinon.spy(RFB.messages, "setDesktopSize"); - }); - afterEach(function () { - RFB.messages.setDesktopSize.restore(); - }); - it('should not change remote size when changing clipping', function () { - // When changing clipping the scrollbars of the container - // will appear and disappear and thus trigger resize observations - client.clipViewport = false; - fakeResizeObserver.fire(); - clock.tick(1000); - client.clipViewport = true; - fakeResizeObserver.fire(); - clock.tick(1000); - - // Then no resize requests should be sent - expect(RFB.messages.setDesktopSize).to.not.have.been.called; - }); - }); - - describe('Dragging', function () { - beforeEach(function () { - client.dragViewport = true; - sinon.spy(RFB.messages, "pointerEvent"); - }); - - afterEach(function () { - RFB.messages.pointerEvent.restore(); - }); - - it('should not send button messages when initiating viewport dragging', function () { - client._handleMouseButton(13, 9, 0x001); - expect(RFB.messages.pointerEvent).to.not.have.been.called; - }); - - it('should send button messages when release without movement', function () { - // Just up and down - client._handleMouseButton(13, 9, 0x001); - client._handleMouseButton(13, 9, 0x000); - expect(RFB.messages.pointerEvent).to.have.been.calledTwice; - - RFB.messages.pointerEvent.resetHistory(); - - // Small movement - client._handleMouseButton(13, 9, 0x001); - client._handleMouseMove(15, 14); - client._handleMouseButton(15, 14, 0x000); - expect(RFB.messages.pointerEvent).to.have.been.calledTwice; - }); - - it('should not send button messages when in view only', function () { - client._viewOnly = true; - client._handleMouseButton(13, 9, 0x001); - client._handleMouseButton(13, 9, 0x000); - expect(RFB.messages.pointerEvent).to.not.have.been.called; - }); - - it('should send button message directly when drag is disabled', function () { - client.dragViewport = false; - client._handleMouseButton(13, 9, 0x001); - expect(RFB.messages.pointerEvent).to.have.been.calledOnce; - }); - - it('should be initiate viewport dragging on sufficient movement', function () { - sinon.spy(client._display, "viewportChangePos"); - - // Too small movement - - client._handleMouseButton(13, 9, 0x001); - client._handleMouseMove(18, 9); - - expect(RFB.messages.pointerEvent).to.not.have.been.called; - expect(client._display.viewportChangePos).to.not.have.been.called; - - // Sufficient movement - - client._handleMouseMove(43, 9); - - expect(RFB.messages.pointerEvent).to.not.have.been.called; - expect(client._display.viewportChangePos).to.have.been.calledOnce; - expect(client._display.viewportChangePos).to.have.been.calledWith(-30, 0); - - client._display.viewportChangePos.resetHistory(); - - // Now a small movement should move right away - - client._handleMouseMove(43, 14); - - expect(RFB.messages.pointerEvent).to.not.have.been.called; - expect(client._display.viewportChangePos).to.have.been.calledOnce; - expect(client._display.viewportChangePos).to.have.been.calledWith(0, -5); - }); - - it('should not send button messages when dragging ends', function () { - // First the movement - - client._handleMouseButton(13, 9, 0x001); - client._handleMouseMove(43, 9); - client._handleMouseButton(43, 9, 0x000); - - expect(RFB.messages.pointerEvent).to.not.have.been.called; - }); - - it('should terminate viewport dragging on a button up event', function () { - // First the dragging movement - - client._handleMouseButton(13, 9, 0x001); - client._handleMouseMove(43, 9); - client._handleMouseButton(43, 9, 0x000); - - // Another movement now should not move the viewport - - sinon.spy(client._display, "viewportChangePos"); - - client._handleMouseMove(43, 59); - - expect(client._display.viewportChangePos).to.not.have.been.called; - }); - }); - }); - - describe('Scaling', function () { - let client; - beforeEach(function () { - client = makeRFB(); - container.style.width = '70px'; - container.style.height = '80px'; - client.scaleViewport = true; - }); - - it('should update display scale factor when changing the property', function () { - const spy = sinon.spy(client._display, "scale", ["set"]); - sinon.spy(client._display, "autoscale"); - - client.scaleViewport = false; - expect(spy.set).to.have.been.calledOnce; - expect(spy.set).to.have.been.calledWith(1.0); - expect(client._display.autoscale).to.not.have.been.called; - - client.scaleViewport = true; - expect(client._display.autoscale).to.have.been.calledOnce; - expect(client._display.autoscale).to.have.been.calledWith(70, 80); - }); - - it('should update the clipping setting when changing the property', function () { - client.clipViewport = true; - - const spy = sinon.spy(client._display, "clipViewport", ["set"]); - - client.scaleViewport = false; - expect(spy.set).to.have.been.calledOnce; - expect(spy.set).to.have.been.calledWith(true); - - spy.set.resetHistory(); - - client.scaleViewport = true; - expect(spy.set).to.have.been.calledOnce; - expect(spy.set).to.have.been.calledWith(false); - }); - - it('should update the scaling when the container size changes', function () { - sinon.spy(client._display, "autoscale"); - - container.style.width = '40px'; - container.style.height = '50px'; - fakeResizeObserver.fire(); - clock.tick(1000); - - expect(client._display.autoscale).to.have.been.calledOnce; - expect(client._display.autoscale).to.have.been.calledWith(40, 50); - }); - - it('should update the scaling when the remote session resizes', function () { - // Simple ExtendedDesktopSize FBU message - const incoming = [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xcc, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, - 0x00, 0x00, 0x00, 0x00 ]; - - sinon.spy(client._display, "autoscale"); - - client._sock._websocket._receiveData(new Uint8Array(incoming)); - // The resize will cause scrollbars on the container, this causes a - // resize observation in the browsers - fakeResizeObserver.fire(); - clock.tick(1000); - - expect(client._display.autoscale).to.have.been.calledOnce; - expect(client._display.autoscale).to.have.been.calledWith(70, 80); - }); - - it('should not update the display scale factor if not scaling', function () { - client.scaleViewport = false; - - sinon.spy(client._display, "autoscale"); - - container.style.width = '40px'; - container.style.height = '50px'; - fakeResizeObserver.fire(); - clock.tick(1000); - - expect(client._display.autoscale).to.not.have.been.called; - }); - }); - - describe('Remote resize', function () { - let client; - beforeEach(function () { - client = makeRFB(); - client.resizeSession = true; - container.style.width = '70px'; - container.style.height = '80px'; - - const incoming = [ 0x00, // msg-type=FBU - 0x00, // padding - 0x00, 0x01, // number of rects = 1 - 0x00, 0x00, // reason = server initialized - 0x00, 0x00, // status = no error - 0x00, 0x04, // new width = 4 - 0x00, 0x04, // new height = 4 - 0xff, 0xff, - 0xfe, 0xcc, // enc = (-308) ExtendedDesktopSize - 0x01, // number of screens = 1 - 0x00, 0x00, - 0x00, // padding - 0x78, 0x90, - 0xab, 0xcd, // screen id = 0 - 0x00, 0x00, // screen x = 0 - 0x00, 0x00, // screen y = 0 - 0x00, 0x04, // screen width = 4 - 0x00, 0x04, // screen height = 4 - 0x12, 0x34, - 0x56, 0x78]; // screen flags - client._sock._websocket._receiveData(new Uint8Array(incoming)); - - sinon.spy(RFB.messages, "setDesktopSize"); - }); - - afterEach(function () { - RFB.messages.setDesktopSize.restore(); - }); - - it('should only request a resize when turned on', function () { - client.resizeSession = false; - expect(RFB.messages.setDesktopSize).to.not.have.been.called; - client.resizeSession = true; - expect(RFB.messages.setDesktopSize).to.have.been.calledOnce; - }); - - it('should request a resize when initially connecting', function () { - // Create a new object that hasn't yet seen a - // ExtendedDesktopSize rect - client = makeRFB(); - client.resizeSession = true; - container.style.width = '70px'; - container.style.height = '80px'; - - // Simple ExtendedDesktopSize FBU message - const incoming = [ 0x00, // msg-type=FBU - 0x00, // padding - 0x00, 0x01, // number of rects = 1 - 0x00, 0x00, // reason = server initialized - 0x00, 0x00, // status = no error - 0x00, 0x04, // new width = 4 - 0x00, 0x04, // new height = 4 - 0xff, 0xff, - 0xfe, 0xcc, // enc = (-308) ExtendedDesktopSize - 0x01, // number of screens = 1 - 0x00, 0x00, - 0x00, // padding - 0x78, 0x90, - 0xab, 0xcd, // screen id = 0 - 0x00, 0x00, // screen x = 0 - 0x00, 0x00, // screen y = 0 - 0x00, 0x04, // screen width = 4 - 0x00, 0x04, // screen height = 4 - 0x12, 0x34, - 0x56, 0x78]; // screen flags - - // First message should trigger a resize - - client._sock._websocket._receiveData(new Uint8Array(incoming)); - - // It should match the current size of the container, - // not the reported size from the server - expect(RFB.messages.setDesktopSize).to.have.been.calledOnce; - expect(RFB.messages.setDesktopSize).to.have.been.calledWith( - sinon.match.object, 70, 80, 0x7890abcd, 0x12345678); - - RFB.messages.setDesktopSize.resetHistory(); - - // Second message should not trigger a resize - - client._sock._websocket._receiveData(new Uint8Array(incoming)); - - expect(RFB.messages.setDesktopSize).to.not.have.been.called; - }); - - it('should request a resize when the container resizes', function () { - container.style.width = '40px'; - container.style.height = '50px'; - fakeResizeObserver.fire(); - clock.tick(1000); - - expect(RFB.messages.setDesktopSize).to.have.been.calledOnce; - expect(RFB.messages.setDesktopSize).to.have.been.calledWith( - sinon.match.object, 40, 50, 0x7890abcd, 0x12345678); - }); - - it('should not request the same size twice', function () { - container.style.width = '40px'; - container.style.height = '50px'; - fakeResizeObserver.fire(); - clock.tick(1000); - - expect(RFB.messages.setDesktopSize).to.have.been.calledOnce; - expect(RFB.messages.setDesktopSize).to.have.been.calledWith( - sinon.match.object, 40, 50, 0x7890abcd, 0x12345678); - - // Server responds with the requested size 40x50 - const incoming = [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x28, 0x00, 0x32, 0xff, 0xff, 0xfe, 0xcc, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x32, - 0x00, 0x00, 0x00, 0x00]; - - client._sock._websocket._receiveData(new Uint8Array(incoming)); - clock.tick(1000); - - RFB.messages.setDesktopSize.resetHistory(); - - // size is still 40x50 - fakeResizeObserver.fire(); - clock.tick(1000); - - expect(RFB.messages.setDesktopSize).to.not.have.been.called; - }); - - it('should not resize until the container size is stable', function () { - container.style.width = '20px'; - container.style.height = '30px'; - fakeResizeObserver.fire(); - clock.tick(400); - - expect(RFB.messages.setDesktopSize).to.not.have.been.called; - - container.style.width = '40px'; - container.style.height = '50px'; - fakeResizeObserver.fire(); - clock.tick(400); - - expect(RFB.messages.setDesktopSize).to.not.have.been.called; - - clock.tick(200); - - expect(RFB.messages.setDesktopSize).to.have.been.calledOnce; - expect(RFB.messages.setDesktopSize).to.have.been.calledWith( - sinon.match.object, 40, 50, 0x7890abcd, 0x12345678); - }); - - it('should not resize when resize is disabled', function () { - client._resizeSession = false; - - container.style.width = '40px'; - container.style.height = '50px'; - fakeResizeObserver.fire(); - clock.tick(1000); - - expect(RFB.messages.setDesktopSize).to.not.have.been.called; - }); - - it('should not resize when resize is not supported', function () { - client._supportsSetDesktopSize = false; - - container.style.width = '40px'; - container.style.height = '50px'; - fakeResizeObserver.fire(); - clock.tick(1000); - - expect(RFB.messages.setDesktopSize).to.not.have.been.called; - }); - - it('should not resize when in view only mode', function () { - client._viewOnly = true; - - container.style.width = '40px'; - container.style.height = '50px'; - fakeResizeObserver.fire(); - clock.tick(1000); - - expect(RFB.messages.setDesktopSize).to.not.have.been.called; - }); - - it('should not try to override a server resize', function () { - // Simple ExtendedDesktopSize FBU message, new size: 100x100 - const incoming = [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x64, 0x00, 0x64, 0xff, 0xff, 0xfe, 0xcc, - 0x01, 0x00, 0x00, 0x00, 0xab, 0xab, 0xab, 0xab, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, - 0x11, 0x22, 0x33, 0x44 ]; - - // Note that this will cause the browser to display scrollbars - // since the framebuffer is 100x100 and the container is 70x80. - // The usable space (clientWidth/clientHeight) will be even smaller - // due to the scrollbars taking up space. - client._sock._websocket._receiveData(new Uint8Array(incoming)); - // The scrollbars cause the ResizeObserver to fire - fakeResizeObserver.fire(); - clock.tick(1000); - - expect(RFB.messages.setDesktopSize).to.not.have.been.called; - - // An actual size change must not be ignored afterwards - container.style.width = '120px'; - container.style.height = '130px'; - fakeResizeObserver.fire(); - clock.tick(1000); - - expect(RFB.messages.setDesktopSize).to.have.been.calledOnce; - expect(RFB.messages.setDesktopSize).to.have.been.calledWith( - sinon.match.object, 120, 130, 0xabababab, 0x11223344); - }); - }); - - describe('Misc Internals', function () { - describe('#_fail', function () { - let client; - beforeEach(function () { - client = makeRFB(); - }); - - it('should close the WebSocket connection', function () { - sinon.spy(client._sock, 'close'); - client._fail(); - expect(client._sock.close).to.have.been.calledOnce; - }); - - it('should transition to disconnected', function () { - sinon.spy(client, '_updateConnectionState'); - client._fail(); - this.clock.tick(2000); - expect(client._updateConnectionState).to.have.been.called; - expect(client._rfbConnectionState).to.equal('disconnected'); - }); - - it('should set clean_disconnect variable', function () { - client._rfbCleanDisconnect = true; - client._rfbConnectionState = 'connected'; - client._fail(); - expect(client._rfbCleanDisconnect).to.be.false; - }); - - it('should result in disconnect event with clean set to false', function () { - client._rfbConnectionState = 'connected'; - const spy = sinon.spy(); - client.addEventListener("disconnect", spy); - client._fail(); - this.clock.tick(2000); - expect(spy).to.have.been.calledOnce; - expect(spy.args[0][0].detail.clean).to.be.false; - }); - - }); - }); - - describe('Protocol Initialization States', function () { - let client; - beforeEach(function () { - client = makeRFB(); - client._rfbConnectionState = 'connecting'; - }); - - function sendVer(ver, client) { - const arr = new Uint8Array(12); - for (let i = 0; i < ver.length; i++) { - arr[i+4] = ver.charCodeAt(i); - } - arr[0] = 'R'; arr[1] = 'F'; arr[2] = 'B'; arr[3] = ' '; - arr[11] = '\n'; - client._sock._websocket._receiveData(arr); - } - - function sendSecurity(type, cl) { - cl._sock._websocket._receiveData(new Uint8Array([1, type])); - } - - describe('ProtocolVersion', function () { - describe('version parsing', function () { - it('should interpret version 003.003 as version 3.3', function () { - sendVer('003.003', client); - expect(client._rfbVersion).to.equal(3.3); - }); - - it('should interpret version 003.006 as version 3.3', function () { - sendVer('003.006', client); - expect(client._rfbVersion).to.equal(3.3); - }); - - it('should interpret version 003.889 as version 3.8', function () { - sendVer('003.889', client); - expect(client._rfbVersion).to.equal(3.8); - }); - - it('should interpret version 003.007 as version 3.7', function () { - sendVer('003.007', client); - expect(client._rfbVersion).to.equal(3.7); - }); - - it('should interpret version 003.008 as version 3.8', function () { - sendVer('003.008', client); - expect(client._rfbVersion).to.equal(3.8); - }); - - it('should interpret version 004.000 as version 3.8', function () { - sendVer('004.000', client); - expect(client._rfbVersion).to.equal(3.8); - }); - - it('should interpret version 004.001 as version 3.8', function () { - sendVer('004.001', client); - expect(client._rfbVersion).to.equal(3.8); - }); - - it('should interpret version 005.000 as version 3.8', function () { - sendVer('005.000', client); - expect(client._rfbVersion).to.equal(3.8); - }); - - it('should fail on an invalid version', function () { - let callback = sinon.spy(); - client.addEventListener("disconnect", callback); - - sendVer('002.000', client); - - expect(callback).to.have.been.calledOnce; - expect(callback.args[0][0].detail.clean).to.be.false; - }); - }); - - it('should send back the interpreted version', function () { - sendVer('004.000', client); - - const expectedStr = 'RFB 003.008\n'; - const expected = []; - for (let i = 0; i < expectedStr.length; i++) { - expected[i] = expectedStr.charCodeAt(i); - } - - expect(client._sock).to.have.sent(new Uint8Array(expected)); - }); - - it('should transition to the Security state on successful negotiation', function () { - sendVer('003.008', client); - expect(client._rfbInitState).to.equal('Security'); - }); - - describe('Repeater', function () { - beforeEach(function () { - client = makeRFB('wss://host:8675', { repeaterID: "12345" }); - client._rfbConnectionState = 'connecting'; - }); - - it('should interpret version 000.000 as a repeater', function () { - sendVer('000.000', client); - expect(client._rfbVersion).to.equal(0); - - const sentData = client._sock._websocket._getSentData(); - expect(new Uint8Array(sentData.buffer, 0, 9)).to.array.equal(new Uint8Array([73, 68, 58, 49, 50, 51, 52, 53, 0])); - expect(sentData).to.have.length(250); - }); - - it('should handle two step repeater negotiation', function () { - sendVer('000.000', client); - sendVer('003.008', client); - expect(client._rfbVersion).to.equal(3.8); - }); - }); - }); - - describe('Security', function () { - beforeEach(function () { - sendVer('003.008\n', client); - client._sock._websocket._getSentData(); - }); - - it('should respect server preference order', function () { - const authSchemes = [ 6, 79, 30, 188, 16, 6, 1 ]; - client._sock._websocket._receiveData(new Uint8Array(authSchemes)); - expect(client._sock).to.have.sent(new Uint8Array([30])); - }); - - it('should fail if there are no supported schemes', function () { - let callback = sinon.spy(); - client.addEventListener("disconnect", callback); - - const authSchemes = [1, 32]; - client._sock._websocket._receiveData(new Uint8Array(authSchemes)); - - expect(callback).to.have.been.calledOnce; - expect(callback.args[0][0].detail.clean).to.be.false; - }); - - it('should fail with the appropriate message if no types are sent', function () { - const failureData = [0, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115]; - let callback = sinon.spy(); - client.addEventListener("securityfailure", callback); - - client._sock._websocket._receiveData(new Uint8Array(failureData)); - - expect(callback).to.have.been.calledOnce; - expect(callback.args[0][0].detail.status).to.equal(1); - expect(callback.args[0][0].detail.reason).to.equal("whoops"); - }); - - it('should transition to the Authentication state and continue on successful negotiation', function () { - const authSchemes = [1, 2]; - sinon.spy(client, "_negotiateAuthentication"); - client._sock._websocket._receiveData(new Uint8Array(authSchemes)); - expect(client._rfbInitState).to.equal('Authentication'); - expect(client._negotiateAuthentication).to.have.been.calledOnce; - }); - }); - - describe('Legacy Authentication', function () { - it('should fail on auth scheme 0 (pre 3.7) with the given message', function () { - const errMsg = "Whoopsies"; - const data = [0, 0, 0, 0]; - const errLen = errMsg.length; - push32(data, errLen); - for (let i = 0; i < errLen; i++) { - data.push(errMsg.charCodeAt(i)); - } - - sendVer('003.006\n', client); - client._sock._websocket._getSentData(); - let callback = sinon.spy(); - client.addEventListener("securityfailure", callback); - - client._sock._websocket._receiveData(new Uint8Array(data)); - - expect(callback).to.have.been.calledOnce; - expect(callback.args[0][0].detail.status).to.equal(1); - expect(callback.args[0][0].detail.reason).to.equal("Whoopsies"); - }); - - it('should transition straight to ServerInitialisation on "no auth" for versions < 3.7', function () { - sendVer('003.006\n', client); - client._sock._websocket._getSentData(); - - client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 1])); - expect(client._rfbInitState).to.equal('ServerInitialisation'); - }); - - it('should transition straight to ServerInitialisation on "no auth" for versions < 3.8', function () { - sendVer('003.007\n', client); - client._sock._websocket._getSentData(); - - sendSecurity(1, client); - expect(client._rfbInitState).to.equal('ServerInitialisation'); - }); - }); - - describe('Authentication', function () { - beforeEach(function () { - sendVer('003.008\n', client); - client._sock._websocket._getSentData(); - }); - - it('should transition straight to SecurityResult on "no auth" (1)', function () { - sendSecurity(1, client); - expect(client._rfbInitState).to.equal('SecurityResult'); - }); - - it('should fail on an unknown auth scheme', function () { - let callback = sinon.spy(); - client.addEventListener("disconnect", callback); - - sendSecurity(57, client); - - expect(callback).to.have.been.calledOnce; - expect(callback.args[0][0].detail.clean).to.be.false; - }); - - describe('VNC Authentication (type 2) Handler', function () { - it('should fire the credentialsrequired event if missing a password', function () { - const spy = sinon.spy(); - client.addEventListener("credentialsrequired", spy); - sendSecurity(2, client); - - const challenge = []; - for (let i = 0; i < 16; i++) { challenge[i] = i; } - client._sock._websocket._receiveData(new Uint8Array(challenge)); - - expect(spy).to.have.been.calledOnce; - expect(spy.args[0][0].detail.types).to.have.members(["password"]); - }); - - it('should encrypt the password with DES and then send it back', function () { - client.addEventListener("credentialsrequired", () => { - client.sendCredentials({ password: 'passwd' }); - }); - sendSecurity(2, client); - client._sock._websocket._getSentData(); // skip the choice of auth reply - - const challenge = []; - for (let i = 0; i < 16; i++) { challenge[i] = i; } - client._sock._websocket._receiveData(new Uint8Array(challenge)); - clock.tick(); - - const desPass = RFB.genDES('passwd', challenge); - expect(client._sock).to.have.sent(new Uint8Array(desPass)); - }); - - it('should transition to SecurityResult immediately after sending the password', function () { - client.addEventListener("credentialsrequired", () => { - client.sendCredentials({ password: 'passwd' }); - }); - sendSecurity(2, client); - - const challenge = []; - for (let i = 0; i < 16; i++) { challenge[i] = i; } - client._sock._websocket._receiveData(new Uint8Array(challenge)); - clock.tick(); - - expect(client._rfbInitState).to.equal('SecurityResult'); - }); - }); - - describe('RSA-AES Authentication (type 6) Handler', function () { - function fakeGetRandomValues(arr) { - if (arr.length === 16) { - arr.set(new Uint8Array([ - 0x1c, 0x08, 0xfe, 0x21, 0x78, 0xef, 0x4e, 0xf9, - 0x3f, 0x05, 0xec, 0xea, 0xd4, 0x6b, 0xa5, 0xd5, - ])); - } else { - arr.set(new Uint8Array([ - 0xee, 0xe2, 0xf1, 0x5a, 0x3c, 0xa7, 0xbe, 0x95, - 0x6f, 0x2a, 0x75, 0xfd, 0x62, 0x01, 0xcb, 0xbf, - 0x43, 0x74, 0xca, 0x47, 0x4d, 0xfb, 0x0f, 0xcf, - 0x3a, 0x6d, 0x55, 0x6b, 0x59, 0x3a, 0xf6, 0x87, - 0xcb, 0x03, 0xb7, 0x28, 0x35, 0x7b, 0x15, 0x8e, - 0xb6, 0xc8, 0x8f, 0x2d, 0x5e, 0x7b, 0x1c, 0x9a, - 0x32, 0x55, 0xe7, 0x64, 0x36, 0x25, 0x7b, 0xa3, - 0xe9, 0x4f, 0x6f, 0x97, 0xdc, 0xa4, 0xd4, 0x62, - 0x6d, 0x7f, 0xab, 0x02, 0x6b, 0x13, 0x56, 0x69, - 0xfb, 0xd0, 0xd4, 0x13, 0x76, 0xcd, 0x0d, 0xd0, - 0x1f, 0xd1, 0x0c, 0x63, 0x3a, 0x34, 0x20, 0x6c, - 0xbb, 0x60, 0x45, 0x82, 0x23, 0xfd, 0x7c, 0x77, - 0x6d, 0xcc, 0x5e, 0xaa, 0xc3, 0x0c, 0x43, 0xb7, - 0x8d, 0xc0, 0x27, 0x6e, 0xeb, 0x1d, 0x6c, 0x5f, - 0xd8, 0x1c, 0x3c, 0x1c, 0x60, 0x2e, 0x82, 0x15, - 0xfd, 0x2e, 0x5f, 0x3a, 0x15, 0x53, 0x14, 0x70, - 0x4f, 0xe1, 0x65, 0x68, 0x35, 0x6d, 0xc7, 0x64, - 0xdb, 0xdd, 0x09, 0x31, 0x4f, 0x7b, 0x6d, 0x6c, - 0x77, 0x59, 0x5e, 0x1e, 0xfa, 0x4b, 0x06, 0x14, - 0xbe, 0xdc, 0x9c, 0x3d, 0x7b, 0xed, 0xf3, 0x2b, - 0x19, 0x26, 0x11, 0x8e, 0x3f, 0xab, 0x73, 0x9a, - 0x0a, 0x3a, 0xaa, 0x85, 0x06, 0xd5, 0xca, 0x3f, - 0xc3, 0xe2, 0x33, 0x7f, 0x97, 0x74, 0x98, 0x8f, - 0x2f, 0xa5, 0xfc, 0x7e, 0xb1, 0x77, 0x71, 0x58, - 0xf0, 0xbc, 0x04, 0x59, 0xbb, 0xb4, 0xc6, 0xcc, - 0x0f, 0x06, 0xcd, 0xa2, 0xd5, 0x01, 0x2f, 0xb2, - 0x22, 0x0b, 0xfc, 0x1e, 0x59, 0x9f, 0xd3, 0x4f, - 0x30, 0x95, 0xc6, 0x80, 0x0f, 0x69, 0xf3, 0x4a, - 0xd4, 0x36, 0xb6, 0x5a, 0x0b, 0x16, 0x0d, 0x81, - 0x31, 0xb0, 0x69, 0xd4, 0x4e, - ])); - } - } - - async function fakeGeneratekey() { - let key = { "alg": "RSA-OAEP-256", - "d": "B7QR2yI8sXjo8vQhJpX9odqqR6wIuPr" + - "TM1B1JJEKVeSrr7OYcc1FRJ52Vap9LI" + - "AU-ezigs9QDvWMxknB8motLnG69Wck3" + - "7nt9_z4s8lFQp0nROA-oaR92HW34KNL" + - "1b2fEVWGI0N86h730MvTJC5O2cmKeMe" + - "zIG-oNqbbfFyP8AW-WLdDlgZm11-Fjz" + - "hbVpb0Bc7nRSgBPSV-EY6Sl-LuglxDx" + - "4LaTdQW7QE_WXoRUt-GYGfTseuFQQK5" + - "WeoyX3yBtQydpauW6rrgyWdtP4hDFIo" + - "ZsX6w1i-UMWMMwlIB5FdnUSi26igVGA" + - "DGpV_vGMP36bv-EHp0bY-Qp0gpIfLfgQ", - "dp": "Z1v5UceFfV2bhmbG19eGYb30jFxqoR" + - "Bq36PKNY7IunMs1keYy0FpLbyGhtgM" + - "Z1Ymmc8wEzGYsCPEP-ykcun_rlyu7Y" + - "xmcnyC9YQqTqLyqvO-7rUqDvk9TMfd" + - "qWFP6heADRhKZmEbmcau6_m2MwwK9k" + - "OkMKWvpqp8_TpJMnAH7zE", - "dq": "OBacRE15aY3NtCR4cvP5os3sT70JbD" + - "dDLHT3IHZM6rE35CYNpLDia2chm_wn" + - "McYvKFW9zC2ajRZ15i9c_VXQzS7ZlT" + - "aQYBFyMt7kVhxMEMFsPv1crD6t3uEI" + - "j0LNuNYyy0jkon_LPZKQFK654CiL-L" + - "2YaNXOH4HbHP02dWeVQIE", - "e": "AQAB", - "ext": true, - "key_ops": ["decrypt"], - "kty": "RSA", - "n": "m1c92ZFk9ZI6l_O4YFiNxbv0Ng94SB3" + - "yThy1P_mcqrGDQkRiGVdcTxAk38T9Pg" + - "LztmspF-6U5TAHO-gSmmW88AC9m6f1M" + - "spps6r7zl-M_OG-TwvGzf3BDz8zEg1F" + - "PbZV7whO1M4TCAZ0PqwG7qCc6nK1WiA" + - "haKrSpzuPdL1igfNBsX7qu5wgw4ZTTG" + - "SLbVC_LfULQ5FADgFTRXUSaxm1F8C_L" + - "wy6a2e4nTcXilmtN2IHUjHegzm-Tq2H" + - "izmR3ARdWJpESYIW5-AXoiqj29tDrqC" + - "mu2WPkB2psVp83IzZfaQNQzjNfvA8Gp" + - "imkcDCkP5VMRrtKCcG4ZAFnO-A3NBX_Q", - "p": "2Q_lNL7vCOBzAppYzCZo3WSh0hX-MOZ" + - "yPUznks5U2TjmfdNZoL6_FJRiGyyLvw" + - "SiZFdEAAvpAyESFfFigngAqMLSf448n" + - "Pg15VUGj533CotsEM0WpoEr1JCgqdUb" + - "gDAfJQIBcwOmegBqd7lWm7uzEnRCvou" + - "B70ybkJfpdprhkVE", - "q": "tzTt-F3g2u_3Ctj26Ho9iN_wC_W0lXG" + - "zslLt5nLmss8JqdLoDDrijjU-gjeRh7" + - "lgiuHdUc3dorfFKbaMNOjoW3QKqt9oZ" + - "1JM0HKeRw0X2PnWW_0WK6DK5ASWDTXb" + - "Mq2sUZqJvYEyL74H2Zrt0RPAux7XQLE" + - "VgND6ROdXnMJ70O0", - "qi": "qfl4cXQkz4BNqa2De0-PfdU-8d1w3o" + - "nnaGqx1Ds2fHzD_SJ4cNghn2TksoT9" + - "Qo64b3pUjH9igi2pyEjomk6D12N6FG" + - "0e10u7vFKv3W5YqUOgTpYdbcWHdZ2q" + - "ZWJU0XQZIrF8jLGTOO4GYP6_9sJ5R7" + - "Wk_0MdqQy8qvixWD4zLcY", - }; - key = await window.crypto.subtle.importKey("jwk", key, { - name: "RSA-OAEP", - hash: {name: "SHA-256"} - }, true, ["decrypt"]); - return {privateKey: key}; - } - - before(() => { - sinon.stub(window.crypto, "getRandomValues").callsFake(fakeGetRandomValues); - sinon.stub(window.crypto.subtle, "generateKey").callsFake(fakeGeneratekey); - }); - after(() => { - window.crypto.getRandomValues.restore(); - window.crypto.subtle.generateKey.restore(); - }); - - beforeEach(function () { - sendSecurity(6, client); - expect(client._sock).to.have.sent(new Uint8Array([6])); - }); - - const serverPublicKey = [ - 0x00, 0x00, 0x08, 0x00, 0xac, 0x1a, 0xbc, 0x42, - 0x8a, 0x2a, 0x69, 0x65, 0x54, 0xf8, 0x9a, 0xe6, - 0x43, 0xaa, 0xf7, 0x27, 0xf6, 0x2a, 0xf8, 0x8f, - 0x36, 0xd4, 0xae, 0x54, 0x0f, 0x16, 0x28, 0x08, - 0xc2, 0x5b, 0xca, 0x23, 0xdc, 0x27, 0x88, 0x1a, - 0x12, 0x82, 0xa8, 0x54, 0xea, 0x00, 0x99, 0x8d, - 0x02, 0x1d, 0x77, 0x4a, 0xeb, 0xd0, 0x93, 0x40, - 0x79, 0x86, 0xcb, 0x37, 0xd4, 0xb2, 0xc7, 0xcd, - 0x93, 0xe1, 0x00, 0x4d, 0x86, 0xff, 0x97, 0x33, - 0x0c, 0xad, 0x51, 0x47, 0x45, 0x85, 0x56, 0x07, - 0x65, 0x21, 0x7c, 0x57, 0x6d, 0x68, 0x7d, 0xd7, - 0x00, 0x43, 0x0c, 0x9d, 0x3b, 0xa1, 0x5a, 0x11, - 0xed, 0x51, 0x77, 0xf9, 0xd1, 0x5b, 0x33, 0xd7, - 0x1a, 0xeb, 0x65, 0x57, 0xc0, 0x01, 0x51, 0xff, - 0x9b, 0x82, 0xb3, 0xeb, 0x82, 0xc2, 0x1f, 0xca, - 0x47, 0xc0, 0x6a, 0x09, 0xe0, 0xf7, 0xda, 0x39, - 0x85, 0x12, 0xe7, 0x45, 0x8d, 0xb4, 0x1a, 0xda, - 0xcb, 0x86, 0x58, 0x52, 0x37, 0x66, 0x9d, 0x8a, - 0xce, 0xf2, 0x18, 0x78, 0x7d, 0x7f, 0xf0, 0x07, - 0x94, 0x8e, 0x6b, 0x17, 0xd9, 0x00, 0x2a, 0x3a, - 0xb9, 0xd4, 0x77, 0xde, 0x70, 0x85, 0xc4, 0x3a, - 0x62, 0x10, 0x02, 0xee, 0xba, 0xd8, 0xc0, 0x62, - 0xd0, 0x8e, 0xc1, 0x98, 0x19, 0x8e, 0x39, 0x0f, - 0x3e, 0x1d, 0x61, 0xb1, 0x93, 0x13, 0x59, 0x39, - 0xcb, 0x96, 0xf2, 0x17, 0xc9, 0xe1, 0x41, 0xd3, - 0x20, 0xdd, 0x62, 0x5e, 0x7d, 0x53, 0xd6, 0xb7, - 0x1d, 0xfe, 0x02, 0x18, 0x1f, 0xe0, 0xef, 0x3d, - 0x94, 0xe3, 0x0a, 0x9c, 0x59, 0x54, 0xd8, 0x98, - 0x16, 0x9c, 0x31, 0xda, 0x41, 0x0f, 0x2e, 0x71, - 0x68, 0xe0, 0xa2, 0x62, 0x3e, 0xe5, 0x25, 0x31, - 0xcf, 0xfc, 0x67, 0x63, 0xc3, 0xb0, 0xda, 0x3f, - 0x7b, 0x59, 0xbe, 0x7e, 0x9e, 0xa8, 0xd0, 0x01, - 0x4f, 0x43, 0x7f, 0x8d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x01, - ]; - - const serverRandom = [ - 0x01, 0x00, 0x5b, 0x58, 0x2a, 0x96, 0x2d, 0xbb, - 0x88, 0xec, 0xc3, 0x54, 0x00, 0xf3, 0xbb, 0xbe, - 0x17, 0xa3, 0x84, 0xd3, 0xef, 0xd8, 0x4a, 0x31, - 0x09, 0x20, 0xdd, 0xbc, 0x16, 0x9d, 0xc9, 0x5b, - 0x99, 0x62, 0x86, 0xfe, 0x0b, 0x28, 0x4b, 0xfe, - 0x5b, 0x56, 0x2d, 0xcb, 0x6e, 0x6f, 0xec, 0xf0, - 0x53, 0x0c, 0x33, 0x84, 0x93, 0xc9, 0xbf, 0x79, - 0xde, 0xb3, 0xb9, 0x29, 0x60, 0x78, 0xde, 0xe6, - 0x1d, 0xa7, 0x89, 0x48, 0x3f, 0xd1, 0x58, 0x66, - 0x27, 0x9c, 0xd4, 0x6e, 0x72, 0x9c, 0x6e, 0x4a, - 0xc0, 0x69, 0x79, 0x6f, 0x79, 0x0f, 0x13, 0xc4, - 0x20, 0xcf, 0xa6, 0xbb, 0xce, 0x18, 0x6d, 0xd5, - 0x9e, 0xd9, 0x67, 0xbe, 0x61, 0x43, 0x67, 0x11, - 0x76, 0x2f, 0xfd, 0x78, 0x75, 0x2b, 0x89, 0x35, - 0xdd, 0x0f, 0x13, 0x7f, 0xee, 0x78, 0xad, 0x32, - 0x56, 0x21, 0x81, 0x08, 0x1f, 0xcf, 0x4c, 0x29, - 0xa3, 0xeb, 0x89, 0x2d, 0xbe, 0xba, 0x8d, 0xe4, - 0x69, 0x28, 0xba, 0x53, 0x82, 0xce, 0x5c, 0xf6, - 0x5e, 0x5e, 0xa5, 0xb3, 0x88, 0xd8, 0x3d, 0xab, - 0xf4, 0x24, 0x9e, 0x3f, 0x04, 0xaf, 0xdc, 0x48, - 0x90, 0x53, 0x37, 0xe6, 0x82, 0x1d, 0xe0, 0x15, - 0x91, 0xa1, 0xc6, 0xa9, 0x54, 0xe5, 0x2a, 0xb5, - 0x64, 0x2d, 0x93, 0xc0, 0xc0, 0xe1, 0x0f, 0x6a, - 0x4b, 0xdb, 0x77, 0xf8, 0x4a, 0x0f, 0x83, 0x36, - 0xdd, 0x5e, 0x1e, 0xdd, 0x39, 0x65, 0xa2, 0x11, - 0xc2, 0xcf, 0x56, 0x1e, 0xa1, 0x29, 0xae, 0x11, - 0x9f, 0x3a, 0x82, 0xc7, 0xbd, 0x89, 0x6e, 0x59, - 0xb8, 0x59, 0x17, 0xcb, 0x65, 0xa0, 0x4b, 0x4d, - 0xbe, 0x33, 0x32, 0x85, 0x9c, 0xca, 0x5e, 0x95, - 0xc2, 0x5a, 0xd0, 0xc9, 0x8b, 0xf1, 0xf5, 0x14, - 0xcf, 0x76, 0x80, 0xc2, 0x24, 0x0a, 0x39, 0x7e, - 0x60, 0x64, 0xce, 0xd9, 0xb8, 0xad, 0x24, 0xa8, - 0xdf, 0xcb, - ]; - - const serverHash = [ - 0x00, 0x14, 0x39, 0x30, 0x66, 0xb5, 0x66, 0x8a, - 0xcd, 0xb9, 0xda, 0xe0, 0xde, 0xcb, 0xf6, 0x47, - 0x5f, 0x54, 0x66, 0xe0, 0xbc, 0x49, 0x37, 0x01, - 0xf2, 0x9e, 0xef, 0xcc, 0xcd, 0x4d, 0x6c, 0x0e, - 0xc6, 0xab, 0x28, 0xd4, 0x7b, 0x13, - ]; - - const subType = [ - 0x00, 0x01, 0x30, 0x2a, 0xc3, 0x0b, 0xc2, 0x1c, - 0xeb, 0x02, 0x44, 0x92, 0x5d, 0xfd, 0xf9, 0xa7, - 0x94, 0xd0, 0x19, - ]; - - const clientPublicKey = [ - 0x00, 0x00, 0x08, 0x00, 0x9b, 0x57, 0x3d, 0xd9, - 0x91, 0x64, 0xf5, 0x92, 0x3a, 0x97, 0xf3, 0xb8, - 0x60, 0x58, 0x8d, 0xc5, 0xbb, 0xf4, 0x36, 0x0f, - 0x78, 0x48, 0x1d, 0xf2, 0x4e, 0x1c, 0xb5, 0x3f, - 0xf9, 0x9c, 0xaa, 0xb1, 0x83, 0x42, 0x44, 0x62, - 0x19, 0x57, 0x5c, 0x4f, 0x10, 0x24, 0xdf, 0xc4, - 0xfd, 0x3e, 0x02, 0xf3, 0xb6, 0x6b, 0x29, 0x17, - 0xee, 0x94, 0xe5, 0x30, 0x07, 0x3b, 0xe8, 0x12, - 0x9a, 0x65, 0xbc, 0xf0, 0x00, 0xbd, 0x9b, 0xa7, - 0xf5, 0x32, 0xca, 0x69, 0xb3, 0xaa, 0xfb, 0xce, - 0x5f, 0x8c, 0xfc, 0xe1, 0xbe, 0x4f, 0x0b, 0xc6, - 0xcd, 0xfd, 0xc1, 0x0f, 0x3f, 0x33, 0x12, 0x0d, - 0x45, 0x3d, 0xb6, 0x55, 0xef, 0x08, 0x4e, 0xd4, - 0xce, 0x13, 0x08, 0x06, 0x74, 0x3e, 0xac, 0x06, - 0xee, 0xa0, 0x9c, 0xea, 0x72, 0xb5, 0x5a, 0x20, - 0x21, 0x68, 0xaa, 0xd2, 0xa7, 0x3b, 0x8f, 0x74, - 0xbd, 0x62, 0x81, 0xf3, 0x41, 0xb1, 0x7e, 0xea, - 0xbb, 0x9c, 0x20, 0xc3, 0x86, 0x53, 0x4c, 0x64, - 0x8b, 0x6d, 0x50, 0xbf, 0x2d, 0xf5, 0x0b, 0x43, - 0x91, 0x40, 0x0e, 0x01, 0x53, 0x45, 0x75, 0x12, - 0x6b, 0x19, 0xb5, 0x17, 0xc0, 0xbf, 0x2f, 0x0c, - 0xba, 0x6b, 0x67, 0xb8, 0x9d, 0x37, 0x17, 0x8a, - 0x59, 0xad, 0x37, 0x62, 0x07, 0x52, 0x31, 0xde, - 0x83, 0x39, 0xbe, 0x4e, 0xad, 0x87, 0x8b, 0x39, - 0x91, 0xdc, 0x04, 0x5d, 0x58, 0x9a, 0x44, 0x49, - 0x82, 0x16, 0xe7, 0xe0, 0x17, 0xa2, 0x2a, 0xa3, - 0xdb, 0xdb, 0x43, 0xae, 0xa0, 0xa6, 0xbb, 0x65, - 0x8f, 0x90, 0x1d, 0xa9, 0xb1, 0x5a, 0x7c, 0xdc, - 0x8c, 0xd9, 0x7d, 0xa4, 0x0d, 0x43, 0x38, 0xcd, - 0x7e, 0xf0, 0x3c, 0x1a, 0x98, 0xa6, 0x91, 0xc0, - 0xc2, 0x90, 0xfe, 0x55, 0x31, 0x1a, 0xed, 0x28, - 0x27, 0x06, 0xe1, 0x90, 0x05, 0x9c, 0xef, 0x80, - 0xdc, 0xd0, 0x57, 0xfd, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x01, - ]; - - const clientRandom = [ - 0x01, 0x00, 0x84, 0x7f, 0x26, 0x54, 0x74, 0xf6, - 0x47, 0xaf, 0x33, 0x64, 0x0d, 0xa6, 0xe5, 0x30, - 0xba, 0xe6, 0xe4, 0x8e, 0x50, 0x40, 0x71, 0x1c, - 0x0e, 0x06, 0x63, 0xf5, 0x07, 0x2a, 0x26, 0x68, - 0xd6, 0xcf, 0xa6, 0x80, 0x84, 0x5e, 0x64, 0xd4, - 0x5e, 0x62, 0x31, 0xfe, 0x44, 0x51, 0x0b, 0x7c, - 0x4d, 0x55, 0xc5, 0x4a, 0x7e, 0x0d, 0x4d, 0x9b, - 0x84, 0xb4, 0x32, 0x2b, 0x4d, 0x8a, 0x34, 0x8d, - 0xc8, 0xcf, 0x19, 0x3b, 0x64, 0x82, 0x27, 0x9e, - 0xa7, 0x70, 0x2a, 0xc1, 0xb8, 0xf3, 0x6a, 0x3a, - 0xf2, 0x75, 0x6e, 0x1d, 0xeb, 0xb6, 0x70, 0x7a, - 0x15, 0x18, 0x38, 0x00, 0xb4, 0x4f, 0x55, 0xb5, - 0xd8, 0x03, 0x4e, 0xb8, 0x53, 0xff, 0x80, 0x62, - 0xf1, 0x9d, 0x27, 0xe8, 0x2a, 0x3d, 0x98, 0x19, - 0x32, 0x09, 0x7e, 0x9a, 0xb0, 0xc7, 0x46, 0x23, - 0x10, 0x85, 0x35, 0x00, 0x96, 0xce, 0xb3, 0x2c, - 0x84, 0x8d, 0xf4, 0x9e, 0xa8, 0x42, 0x67, 0xed, - 0x09, 0xa6, 0x09, 0x97, 0xb3, 0x64, 0x26, 0xfb, - 0x71, 0x11, 0x9b, 0x3f, 0xbb, 0x57, 0xb8, 0x5b, - 0x2e, 0xc5, 0x2d, 0x8c, 0x5c, 0xf7, 0xef, 0x27, - 0x25, 0x88, 0x42, 0x45, 0x43, 0xa4, 0xe7, 0xde, - 0xea, 0xf9, 0x15, 0x7b, 0x5d, 0x66, 0x24, 0xce, - 0xf7, 0xc8, 0x2f, 0xc5, 0xc0, 0x3d, 0xcd, 0xf2, - 0x62, 0xfc, 0x1a, 0x5e, 0xec, 0xff, 0xf1, 0x1b, - 0xc8, 0xdb, 0xc1, 0x0f, 0x54, 0x66, 0x9e, 0xfd, - 0x99, 0x9b, 0x23, 0x70, 0x62, 0x37, 0x80, 0xad, - 0x91, 0x6b, 0x84, 0x85, 0x6a, 0x4c, 0x80, 0x9e, - 0x60, 0x8a, 0x93, 0xa3, 0xc8, 0x8e, 0xc4, 0x4b, - 0x4d, 0xb4, 0x8e, 0x3e, 0xaf, 0xce, 0xcd, 0x83, - 0xe5, 0x21, 0x90, 0x95, 0x20, 0x3c, 0x82, 0xb4, - 0x7c, 0xab, 0x63, 0x9c, 0xae, 0xc3, 0xc9, 0x71, - 0x1a, 0xec, 0x34, 0x18, 0x47, 0xec, 0x5c, 0x4d, - 0xed, 0x84, - ]; - - const clientHash = [ - 0x00, 0x14, 0x9c, 0x91, 0x9e, 0x76, 0xcf, 0x1e, - 0x66, 0x87, 0x5e, 0x29, 0xf1, 0x13, 0x80, 0xea, - 0x7d, 0xec, 0xae, 0xf9, 0x60, 0x01, 0xd3, 0x6f, - 0xb7, 0x9e, 0xb2, 0xcd, 0x2d, 0xc8, 0xf8, 0x84, - 0xb2, 0x9f, 0xc3, 0x7e, 0xb4, 0xbe, - ]; - - const credentialsData = [ - 0x00, 0x08, 0x9d, 0xc8, 0x3a, 0xb8, 0x80, 0x4f, - 0xe3, 0x52, 0xdb, 0x62, 0x9e, 0x97, 0x64, 0x82, - 0xa8, 0xa1, 0x6b, 0x7e, 0x4d, 0x68, 0x8c, 0x29, - 0x91, 0x38, - ]; - - it('should fire the serververification event', async function () { - let verification = new Promise((resolve, reject) => { - client.addEventListener("serververification", (e) => { - resolve(e.detail.publickey); - }); - }); - - client._sock._websocket._receiveData(new Uint8Array(serverPublicKey)); - client._sock._websocket._receiveData(new Uint8Array(serverRandom)); - - expect(await verification).to.deep.equal(new Uint8Array(serverPublicKey)); - }); - - it('should handle approveServer and fire the credentialsrequired event', async function () { - let verification = new Promise((resolve, reject) => { - client.addEventListener("serververification", (e) => { - resolve(e.detail.publickey); - }); - }); - let credentials = new Promise((resolve, reject) => { - client.addEventListener("credentialsrequired", (e) => { - resolve(e.detail.types); - }); - }); - - client._sock._websocket._receiveData(new Uint8Array(serverPublicKey)); - client._sock._websocket._receiveData(new Uint8Array(serverRandom)); - - await verification; - client.approveServer(); - - client._sock._websocket._receiveData(new Uint8Array(serverHash)); - client._sock._websocket._receiveData(new Uint8Array(subType)); - - expect(await credentials).to.have.members(["password"]); - }); - - it('should send credentials to server', async function () { - let verification = new Promise((resolve, reject) => { - client.addEventListener("serververification", (e) => { - resolve(e.detail.publickey); - }); - }); - let credentials = new Promise((resolve, reject) => { - client.addEventListener("credentialsrequired", (e) => { - resolve(e.detail.types); - }); - }); - - client._sock._websocket._receiveData(new Uint8Array(serverPublicKey)); - client._sock._websocket._receiveData(new Uint8Array(serverRandom)); - - await verification; - client.approveServer(); - - client._sock._websocket._receiveData(new Uint8Array(serverHash)); - client._sock._websocket._receiveData(new Uint8Array(subType)); - - await credentials; - - let expected = []; - expected = expected.concat(clientPublicKey); - expected = expected.concat(clientRandom); - expected = expected.concat(clientHash); - expect(client._sock).to.have.sent(new Uint8Array(expected)); - - client.sendCredentials({ "password": "123456" }); - clock.tick(); - - // FIXME: We don't have a good way to know when - // the async stuff is done, so we hook in - // to this internal function that is - // called at the end - await new Promise((resolve, reject) => { - sinon.stub(client._sock._websocket, "send") - .callsFake((data) => { - FakeWebSocket.prototype.send.call(client._sock._websocket, data); - resolve(); - }); - }); - - expect(client._sock).to.have.sent(new Uint8Array(credentialsData)); - }); - }); - - describe('ARD Authentication (type 30) Handler', function () { - let byteArray = new Uint8Array(Array.from(new Uint8Array(128).keys())); - function fakeGetRandomValues(arr) { - if (arr.length == 128) { - arr.set(byteArray); - } - return arr; - } - before(() => { - sinon.stub(window.crypto, "getRandomValues").callsFake(fakeGetRandomValues); - }); - after(() => { - window.crypto.getRandomValues.restore(); - }); - it('should fire the credentialsrequired event if all credentials are missing', function () { - const spy = sinon.spy(); - client.addEventListener("credentialsrequired", spy); - sendSecurity(30, client); - - expect(spy).to.have.been.calledOnce; - expect(spy.args[0][0].detail.types).to.have.members(["username", "password"]); - }); - - it('should fire the credentialsrequired event if some credentials are missing', function () { - const spy = sinon.spy(); - client.addEventListener("credentialsrequired", spy); - client.sendCredentials({ password: 'password'}); - sendSecurity(30, client); - - expect(spy).to.have.been.calledOnce; - expect(spy.args[0][0].detail.types).to.have.members(["username", "password"]); - }); - - it('should return properly encrypted credentials and public key', async function () { - client.addEventListener("credentialsrequired", () => { - client.sendCredentials({ username: 'user', - password: 'password' }); - }); - sendSecurity(30, client); - - expect(client._sock).to.have.sent([30]); - - const generator = new Uint8Array([127, 255]); - const prime = new Uint8Array(byteArray); - const serverKey = legacyCrypto.generateKey( - { name: "DH", g: generator, p: prime }, false, ["deriveBits"]); - const clientKey = legacyCrypto.generateKey( - { name: "DH", g: generator, p: prime }, false, ["deriveBits"]); - const serverPublicKey = legacyCrypto.exportKey("raw", serverKey.publicKey); - const clientPublicKey = legacyCrypto.exportKey("raw", clientKey.publicKey); - - let data = []; - - data = data.concat(Array.from(generator)); - push16(data, prime.length); - data = data.concat(Array.from(prime)); - data = data.concat(Array.from(serverPublicKey)); - - client._sock._websocket._receiveData(new Uint8Array(data)); - - // FIXME: We don't have a good way to know when the - // async stuff is done, so we hook in to this - // internal function that is called at the - // end - await new Promise((resolve, reject) => { - sinon.stub(client, "_resumeAuthentication") - .callsFake(() => { - RFB.prototype._resumeAuthentication.call(client); - resolve(); - }); - }); - clock.tick(); - - expect(client._rfbInitState).to.equal('SecurityResult'); - - let expectEncrypted = new Uint8Array([ - 199, 39, 204, 95, 190, 70, 127, 66, 5, 106, 153, 228, 123, 236, 150, 206, - 62, 107, 11, 4, 21, 242, 92, 184, 9, 81, 35, 125, 56, 167, 1, 215, - 182, 145, 183, 75, 245, 197, 47, 19, 122, 94, 64, 76, 77, 163, 222, 143, - 186, 174, 84, 39, 244, 179, 227, 114, 83, 231, 42, 106, 205, 43, 159, 110, - 209, 240, 157, 246, 237, 206, 134, 153, 195, 112, 92, 60, 28, 234, 91, 66, - 131, 38, 187, 195, 110, 167, 212, 241, 32, 250, 212, 213, 202, 89, 180, 21, - 71, 217, 209, 81, 42, 61, 118, 248, 65, 123, 98, 78, 139, 111, 202, 137, - 50, 185, 37, 173, 58, 99, 187, 53, 42, 125, 13, 165, 232, 163, 151, 42, 0]); - - let output = new Uint8Array(256); - output.set(expectEncrypted, 0); - output.set(clientPublicKey, 128); - - expect(client._sock).to.have.sent(output); - }); - }); - - describe('MSLogonII Authentication (type 113) Handler', function () { - function fakeGetRandomValues(arr) { - if (arr.length == 8) { - arr.set(new Uint8Array([0, 0, 0, 0, 5, 6, 7, 8])); - } else if (arr.length == 256) { - arr.set(new Uint8Array(256)); - } else if (arr.length == 64) { - arr.set(new Uint8Array(64)); - } - return arr; - } - const expected = new Uint8Array([ - 0x00, 0x00, 0x00, 0x00, 0x0a, 0xbc, 0x7c, 0xfd, - 0x58, 0x34, 0xd2, 0x24, 0x44, 0x60, 0xf0, 0xd1, - 0xa3, 0x73, 0x32, 0x02, 0x07, 0xce, 0xc1, 0x3f, - 0x10, 0x53, 0xf1, 0xdd, 0x99, 0xad, 0x44, 0x18, - 0xa1, 0xc4, 0xac, 0xc1, 0x1c, 0x13, 0x11, 0x85, - 0x3a, 0x6f, 0xcb, 0xc6, 0xb1, 0x6c, 0x68, 0x47, - 0x85, 0x01, 0xbb, 0xfa, 0x23, 0x8c, 0x59, 0x47, - 0x67, 0x47, 0x56, 0x6e, 0x6f, 0x9f, 0x07, 0x76, - 0x2e, 0x90, 0x1e, 0xdc, 0x80, 0xc4, 0x4b, 0x72, - 0xd2, 0xd5, 0xcd, 0x4b, 0x14, 0xff, 0x05, 0x8b, - 0x8d, 0xf1, 0x9b, 0xe0, 0xff, 0xa5, 0x3b, 0x56, - 0xb9, 0x6f, 0x84, 0x3e, 0x15, 0x84, 0x31, 0x4e, - 0x10, 0x0b, 0x56, 0xf4, 0x10, 0x05, 0x02, 0xc7, - 0x05, 0x0b, 0xc9, 0x66, 0x75, 0x32, 0xd3, 0x74, - 0xfc, 0x8c, 0xcf, 0xbd, 0x2d, 0x53, 0xd7, 0xa7, - 0xca, 0x82, 0x12, 0xce, 0xbb, 0x33, 0x09, 0x3f, - 0xff, 0x76, 0x7c, 0xdf, 0x2c, 0x2f, 0x4d, 0x95, - 0x86, 0xe4, 0x10, 0x07, 0x75, 0x1a, 0x6d, 0xdb, - 0x05, 0x91, 0x70, 0x34, 0x5c, 0x12, 0xbc, 0x4e, - 0x5e, 0xd0, 0x21, 0x39, 0x25, 0x2b, 0x62, 0x19, - 0x29, 0xa5, 0xe6, 0x93, 0x7b, 0xf8, 0x3f, 0xcf, - 0xd7, 0x3f, 0x0c, 0xd2, 0x68, 0x2d, 0x1e, 0x01, - 0x1a, 0x31, 0xc1, 0x59, 0x04, 0x06, 0xf6, 0x3b, - 0xec, 0x38, 0xef, 0x1b, 0x5b, 0x39, 0x88, 0xd3, - 0xe0, 0x5b, 0xb9, 0xef, 0xc3, 0x82, 0xfa, 0xdf, - 0x04, 0xf7, 0x65, 0x56, 0x82, 0x77, 0xfd, 0x63, - 0x10, 0xd7, 0xab, 0x0b, 0x5e, 0xd9, 0x07, 0x81, - 0x9d, 0xce, 0x26, 0xfb, 0x5d, 0xa8, 0x59, 0x2a, - 0xd9, 0xb8, 0xac, 0xcd, 0x6e, 0x61, 0x07, 0x39, - 0x9f, 0x8d, 0xdf, 0x53, 0x44, 0xab, 0x28, 0x01, - 0x86, 0x4d, 0x07, 0x8a, 0x5b, 0xdd, 0xc1, 0x18, - 0x29, 0xaa, 0xa2, 0xbe, 0xe2, 0x9c, 0x9e, 0xb0, - 0xb3, 0x2b, 0x2c, 0x93, 0x3e, 0x82, 0x07, 0xa6, - 0xef, 0x21, 0x2c, 0xa7, 0xf0, 0x65, 0xba, 0xda, - 0x13, 0xe4, 0x41, 0x87, 0x36, 0x1c, 0xa5, 0x81, - 0xae, 0xf3, 0x3e, 0xda, 0x03, 0x09, 0x63, 0x4b, - 0xb5, 0x29, 0x49, 0xfa, 0xbb, 0xa6, 0x31, 0x3c, - 0xc8, 0x15, 0xfb, 0xfc, 0xd6, 0xff, 0x04, 0x92, - 0x56, 0xbc, 0x66, 0xf1, 0x78, 0xfb, 0x14, 0x79, - 0x48, 0xd2, 0xcf, 0x87, 0x60, 0x23, 0xcf, 0xdb, - 0x1b, 0xad, 0x42, 0x32, 0x4e, 0x6d, 0x1f, 0x49, - ]); - before(() => { - sinon.stub(window.crypto, "getRandomValues").callsFake(fakeGetRandomValues); - }); - after(() => { - window.crypto.getRandomValues.restore(); - }); - it('should send public value and encrypted credentials', function () { - client.addEventListener("credentialsrequired", () => { - client.sendCredentials({ username: 'username', - password: 'password123456' }); - }); - sendSecurity(113, client); - - expect(client._sock).to.have.sent([113]); - - const g = new Uint8Array([0, 0, 0, 0, 0, 1, 0, 1]); - const p = new Uint8Array([0, 0, 0, 0, 0x25, 0x18, 0x26, 0x17]); - const A = new Uint8Array([0, 0, 0, 0, 0x0e, 0x12, 0xd0, 0xf5]); - - client._sock._websocket._receiveData(g); - client._sock._websocket._receiveData(p); - client._sock._websocket._receiveData(A); - clock.tick(); - - expect(client._sock).to.have.sent(expected); - expect(client._rfbInitState).to.equal('SecurityResult'); - }); - }); - - describe('XVP Authentication (type 22) Handler', function () { - it('should fall through to standard VNC authentication upon completion', function () { - client.addEventListener("credentialsrequired", () => { - client.sendCredentials({ username: 'user', - target: 'target', - password: 'password' }); - }); - sinon.spy(client, "_negotiateStdVNCAuth"); - sendSecurity(22, client); - clock.tick(); - expect(client._negotiateStdVNCAuth).to.have.been.calledOnce; - }); - - it('should fire the credentialsrequired event if all credentials are missing', function () { - const spy = sinon.spy(); - client.addEventListener("credentialsrequired", spy); - sendSecurity(22, client); - - expect(spy).to.have.been.calledOnce; - expect(spy.args[0][0].detail.types).to.have.members(["username", "password", "target"]); - }); - - it('should fire the credentialsrequired event if some credentials are missing', function () { - const spy = sinon.spy(); - client.addEventListener("credentialsrequired", spy); - client.sendCredentials({ username: 'user', - target: 'target' }); - sendSecurity(22, client); - - expect(spy).to.have.been.calledOnce; - expect(spy.args[0][0].detail.types).to.have.members(["username", "password", "target"]); - }); - - it('should send user and target separately', function () { - client.addEventListener("credentialsrequired", () => { - client.sendCredentials({ username: 'user', - target: 'target', - password: 'password' }); - }); - sendSecurity(22, client); - clock.tick(); - - const expected = [22, 4, 6]; // auth selection, len user, len target - for (let i = 0; i < 10; i++) { expected[i+3] = 'usertarget'.charCodeAt(i); } - - expect(client._sock).to.have.sent(new Uint8Array(expected)); - }); - }); - - describe('TightVNC Authentication (type 16) Handler', function () { - beforeEach(function () { - sendSecurity(16, client); - client._sock._websocket._getSentData(); // skip the security reply - }); - - function sendNumStrPairs(pairs, client) { - const data = []; - push32(data, pairs.length); - - for (let i = 0; i < pairs.length; i++) { - push32(data, pairs[i][0]); - for (let j = 0; j < 4; j++) { - data.push(pairs[i][1].charCodeAt(j)); - } - for (let j = 0; j < 8; j++) { - data.push(pairs[i][2].charCodeAt(j)); - } - } - - client._sock._websocket._receiveData(new Uint8Array(data)); - } - - it('should skip tunnel negotiation if no tunnels are requested', function () { - client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 0])); - expect(client._rfbTightVNC).to.be.true; - }); - - it('should fail if no supported tunnels are listed', function () { - let callback = sinon.spy(); - client.addEventListener("disconnect", callback); - - sendNumStrPairs([[123, 'OTHR', 'SOMETHNG']], client); - - expect(callback).to.have.been.calledOnce; - expect(callback.args[0][0].detail.clean).to.be.false; - }); - - it('should choose the notunnel tunnel type', function () { - sendNumStrPairs([[0, 'TGHT', 'NOTUNNEL'], [123, 'OTHR', 'SOMETHNG']], client); - expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 0])); - }); - - it('should choose the notunnel tunnel type for Siemens devices', function () { - sendNumStrPairs([[1, 'SICR', 'SCHANNEL'], [2, 'SICR', 'SCHANLPW']], client); - expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 0])); - }); - - it('should continue to sub-auth negotiation after tunnel negotiation', function () { - sendNumStrPairs([[0, 'TGHT', 'NOTUNNEL']], client); - client._sock._websocket._getSentData(); // skip the tunnel choice here - sendNumStrPairs([[1, 'STDV', 'NOAUTH__']], client); - expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 1])); - expect(client._rfbInitState).to.equal('SecurityResult'); - }); - - it('should accept the "no auth" auth type and transition to SecurityResult', function () { - sendNumStrPairs([[0, 'TGHT', 'NOTUNNEL']], client); - client._sock._websocket._getSentData(); // skip the tunnel choice here - sendNumStrPairs([[1, 'STDV', 'NOAUTH__']], client); - expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 1])); - expect(client._rfbInitState).to.equal('SecurityResult'); - }); - - it('should accept VNC authentication and transition to that', function () { - sendNumStrPairs([[0, 'TGHT', 'NOTUNNEL']], client); - client._sock._websocket._getSentData(); // skip the tunnel choice here - sinon.spy(client, "_negotiateStdVNCAuth"); - sendNumStrPairs([[2, 'STDV', 'VNCAUTH__']], client); - expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 2])); - expect(client._negotiateStdVNCAuth).to.have.been.calledOnce; - expect(client._rfbAuthScheme).to.equal(2); - }); - - it('should fail if there are no supported auth types', function () { - let callback = sinon.spy(); - client.addEventListener("disconnect", callback); - - sendNumStrPairs([[0, 'TGHT', 'NOTUNNEL']], client); - client._sock._websocket._getSentData(); // skip the tunnel choice here - sendNumStrPairs([[23, 'stdv', 'badval__']], client); - - expect(callback).to.have.been.calledOnce; - expect(callback.args[0][0].detail.clean).to.be.false; - }); - }); - - describe('VeNCrypt Authentication (type 19) Handler', function () { - beforeEach(function () { - sendSecurity(19, client); - expect(client._sock).to.have.sent(new Uint8Array([19])); - }); - - it('should fail with non-0.2 versions', function () { - let callback = sinon.spy(); - client.addEventListener("disconnect", callback); - - client._sock._websocket._receiveData(new Uint8Array([0, 1])); - - expect(callback).to.have.been.calledOnce; - expect(callback.args[0][0].detail.clean).to.be.false; - }); - - it('should fail if there are no supported subtypes', function () { - // VeNCrypt version - client._sock._websocket._receiveData(new Uint8Array([0, 2])); - expect(client._sock).to.have.sent(new Uint8Array([0, 2])); - // Server ACK. - client._sock._websocket._receiveData(new Uint8Array([0])); - // Subtype list - let callback = sinon.spy(); - client.addEventListener("disconnect", callback); - client._sock._websocket._receiveData(new Uint8Array([2, 0, 0, 0, 9, 0, 0, 1, 4])); - expect(callback).to.have.been.calledOnce; - expect(callback.args[0][0].detail.clean).to.be.false; - }); - - it('should support standard types', function () { - // VeNCrypt version - client._sock._websocket._receiveData(new Uint8Array([0, 2])); - expect(client._sock).to.have.sent(new Uint8Array([0, 2])); - // Server ACK. - client._sock._websocket._receiveData(new Uint8Array([0])); - // Subtype list - client._sock._websocket._receiveData(new Uint8Array([2, 0, 0, 0, 2, 0, 0, 1, 4])); - - let expectedResponse = []; - push32(expectedResponse, 2); // Chosen subtype. - - expect(client._sock).to.have.sent(new Uint8Array(expectedResponse)); - }); - - it('should respect server preference order', function () { - // VeNCrypt version - client._sock._websocket._receiveData(new Uint8Array([0, 2])); - expect(client._sock).to.have.sent(new Uint8Array([0, 2])); - // Server ACK. - client._sock._websocket._receiveData(new Uint8Array([0])); - // Subtype list - let subtypes = [ 6 ]; - push32(subtypes, 79); - push32(subtypes, 30); - push32(subtypes, 188); - push32(subtypes, 256); - push32(subtypes, 6); - push32(subtypes, 1); - client._sock._websocket._receiveData(new Uint8Array(subtypes)); - - let expectedResponse = []; - push32(expectedResponse, 30); // Chosen subtype. - - expect(client._sock).to.have.sent(new Uint8Array(expectedResponse)); - }); - - it('should ignore redundant VeNCrypt subtype', function () { - // VeNCrypt version - client._sock._websocket._receiveData(new Uint8Array([0, 2])); - expect(client._sock).to.have.sent(new Uint8Array([0, 2])); - // Server ACK. - client._sock._websocket._receiveData(new Uint8Array([0])); - // Subtype list - client._sock._websocket._receiveData(new Uint8Array([2, 0, 0, 0, 19, 0, 0, 0, 2])); - - let expectedResponse = []; - push32(expectedResponse, 2); // Chosen subtype. - - expect(client._sock).to.have.sent(new Uint8Array(expectedResponse)); - }); - }); - - describe('Plain Authentication (type 256) Handler', function () { - beforeEach(function () { - sendSecurity(19, client); - expect(client._sock).to.have.sent(new Uint8Array([19])); - // VeNCrypt version - client._sock._websocket._receiveData(new Uint8Array([0, 2])); - expect(client._sock).to.have.sent(new Uint8Array([0, 2])); - // Server ACK. - client._sock._websocket._receiveData(new Uint8Array([0])); - }); - - it('should support Plain authentication', function () { - client.addEventListener("credentialsrequired", () => { - client.sendCredentials({ username: 'username', password: 'password' }); - }); - client._sock._websocket._receiveData(new Uint8Array([1, 0, 0, 1, 0])); - expect(client._sock).to.have.sent(new Uint8Array([0, 0, 1, 0])); - - clock.tick(); - - const expectedResponse = []; - push32(expectedResponse, 8); - push32(expectedResponse, 8); - pushString(expectedResponse, 'username'); - pushString(expectedResponse, 'password'); - expect(client._sock).to.have.sent(new Uint8Array(expectedResponse)); - }); - - it('should support Plain authentication with an empty password', function () { - client.addEventListener("credentialsrequired", () => { - client.sendCredentials({ username: 'username', password: '' }); - }); - client._sock._websocket._receiveData(new Uint8Array([1, 0, 0, 1, 0])); - expect(client._sock).to.have.sent(new Uint8Array([0, 0, 1, 0])); - - clock.tick(); - - const expectedResponse = []; - push32(expectedResponse, 8); - push32(expectedResponse, 0); - pushString(expectedResponse, 'username'); - pushString(expectedResponse, ''); - expect(client._sock).to.have.sent(new Uint8Array(expectedResponse)); - }); - - it('should support Plain authentication with a very long username and password', function () { - client.addEventListener("credentialsrequired", () => { - client.sendCredentials({ username: 'a'.repeat(300), password: 'b'.repeat(300) }); - }); - client._sock._websocket._receiveData(new Uint8Array([1, 0, 0, 1, 0])); - expect(client._sock).to.have.sent(new Uint8Array([0, 0, 1, 0])); - - clock.tick(); - - const expectedResponse = []; - push32(expectedResponse, 300); - push32(expectedResponse, 300); - pushString(expectedResponse, 'a'.repeat(300)); - pushString(expectedResponse, 'b'.repeat(300)); - expect(client._sock).to.have.sent(new Uint8Array(expectedResponse)); - }); - }); - }); - - describe('Legacy SecurityResult', function () { - it('should not include reason in securityfailure event for versions < 3.7', function () { - client.addEventListener("credentialsrequired", () => { - client.sendCredentials({ password: 'passwd' }); - }); - const spy = sinon.spy(); - client.addEventListener("securityfailure", spy); - sendVer('003.006\n', client); - client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 2])); - const challenge = []; - for (let i = 0; i < 16; i++) { challenge[i] = i; } - client._sock._websocket._receiveData(new Uint8Array(challenge)); - - client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 2])); - expect(spy).to.have.been.calledOnce; - expect(spy.args[0][0].detail.status).to.equal(2); - expect('reason' in spy.args[0][0].detail).to.be.false; - }); - - it('should not include reason in securityfailure event for versions < 3.8', function () { - client.addEventListener("credentialsrequired", () => { - client.sendCredentials({ password: 'passwd' }); - }); - const spy = sinon.spy(); - client.addEventListener("securityfailure", spy); - sendVer('003.007\n', client); - sendSecurity(2, client); - const challenge = []; - for (let i = 0; i < 16; i++) { challenge[i] = i; } - client._sock._websocket._receiveData(new Uint8Array(challenge)); - - client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 2])); - expect(spy).to.have.been.calledOnce; - expect(spy.args[0][0].detail.status).to.equal(2); - expect('reason' in spy.args[0][0].detail).to.be.false; - }); - }); - - describe('SecurityResult', function () { - beforeEach(function () { - sendVer('003.008\n', client); - client._sock._websocket._getSentData(); - sendSecurity(1, client); - client._sock._websocket._getSentData(); - }); - - it('should fall through to ServerInitialisation on a response code of 0', function () { - client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 0])); - expect(client._rfbInitState).to.equal('ServerInitialisation'); - }); - - it('should include reason when provided in securityfailure event', function () { - const spy = sinon.spy(); - client.addEventListener("securityfailure", spy); - const failureData = [0, 0, 0, 1, 0, 0, 0, 12, 115, 117, 99, 104, - 32, 102, 97, 105, 108, 117, 114, 101]; - client._sock._websocket._receiveData(new Uint8Array(failureData)); - expect(spy).to.have.been.calledOnce; - expect(spy.args[0][0].detail.status).to.equal(1); - expect(spy.args[0][0].detail.reason).to.equal('such failure'); - }); - - it('should not include reason when length is zero in securityfailure event', function () { - const spy = sinon.spy(); - client.addEventListener("securityfailure", spy); - const failureData = [0, 0, 0, 1, 0, 0, 0, 0]; - client._sock._websocket._receiveData(new Uint8Array(failureData)); - expect(spy).to.have.been.calledOnce; - expect(spy.args[0][0].detail.status).to.equal(1); - expect('reason' in spy.args[0][0].detail).to.be.false; - }); - }); - - describe('ClientInitialisation', function () { - it('should transition to the ServerInitialisation state', function () { - const client = makeRFB(); - client._rfbConnectionState = 'connecting'; - client._rfbInitState = 'SecurityResult'; - client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 0])); - expect(client._rfbInitState).to.equal('ServerInitialisation'); - }); - - it('should send 1 if we are in shared mode', function () { - const client = makeRFB('wss://host:8675', { shared: true }); - client._rfbConnectionState = 'connecting'; - client._rfbInitState = 'SecurityResult'; - client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 0])); - expect(client._sock).to.have.sent(new Uint8Array([1])); - }); - - it('should send 0 if we are not in shared mode', function () { - const client = makeRFB('wss://host:8675', { shared: false }); - client._rfbConnectionState = 'connecting'; - client._rfbInitState = 'SecurityResult'; - client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 0])); - expect(client._sock).to.have.sent(new Uint8Array([0])); - }); - }); - - describe('ServerInitialisation', function () { - beforeEach(function () { - client._rfbInitState = 'ServerInitialisation'; - }); - - function sendServerInit(opts, client) { - const fullOpts = { width: 10, height: 12, bpp: 24, depth: 24, bigEndian: 0, - trueColor: 1, redMax: 255, greenMax: 255, blueMax: 255, - redShift: 16, greenShift: 8, blueShift: 0, name: 'a name' }; - for (let opt in opts) { - fullOpts[opt] = opts[opt]; - } - const data = []; - - push16(data, fullOpts.width); - push16(data, fullOpts.height); - - data.push(fullOpts.bpp); - data.push(fullOpts.depth); - data.push(fullOpts.bigEndian); - data.push(fullOpts.trueColor); - - push16(data, fullOpts.redMax); - push16(data, fullOpts.greenMax); - push16(data, fullOpts.blueMax); - push8(data, fullOpts.redShift); - push8(data, fullOpts.greenShift); - push8(data, fullOpts.blueShift); - - // padding - push8(data, 0); - push8(data, 0); - push8(data, 0); - - client._sock._websocket._receiveData(new Uint8Array(data)); - - const nameData = []; - let nameLen = []; - pushString(nameData, fullOpts.name); - push32(nameLen, nameData.length); - - client._sock._websocket._receiveData(new Uint8Array(nameLen)); - client._sock._websocket._receiveData(new Uint8Array(nameData)); - } - - it('should set the framebuffer width and height', function () { - sendServerInit({ width: 32, height: 84 }, client); - expect(client._fbWidth).to.equal(32); - expect(client._fbHeight).to.equal(84); - }); - - // NB(sross): we just warn, not fail, for endian-ness and shifts, so we don't test them - - it('should set the framebuffer name and call the callback', function () { - const spy = sinon.spy(); - client.addEventListener("desktopname", spy); - sendServerInit({ name: 'som€ nam€' }, client); - - expect(client._fbName).to.equal('som€ nam€'); - expect(spy).to.have.been.calledOnce; - expect(spy.args[0][0].detail.name).to.equal('som€ nam€'); - }); - - it('should handle the extended init message of the tight encoding', function () { - // NB(sross): we don't actually do anything with it, so just test that we can - // read it w/o throwing an error - client._rfbTightVNC = true; - sendServerInit({}, client); - - const tightData = []; - push16(tightData, 1); - push16(tightData, 2); - push16(tightData, 3); - push16(tightData, 0); - for (let i = 0; i < 16 + 32 + 48; i++) { - tightData.push(i); - } - client._sock._websocket._receiveData(new Uint8Array(tightData)); - - expect(client._rfbConnectionState).to.equal('connected'); - }); - - it('should resize the display', function () { - sinon.spy(client._display, 'resize'); - sendServerInit({ width: 27, height: 32 }, client); - - expect(client._display.resize).to.have.been.calledOnce; - expect(client._display.resize).to.have.been.calledWith(27, 32); - }); - - it('should grab the keyboard', function () { - sinon.spy(client._keyboard, 'grab'); - sendServerInit({}, client); - expect(client._keyboard.grab).to.have.been.calledOnce; - }); - - describe('Initial Update Request', function () { - beforeEach(function () { - sinon.spy(RFB.messages, "pixelFormat"); - sinon.spy(RFB.messages, "clientEncodings"); - sinon.spy(RFB.messages, "fbUpdateRequest"); - }); - - afterEach(function () { - RFB.messages.pixelFormat.restore(); - RFB.messages.clientEncodings.restore(); - RFB.messages.fbUpdateRequest.restore(); - }); - - // TODO(directxman12): test the various options in this configuration matrix - it('should reply with the pixel format, client encodings, and initial update request', function () { - sendServerInit({ width: 27, height: 32 }, client); - - expect(RFB.messages.pixelFormat).to.have.been.calledOnce; - expect(RFB.messages.pixelFormat).to.have.been.calledWith(client._sock, 24, true); - expect(RFB.messages.pixelFormat).to.have.been.calledBefore(RFB.messages.clientEncodings); - expect(RFB.messages.clientEncodings).to.have.been.calledOnce; - expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.encodingTight); - RFB.messages.clientEncodings.getCall(0).args[1].forEach((enc) => { - expect(enc).to.be.a('number'); - expect(Number.isInteger(enc)).to.be.true; - }); - expect(RFB.messages.clientEncodings).to.have.been.calledBefore(RFB.messages.fbUpdateRequest); - expect(RFB.messages.fbUpdateRequest).to.have.been.calledOnce; - expect(RFB.messages.fbUpdateRequest).to.have.been.calledWith(client._sock, false, 0, 0, 27, 32); - }); - - it('should reply with restricted settings for Intel AMT servers', function () { - sendServerInit({ width: 27, height: 32, name: "Intel(r) AMT KVM"}, client); - - expect(RFB.messages.pixelFormat).to.have.been.calledOnce; - expect(RFB.messages.pixelFormat).to.have.been.calledWith(client._sock, 8, true); - expect(RFB.messages.pixelFormat).to.have.been.calledBefore(RFB.messages.clientEncodings); - expect(RFB.messages.clientEncodings).to.have.been.calledOnce; - expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.not.include(encodings.encodingTight); - expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.not.include(encodings.encodingHextile); - expect(RFB.messages.clientEncodings).to.have.been.calledBefore(RFB.messages.fbUpdateRequest); - expect(RFB.messages.fbUpdateRequest).to.have.been.calledOnce; - expect(RFB.messages.fbUpdateRequest).to.have.been.calledWith(client._sock, false, 0, 0, 27, 32); - }); - }); - - it('should send the "connect" event', function () { - let spy = sinon.spy(); - client.addEventListener('connect', spy); - sendServerInit({}, client); - expect(spy).to.have.been.calledOnce; - }); - }); - }); - - describe('Protocol Message Processing After Completing Initialization', function () { - let client; - - beforeEach(function () { - client = makeRFB(); - client._fbName = 'some device'; - client._fbWidth = 640; - client._fbHeight = 20; - }); - - describe('Framebuffer Update Handling', function () { - function sendFbuMsg(rectInfo, rectData, client, rectCnt) { - let data = []; - - if (!rectCnt || rectCnt > -1) { - // header - data.push(0); // msg type - data.push(0); // padding - push16(data, rectCnt || rectData.length); - } - - for (let i = 0; i < rectData.length; i++) { - if (rectInfo[i]) { - push16(data, rectInfo[i].x); - push16(data, rectInfo[i].y); - push16(data, rectInfo[i].width); - push16(data, rectInfo[i].height); - push32(data, rectInfo[i].encoding); - } - data = data.concat(rectData[i]); - } - - client._sock._websocket._receiveData(new Uint8Array(data)); - } - - it('should send an update request if there is sufficient data', function () { - let esock = new Websock(); - let ews = new FakeWebSocket(); - ews._open(); - esock.attach(ews); - RFB.messages.fbUpdateRequest(esock, true, 0, 0, 640, 20); - let expected = ews._getSentData(); - - client._framebufferUpdate = () => true; - client._sock._websocket._receiveData(new Uint8Array([0])); - - expect(client._sock).to.have.sent(expected); - }); - - it('should not send an update request if we need more data', function () { - client._sock._websocket._receiveData(new Uint8Array([0])); - expect(client._sock).to.have.sent(new Uint8Array([])); - }); - - it('should resume receiving an update if we previously did not have enough data', function () { - let esock = new Websock(); - let ews = new FakeWebSocket(); - ews._open(); - esock.attach(ews); - RFB.messages.fbUpdateRequest(esock, true, 0, 0, 640, 20); - let expected = ews._getSentData(); - - // just enough to set FBU.rects - client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 3])); - expect(client._sock._websocket._getSentData()).to.have.length(0); - - client._framebufferUpdate = function () { this._sock.rQskipBytes(1); return true; }; // we magically have enough data - // 247 should *not* be used as the message type here - client._sock._websocket._receiveData(new Uint8Array([247])); - expect(client._sock).to.have.sent(expected); - }); - - it('should not send a request in continuous updates mode', function () { - client._enabledContinuousUpdates = true; - client._framebufferUpdate = () => true; - client._sock._websocket._receiveData(new Uint8Array([0])); - - expect(client._sock).to.have.sent(new Uint8Array([])); - }); - - it('should fail on an unsupported encoding', function () { - let callback = sinon.spy(); - client.addEventListener("disconnect", callback); - - const rectInfo = { x: 8, y: 11, width: 27, height: 32, encoding: 234 }; - sendFbuMsg([rectInfo], [[]], client); - - expect(callback).to.have.been.calledOnce; - expect(callback.args[0][0].detail.clean).to.be.false; - }); - - describe('Message Encoding Handlers', function () { - beforeEach(function () { - // a really small frame - client._fbWidth = 4; - client._fbHeight = 4; - client._fbDepth = 24; - client._display.resize(4, 4); - }); - - it('should handle the DesktopSize pseduo-encoding', function () { - sinon.spy(client._display, 'resize'); - sendFbuMsg([{ x: 0, y: 0, width: 20, height: 50, encoding: -223 }], [[]], client); - - expect(client._fbWidth).to.equal(20); - expect(client._fbHeight).to.equal(50); - - expect(client._display.resize).to.have.been.calledOnce; - expect(client._display.resize).to.have.been.calledWith(20, 50); - }); - - describe('the ExtendedDesktopSize pseudo-encoding handler', function () { - beforeEach(function () { - // a really small frame - client._fbWidth = 4; - client._fbHeight = 4; - client._display.resize(4, 4); - sinon.spy(client._display, 'resize'); - }); - - function makeScreenData(nrOfScreens) { - const data = []; - push8(data, nrOfScreens); // number-of-screens - push8(data, 0); // padding - push16(data, 0); // padding - for (let i=0; i<nrOfScreens; i += 1) { - push32(data, 0); // id - push16(data, 0); // x-position - push16(data, 0); // y-position - push16(data, 20); // width - push16(data, 50); // height - push32(data, 0); // flags - } - return data; - } - - it('should handle a resize requested by this client', function () { - const reasonForChange = 1; // requested by this client - const statusCode = 0; // No error - - sendFbuMsg([{ x: reasonForChange, y: statusCode, - width: 20, height: 50, encoding: -308 }], - makeScreenData(1), client); - - expect(client._fbWidth).to.equal(20); - expect(client._fbHeight).to.equal(50); - - expect(client._display.resize).to.have.been.calledOnce; - expect(client._display.resize).to.have.been.calledWith(20, 50); - }); - - it('should handle a resize requested by another client', function () { - const reasonForChange = 2; // requested by another client - const statusCode = 0; // No error - - sendFbuMsg([{ x: reasonForChange, y: statusCode, - width: 20, height: 50, encoding: -308 }], - makeScreenData(1), client); - - expect(client._fbWidth).to.equal(20); - expect(client._fbHeight).to.equal(50); - - expect(client._display.resize).to.have.been.calledOnce; - expect(client._display.resize).to.have.been.calledWith(20, 50); - }); - - it('should be able to recieve requests which contain data for multiple screens', function () { - const reasonForChange = 2; // requested by another client - const statusCode = 0; // No error - - sendFbuMsg([{ x: reasonForChange, y: statusCode, - width: 60, height: 50, encoding: -308 }], - makeScreenData(3), client); - - expect(client._fbWidth).to.equal(60); - expect(client._fbHeight).to.equal(50); - - expect(client._display.resize).to.have.been.calledOnce; - expect(client._display.resize).to.have.been.calledWith(60, 50); - }); - - it('should not handle a failed request', function () { - const reasonForChange = 1; // requested by this client - const statusCode = 1; // Resize is administratively prohibited - - sendFbuMsg([{ x: reasonForChange, y: statusCode, - width: 20, height: 50, encoding: -308 }], - makeScreenData(1), client); - - expect(client._fbWidth).to.equal(4); - expect(client._fbHeight).to.equal(4); - - expect(client._display.resize).to.not.have.been.called; - }); - }); - - describe('the Cursor pseudo-encoding handler', function () { - beforeEach(function () { - sinon.spy(client._cursor, 'change'); - }); - - it('should handle a standard cursor', function () { - const info = { x: 5, y: 7, - width: 4, height: 4, - encoding: -239}; - let rect = []; - let expected = []; - - for (let i = 0;i < info.width*info.height;i++) { - push32(rect, 0x11223300); - } - push32(rect, 0xa0a0a0a0); - - for (let i = 0;i < info.width*info.height/2;i++) { - push32(expected, 0x332211ff); - push32(expected, 0x33221100); - } - expected = new Uint8Array(expected); - - sendFbuMsg([info], [rect], client); - - expect(client._cursor.change).to.have.been.calledOnce; - expect(client._cursor.change).to.have.been.calledWith(expected, 5, 7, 4, 4); - }); - - it('should handle an empty cursor', function () { - const info = { x: 0, y: 0, - width: 0, height: 0, - encoding: -239}; - const rect = []; - - sendFbuMsg([info], [rect], client); - - expect(client._cursor.change).to.have.been.calledOnce; - expect(client._cursor.change).to.have.been.calledWith(new Uint8Array, 0, 0, 0, 0); - }); - - it('should handle a transparent cursor', function () { - const info = { x: 5, y: 7, - width: 4, height: 4, - encoding: -239}; - let rect = []; - let expected = []; - - for (let i = 0;i < info.width*info.height;i++) { - push32(rect, 0x11223300); - } - push32(rect, 0x00000000); - - for (let i = 0;i < info.width*info.height;i++) { - push32(expected, 0x33221100); - } - expected = new Uint8Array(expected); - - sendFbuMsg([info], [rect], client); - - expect(client._cursor.change).to.have.been.calledOnce; - expect(client._cursor.change).to.have.been.calledWith(expected, 5, 7, 4, 4); - }); - - describe('dot for empty cursor', function () { - beforeEach(function () { - client.showDotCursor = true; - // Was called when we enabled dot cursor - client._cursor.change.resetHistory(); - }); - - it('should show a standard cursor', function () { - const info = { x: 5, y: 7, - width: 4, height: 4, - encoding: -239}; - let rect = []; - let expected = []; - - for (let i = 0;i < info.width*info.height;i++) { - push32(rect, 0x11223300); - } - push32(rect, 0xa0a0a0a0); - - for (let i = 0;i < info.width*info.height/2;i++) { - push32(expected, 0x332211ff); - push32(expected, 0x33221100); - } - expected = new Uint8Array(expected); - - sendFbuMsg([info], [rect], client); - - expect(client._cursor.change).to.have.been.calledOnce; - expect(client._cursor.change).to.have.been.calledWith(expected, 5, 7, 4, 4); - }); - - it('should handle an empty cursor', function () { - const info = { x: 0, y: 0, - width: 0, height: 0, - encoding: -239}; - const rect = []; - const dot = RFB.cursors.dot; - - sendFbuMsg([info], [rect], client); - - expect(client._cursor.change).to.have.been.calledOnce; - expect(client._cursor.change).to.have.been.calledWith(dot.rgbaPixels, - dot.hotx, - dot.hoty, - dot.w, - dot.h); - }); - - it('should handle a transparent cursor', function () { - const info = { x: 5, y: 7, - width: 4, height: 4, - encoding: -239}; - let rect = []; - const dot = RFB.cursors.dot; - - for (let i = 0;i < info.width*info.height;i++) { - push32(rect, 0x11223300); - } - push32(rect, 0x00000000); - - sendFbuMsg([info], [rect], client); - - expect(client._cursor.change).to.have.been.calledOnce; - expect(client._cursor.change).to.have.been.calledWith(dot.rgbaPixels, - dot.hotx, - dot.hoty, - dot.w, - dot.h); - }); - }); - }); - - describe('the VMware Cursor pseudo-encoding handler', function () { - beforeEach(function () { - sinon.spy(client._cursor, 'change'); - }); - afterEach(function () { - client._cursor.change.resetHistory(); - }); - - it('should handle the VMware cursor pseudo-encoding', function () { - let data = [0x00, 0x00, 0xff, 0, - 0x00, 0xff, 0x00, 0, - 0x00, 0xff, 0x00, 0, - 0x00, 0x00, 0xff, 0]; - let rect = []; - push8(rect, 0); - push8(rect, 0); - - //AND-mask - for (let i = 0; i < data.length; i++) { - push8(rect, data[i]); - } - //XOR-mask - for (let i = 0; i < data.length; i++) { - push8(rect, data[i]); - } - - sendFbuMsg([{ x: 0, y: 0, width: 2, height: 2, - encoding: 0x574d5664}], - [rect], client); - expect(client._FBU.rects).to.equal(0); - }); - - it('should handle insufficient cursor pixel data', function () { - - // Specified 14x23 pixels for the cursor, - // but only send 2x2 pixels worth of data - let w = 14; - let h = 23; - let data = [0x00, 0x00, 0xff, 0, - 0x00, 0xff, 0x00, 0]; - let rect = []; - - push8(rect, 0); - push8(rect, 0); - - //AND-mask - for (let i = 0; i < data.length; i++) { - push8(rect, data[i]); - } - //XOR-mask - for (let i = 0; i < data.length; i++) { - push8(rect, data[i]); - } - - sendFbuMsg([{ x: 0, y: 0, width: w, height: h, - encoding: 0x574d5664}], - [rect], client); - - // expect one FBU to remain unhandled - expect(client._FBU.rects).to.equal(1); - }); - - it('should update the cursor when type is classic', function () { - let andMask = - [0xff, 0xff, 0xff, 0xff, //Transparent - 0xff, 0xff, 0xff, 0xff, //Transparent - 0x00, 0x00, 0x00, 0x00, //Opaque - 0xff, 0xff, 0xff, 0xff]; //Inverted - - let xorMask = - [0x00, 0x00, 0x00, 0x00, //Transparent - 0x00, 0x00, 0x00, 0x00, //Transparent - 0x11, 0x22, 0x33, 0x44, //Opaque - 0xff, 0xff, 0xff, 0x44]; //Inverted - - let rect = []; - push8(rect, 0); //cursor_type - push8(rect, 0); //padding - let hotx = 0; - let hoty = 0; - let w = 2; - let h = 2; - - //AND-mask - for (let i = 0; i < andMask.length; i++) { - push8(rect, andMask[i]); - } - //XOR-mask - for (let i = 0; i < xorMask.length; i++) { - push8(rect, xorMask[i]); - } - - let expectedRgba = [0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x33, 0x22, 0x11, 0xff, - 0x00, 0x00, 0x00, 0xff]; - - sendFbuMsg([{ x: hotx, y: hoty, - width: w, height: h, - encoding: 0x574d5664}], - [rect], client); - - expect(client._cursor.change) - .to.have.been.calledOnce; - expect(client._cursor.change) - .to.have.been.calledWith(expectedRgba, - hotx, hoty, - w, h); - }); - - it('should update the cursor when type is alpha', function () { - let data = [0xee, 0x55, 0xff, 0x00, // rgba - 0x00, 0xff, 0x00, 0xff, - 0x00, 0xff, 0x00, 0x22, - 0x00, 0xff, 0x00, 0x22, - 0x00, 0xff, 0x00, 0x22, - 0x00, 0x00, 0xff, 0xee]; - let rect = []; - push8(rect, 1); //cursor_type - push8(rect, 0); //padding - let hotx = 0; - let hoty = 0; - let w = 3; - let h = 2; - - for (let i = 0; i < data.length; i++) { - push8(rect, data[i]); - } - - let expectedRgba = [0xee, 0x55, 0xff, 0x00, - 0x00, 0xff, 0x00, 0xff, - 0x00, 0xff, 0x00, 0x22, - 0x00, 0xff, 0x00, 0x22, - 0x00, 0xff, 0x00, 0x22, - 0x00, 0x00, 0xff, 0xee]; - - sendFbuMsg([{ x: hotx, y: hoty, - width: w, height: h, - encoding: 0x574d5664}], - [rect], client); - - expect(client._cursor.change) - .to.have.been.calledOnce; - expect(client._cursor.change) - .to.have.been.calledWith(expectedRgba, - hotx, hoty, - w, h); - }); - - it('should not update cursor when incorrect cursor type given', function () { - let rect = []; - push8(rect, 3); // invalid cursor type - push8(rect, 0); // padding - - client._cursor.change.resetHistory(); - sendFbuMsg([{ x: 0, y: 0, width: 2, height: 2, - encoding: 0x574d5664}], - [rect], client); - - expect(client._cursor.change) - .to.not.have.been.called; - }); - }); - - it('should handle the last_rect pseudo-encoding', function () { - sendFbuMsg([{ x: 0, y: 0, width: 0, height: 0, encoding: -224}], [[]], client, 100); - // Send a bell message and make sure it is parsed - let spy = sinon.spy(); - client.addEventListener("bell", spy); - client._sock._websocket._receiveData(new Uint8Array([0x02])); - expect(spy).to.have.been.calledOnce; - }); - - it('should handle the DesktopName pseudo-encoding', function () { - let data = []; - push32(data, 13); - pushString(data, "som€ nam€"); - - const spy = sinon.spy(); - client.addEventListener("desktopname", spy); - - sendFbuMsg([{ x: 0, y: 0, width: 0, height: 0, encoding: -307 }], [data], client); - - expect(client._fbName).to.equal('som€ nam€'); - expect(spy).to.have.been.calledOnce; - expect(spy.args[0][0].detail.name).to.equal('som€ nam€'); - }); - }); - - describe('Caps Lock and Num Lock remote fixup', function () { - function sendLedStateUpdate(state) { - let data = []; - push8(data, state); - sendFbuMsg([{ x: 0, y: 0, width: 0, height: 0, encoding: -261 }], [data], client); - } - - let client; - beforeEach(function () { - client = makeRFB(); - sinon.stub(client, 'sendKey'); - }); - - it('should toggle caps lock if remote caps lock is on and local is off', function () { - sendLedStateUpdate(0b100); - client._handleKeyEvent(0x61, 'KeyA', true, null, false); - - expect(client.sendKey).to.have.been.calledThrice; - expect(client.sendKey.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true); - expect(client.sendKey.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false); - expect(client.sendKey.thirdCall).to.have.been.calledWith(0x61, "KeyA", true); - }); - - it('should toggle caps lock if remote caps lock is off and local is on', function () { - sendLedStateUpdate(0b011); - client._handleKeyEvent(0x41, 'KeyA', true, null, true); - - expect(client.sendKey).to.have.been.calledThrice; - expect(client.sendKey.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true); - expect(client.sendKey.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false); - expect(client.sendKey.thirdCall).to.have.been.calledWith(0x41, "KeyA", true); - }); - - it('should not toggle caps lock if remote caps lock is on and local is on', function () { - sendLedStateUpdate(0b100); - client._handleKeyEvent(0x41, 'KeyA', true, null, true); - - expect(client.sendKey).to.have.been.calledOnce; - expect(client.sendKey.firstCall).to.have.been.calledWith(0x41, "KeyA", true); - }); - - it('should not toggle caps lock if remote caps lock is off and local is off', function () { - sendLedStateUpdate(0b011); - client._handleKeyEvent(0x61, 'KeyA', true, null, false); - - expect(client.sendKey).to.have.been.calledOnce; - expect(client.sendKey.firstCall).to.have.been.calledWith(0x61, "KeyA", true); - }); - - it('should not toggle caps lock if the key is caps lock', function () { - sendLedStateUpdate(0b011); - client._handleKeyEvent(0xFFE5, 'CapsLock', true, null, true); - - expect(client.sendKey).to.have.been.calledOnce; - expect(client.sendKey.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true); - }); - - it('should toggle caps lock only once', function () { - sendLedStateUpdate(0b100); - client._handleKeyEvent(0x61, 'KeyA', true, null, false); - client._handleKeyEvent(0x61, 'KeyA', true, null, false); - - expect(client.sendKey).to.have.callCount(4); - expect(client.sendKey.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true); - expect(client.sendKey.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false); - expect(client.sendKey.thirdCall).to.have.been.calledWith(0x61, "KeyA", true); - expect(client.sendKey.lastCall).to.have.been.calledWith(0x61, "KeyA", true); - }); - - it('should retain remote caps lock state on capslock key up', function () { - sendLedStateUpdate(0b100); - client._handleKeyEvent(0xFFE5, 'CapsLock', false, null, true); - - expect(client.sendKey).to.have.been.calledOnce; - expect(client.sendKey.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", false); - expect(client._remoteCapsLock).to.equal(true); - }); - - it('should toggle num lock if remote num lock is on and local is off', function () { - sendLedStateUpdate(0b010); - client._handleKeyEvent(0xFF9C, 'NumPad1', true, false, null); - - expect(client.sendKey).to.have.been.calledThrice; - expect(client.sendKey.firstCall).to.have.been.calledWith(0xFF7F, "NumLock", true); - expect(client.sendKey.secondCall).to.have.been.calledWith(0xFF7F, "NumLock", false); - expect(client.sendKey.thirdCall).to.have.been.calledWith(0xFF9C, "NumPad1", true); - }); - - it('should toggle num lock if remote num lock is off and local is on', function () { - sendLedStateUpdate(0b101); - client._handleKeyEvent(0xFFB1, 'NumPad1', true, true, null); - - expect(client.sendKey).to.have.been.calledThrice; - expect(client.sendKey.firstCall).to.have.been.calledWith(0xFF7F, "NumLock", true); - expect(client.sendKey.secondCall).to.have.been.calledWith(0xFF7F, "NumLock", false); - expect(client.sendKey.thirdCall).to.have.been.calledWith(0xFFB1, "NumPad1", true); - }); - - it('should not toggle num lock if remote num lock is on and local is on', function () { - sendLedStateUpdate(0b010); - client._handleKeyEvent(0xFFB1, 'NumPad1', true, true, null); - - expect(client.sendKey).to.have.been.calledOnce; - expect(client.sendKey.firstCall).to.have.been.calledWith(0xFFB1, "NumPad1", true); - }); - - it('should not toggle num lock if remote num lock is off and local is off', function () { - sendLedStateUpdate(0b101); - client._handleKeyEvent(0xFF9C, 'NumPad1', true, false, null); - - expect(client.sendKey).to.have.been.calledOnce; - expect(client.sendKey.firstCall).to.have.been.calledWith(0xFF9C, "NumPad1", true); - }); - - it('should not toggle num lock if the key is num lock', function () { - sendLedStateUpdate(0b101); - client._handleKeyEvent(0xFF7F, 'NumLock', true, true, null); - - expect(client.sendKey).to.have.been.calledOnce; - expect(client.sendKey.firstCall).to.have.been.calledWith(0xFF7F, "NumLock", true); - }); - - it('should not toggle num lock if local state is unknown', function () { - sendLedStateUpdate(0b010); - client._handleKeyEvent(0xFFB1, 'NumPad1', true, null, null); - - expect(client.sendKey).to.have.been.calledOnce; - expect(client.sendKey.firstCall).to.have.been.calledWith(0xFFB1, "NumPad1", true); - }); - - it('should toggle num lock only once', function () { - sendLedStateUpdate(0b010); - client._handleKeyEvent(0xFF9C, 'NumPad1', true, false, null); - client._handleKeyEvent(0xFF9C, 'NumPad1', true, false, null); - - expect(client.sendKey).to.have.callCount(4); - expect(client.sendKey.firstCall).to.have.been.calledWith(0xFF7F, "NumLock", true); - expect(client.sendKey.secondCall).to.have.been.calledWith(0xFF7F, "NumLock", false); - expect(client.sendKey.thirdCall).to.have.been.calledWith(0xFF9C, "NumPad1", true); - expect(client.sendKey.lastCall).to.have.been.calledWith(0xFF9C, "NumPad1", true); - }); - }); - }); - - describe('XVP Message Handling', function () { - it('should set the XVP version and fire the callback with the version on XVP_INIT', function () { - const spy = sinon.spy(); - client.addEventListener("capabilities", spy); - client._sock._websocket._receiveData(new Uint8Array([250, 0, 10, 1])); - expect(client._rfbXvpVer).to.equal(10); - expect(spy).to.have.been.calledOnce; - expect(spy.args[0][0].detail.capabilities.power).to.be.true; - expect(client.capabilities.power).to.be.true; - }); - - it('should fail on unknown XVP message types', function () { - let callback = sinon.spy(); - client.addEventListener("disconnect", callback); - - client._sock._websocket._receiveData(new Uint8Array([250, 0, 10, 237])); - - expect(callback).to.have.been.calledOnce; - expect(callback.args[0][0].detail.clean).to.be.false; - }); - }); - - describe('Normal Clipboard Handling Receive', function () { - it('should fire the clipboard callback with the retrieved text on ServerCutText', function () { - const expectedStr = 'cheese!'; - const data = [3, 0, 0, 0]; - push32(data, expectedStr.length); - for (let i = 0; i < expectedStr.length; i++) { data.push(expectedStr.charCodeAt(i)); } - const spy = sinon.spy(); - client.addEventListener("clipboard", spy); - - client._sock._websocket._receiveData(new Uint8Array(data)); - expect(spy).to.have.been.calledOnce; - expect(spy.args[0][0].detail.text).to.equal(expectedStr); - }); - }); - - describe('Extended clipboard Handling', function () { - - describe('Extended clipboard initialization', function () { - beforeEach(function () { - sinon.spy(RFB.messages, 'extendedClipboardCaps'); - }); - - afterEach(function () { - RFB.messages.extendedClipboardCaps.restore(); - }); - - it('should update capabilities when receiving a Caps message', function () { - let data = [3, 0, 0, 0]; - const flags = [0x1F, 0x00, 0x00, 0x03]; - let fileSizes = [0x00, 0x00, 0x00, 0x1E, - 0x00, 0x00, 0x00, 0x3C]; - - push32(data, toUnsigned32bit(-12)); - data = data.concat(flags); - data = data.concat(fileSizes); - client._sock._websocket._receiveData(new Uint8Array(data)); - - // Check that we give an response caps when we receive one - expect(RFB.messages.extendedClipboardCaps).to.have.been.calledOnce; - - // FIXME: Can we avoid checking internal variables? - expect(client._clipboardServerCapabilitiesFormats[0]).to.not.equal(true); - expect(client._clipboardServerCapabilitiesFormats[1]).to.equal(true); - expect(client._clipboardServerCapabilitiesFormats[2]).to.equal(true); - expect(client._clipboardServerCapabilitiesActions[(1 << 24)]).to.equal(true); - }); - - - }); - - describe('Extended Clipboard Handling Receive', function () { - - beforeEach(function () { - // Send our capabilities - let data = [3, 0, 0, 0]; - const flags = [0x1F, 0x00, 0x00, 0x01]; - let fileSizes = [0x00, 0x00, 0x00, 0x1E]; - - push32(data, toUnsigned32bit(-8)); - data = data.concat(flags); - data = data.concat(fileSizes); - client._sock._websocket._receiveData(new Uint8Array(data)); - }); - - describe('Handle Provide', function () { - it('should update clipboard with correct Unicode data from a Provide message', function () { - let expectedData = "Aå漢字!"; - let data = [3, 0, 0, 0]; - const flags = [0x10, 0x00, 0x00, 0x01]; - - let text = encodeUTF8("Aå漢字!"); - let deflatedText = deflateWithSize(text); - - // How much data we are sending. - push32(data, toUnsigned32bit(-(4 + deflatedText.length))); - - data = data.concat(flags); - data = data.concat(Array.from(deflatedText)); - - const spy = sinon.spy(); - client.addEventListener("clipboard", spy); - - client._sock._websocket._receiveData(new Uint8Array(data)); - expect(spy).to.have.been.calledOnce; - expect(spy.args[0][0].detail.text).to.equal(expectedData); - client.removeEventListener("clipboard", spy); - }); - - it('should update clipboard with correct escape characters from a Provide message ', function () { - let expectedData = "Oh\nmy\n!"; - let data = [3, 0, 0, 0]; - const flags = [0x10, 0x00, 0x00, 0x01]; - - let text = encodeUTF8("Oh\r\nmy\r\n!\0"); - - let deflatedText = deflateWithSize(text); - - // How much data we are sending. - push32(data, toUnsigned32bit(-(4 + deflatedText.length))); - - data = data.concat(flags); - data = data.concat(Array.from(deflatedText)); - - const spy = sinon.spy(); - client.addEventListener("clipboard", spy); - - client._sock._websocket._receiveData(new Uint8Array(data)); - expect(spy).to.have.been.calledOnce; - expect(spy.args[0][0].detail.text).to.equal(expectedData); - client.removeEventListener("clipboard", spy); - }); - - it('should be able to handle large Provide messages', function () { - let expectedData = "hello".repeat(100000); - let data = [3, 0, 0, 0]; - const flags = [0x10, 0x00, 0x00, 0x01]; - - let text = encodeUTF8(expectedData + "\0"); - - let deflatedText = deflateWithSize(text); - - // How much data we are sending. - push32(data, toUnsigned32bit(-(4 + deflatedText.length))); - - data = data.concat(flags); - data = data.concat(Array.from(deflatedText)); - - const spy = sinon.spy(); - client.addEventListener("clipboard", spy); - - client._sock._websocket._receiveData(new Uint8Array(data)); - expect(spy).to.have.been.calledOnce; - expect(spy.args[0][0].detail.text).to.equal(expectedData); - client.removeEventListener("clipboard", spy); - }); - - }); - - describe('Handle Notify', function () { - beforeEach(function () { - sinon.spy(RFB.messages, 'extendedClipboardRequest'); - }); - - afterEach(function () { - RFB.messages.extendedClipboardRequest.restore(); - }); - - it('should make a request with supported formats when receiving a notify message', function () { - let data = [3, 0, 0, 0]; - const flags = [0x08, 0x00, 0x00, 0x07]; - push32(data, toUnsigned32bit(-4)); - data = data.concat(flags); - let expectedData = [0x01]; - - client._sock._websocket._receiveData(new Uint8Array(data)); - - expect(RFB.messages.extendedClipboardRequest).to.have.been.calledOnce; - expect(RFB.messages.extendedClipboardRequest).to.have.been.calledWith(client._sock, expectedData); - }); - }); - - describe('Handle Peek', function () { - beforeEach(function () { - sinon.spy(RFB.messages, 'extendedClipboardNotify'); - }); - - afterEach(function () { - RFB.messages.extendedClipboardNotify.restore(); - }); - - it('should send an empty Notify when receiving a Peek and no excisting clipboard data', function () { - let data = [3, 0, 0, 0]; - const flags = [0x04, 0x00, 0x00, 0x00]; - push32(data, toUnsigned32bit(-4)); - data = data.concat(flags); - let expectedData = []; - - client._sock._websocket._receiveData(new Uint8Array(data)); - - expect(RFB.messages.extendedClipboardNotify).to.have.been.calledOnce; - expect(RFB.messages.extendedClipboardNotify).to.have.been.calledWith(client._sock, expectedData); - }); - - it('should send a Notify message with supported formats when receiving a Peek', function () { - let data = [3, 0, 0, 0]; - const flags = [0x04, 0x00, 0x00, 0x00]; - push32(data, toUnsigned32bit(-4)); - data = data.concat(flags); - let expectedData = [0x01]; - - // Needed to have clipboard data to read. - // This will trigger a call to Notify, reset history - client.clipboardPasteFrom("HejHej"); - RFB.messages.extendedClipboardNotify.resetHistory(); - - client._sock._websocket._receiveData(new Uint8Array(data)); - - expect(RFB.messages.extendedClipboardNotify).to.have.been.calledOnce; - expect(RFB.messages.extendedClipboardNotify).to.have.been.calledWith(client._sock, expectedData); - }); - }); - - describe('Handle Request', function () { - beforeEach(function () { - sinon.spy(RFB.messages, 'extendedClipboardProvide'); - }); - - afterEach(function () { - RFB.messages.extendedClipboardProvide.restore(); - }); - - it('should send a Provide message with supported formats when receiving a Request', function () { - let data = [3, 0, 0, 0]; - const flags = [0x02, 0x00, 0x00, 0x01]; - push32(data, toUnsigned32bit(-4)); - data = data.concat(flags); - let expectedData = [0x01]; - - client.clipboardPasteFrom("HejHej"); - expect(RFB.messages.extendedClipboardProvide).to.not.have.been.called; - - client._sock._websocket._receiveData(new Uint8Array(data)); - - expect(RFB.messages.extendedClipboardProvide).to.have.been.calledOnce; - expect(RFB.messages.extendedClipboardProvide).to.have.been.calledWith(client._sock, expectedData, ["HejHej"]); - }); - }); - }); - - }); - - it('should fire the bell callback on Bell', function () { - const spy = sinon.spy(); - client.addEventListener("bell", spy); - client._sock._websocket._receiveData(new Uint8Array([2])); - expect(spy).to.have.been.calledOnce; - }); - - it('should respond correctly to ServerFence', function () { - const payload = "foo\x00ab9"; - - let esock = new Websock(); - let ews = new FakeWebSocket(); - ews._open(); - esock.attach(ews); - - // ClientFence and ServerFence are identical in structure - RFB.messages.clientFence(esock, (1<<0) | (1<<1), payload); - let expected = ews._getSentData(); - RFB.messages.clientFence(esock, 0xffffffff, payload); - let incoming = ews._getSentData(); - - client._sock._websocket._receiveData(incoming); - - expect(client._sock).to.have.sent(expected); - - RFB.messages.clientFence(esock, (1<<0), payload); - expected = ews._getSentData(); - RFB.messages.clientFence(esock, (1<<0) | (1<<31), payload); - incoming = ews._getSentData(); - - client._sock._websocket._receiveData(incoming); - - expect(client._sock).to.have.sent(expected); - }); - - it('should enable continuous updates on first EndOfContinousUpdates', function () { - let esock = new Websock(); - let ews = new FakeWebSocket(); - ews._open(); - esock.attach(ews); - RFB.messages.enableContinuousUpdates(esock, true, 0, 0, 640, 20); - let expected = ews._getSentData(); - - expect(client._enabledContinuousUpdates).to.be.false; - - client._sock._websocket._receiveData(new Uint8Array([150])); - - expect(client._enabledContinuousUpdates).to.be.true; - expect(client._sock).to.have.sent(expected); - }); - - it('should disable continuous updates on subsequent EndOfContinousUpdates', function () { - client._enabledContinuousUpdates = true; - client._supportsContinuousUpdates = true; - - client._sock._websocket._receiveData(new Uint8Array([150])); - - expect(client._enabledContinuousUpdates).to.be.false; - }); - - it('should update continuous updates on resize', function () { - let esock = new Websock(); - let ews = new FakeWebSocket(); - ews._open(); - esock.attach(ews); - RFB.messages.enableContinuousUpdates(esock, true, 0, 0, 90, 700); - let expected = ews._getSentData(); - - client._resize(450, 160); - - expect(client._sock).to.have.sent(new Uint8Array([])); - - client._enabledContinuousUpdates = true; - - client._resize(90, 700); - - expect(client._sock).to.have.sent(expected); - }); - - it('should fail on an unknown message type', function () { - let callback = sinon.spy(); - client.addEventListener("disconnect", callback); - - client._sock._websocket._receiveData(new Uint8Array([87])); - - expect(callback).to.have.been.calledOnce; - expect(callback.args[0][0].detail.clean).to.be.false; - }); - }); - - describe('Asynchronous Events', function () { - let client; - let pointerEvent; - let keyEvent; - let qemuKeyEvent; - - beforeEach(function () { - client = makeRFB(); - client._display.resize(100, 100); - - // We need to disable this as focusing the canvas will - // cause the browser to scoll to it, messing up our - // client coordinate calculations - client.focusOnClick = false; - - pointerEvent = sinon.spy(RFB.messages, 'pointerEvent'); - keyEvent = sinon.spy(RFB.messages, 'keyEvent'); - qemuKeyEvent = sinon.spy(RFB.messages, 'QEMUExtendedKeyEvent'); - }); - - afterEach(function () { - pointerEvent.restore(); - keyEvent.restore(); - qemuKeyEvent.restore(); - }); - - function elementToClient(x, y) { - let res = { x: 0, y: 0 }; - - let bounds = client._canvas.getBoundingClientRect(); - - /* - * If the canvas is on a fractional position we will calculate - * a fractional mouse position. But that gets truncated when we - * send the event, AND the same thing happens in RFB when it - * generates the PointerEvent message. To compensate for that - * fact we round the value upwards here. - */ - res.x = Math.ceil(bounds.left + x); - res.y = Math.ceil(bounds.top + y); - - return res; - } - - describe('Mouse Events', function () { - function sendMouseMoveEvent(x, y) { - let pos = elementToClient(x, y); - let ev; - - ev = new MouseEvent('mousemove', - { 'screenX': pos.x + window.screenX, - 'screenY': pos.y + window.screenY, - 'clientX': pos.x, - 'clientY': pos.y }); - client._canvas.dispatchEvent(ev); - } - - function sendMouseButtonEvent(x, y, down, button) { - let pos = elementToClient(x, y); - let ev; - - ev = new MouseEvent(down ? 'mousedown' : 'mouseup', - { 'screenX': pos.x + window.screenX, - 'screenY': pos.y + window.screenY, - 'clientX': pos.x, - 'clientY': pos.y, - 'button': button, - 'buttons': 1 << button }); - client._canvas.dispatchEvent(ev); - } - - it('should not send button messages in view-only mode', function () { - client._viewOnly = true; - sendMouseButtonEvent(10, 10, true, 0); - clock.tick(50); - expect(pointerEvent).to.not.have.been.called; - }); - - it('should not send movement messages in view-only mode', function () { - client._viewOnly = true; - sendMouseMoveEvent(10, 10); - clock.tick(50); - expect(pointerEvent).to.not.have.been.called; - }); - - it('should handle left mouse button', function () { - sendMouseButtonEvent(10, 10, true, 0); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 10, 10, 0x1); - pointerEvent.resetHistory(); - - sendMouseButtonEvent(10, 10, false, 0); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 10, 10, 0x0); - }); - - it('should handle middle mouse button', function () { - sendMouseButtonEvent(10, 10, true, 1); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 10, 10, 0x2); - pointerEvent.resetHistory(); - - sendMouseButtonEvent(10, 10, false, 1); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 10, 10, 0x0); - }); - - it('should handle right mouse button', function () { - sendMouseButtonEvent(10, 10, true, 2); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 10, 10, 0x4); - pointerEvent.resetHistory(); - - sendMouseButtonEvent(10, 10, false, 2); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 10, 10, 0x0); - }); - - it('should handle multiple mouse buttons', function () { - sendMouseButtonEvent(10, 10, true, 0); - sendMouseButtonEvent(10, 10, true, 2); - - expect(pointerEvent).to.have.been.calledTwice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 10, 10, 0x1); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 10, 10, 0x5); - - pointerEvent.resetHistory(); - - sendMouseButtonEvent(10, 10, false, 0); - sendMouseButtonEvent(10, 10, false, 2); - - expect(pointerEvent).to.have.been.calledTwice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 10, 10, 0x4); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 10, 10, 0x0); - }); - - it('should handle mouse movement', function () { - sendMouseMoveEvent(50, 70); - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 50, 70, 0x0); - }); - - it('should handle click and drag', function () { - sendMouseButtonEvent(10, 10, true, 0); - sendMouseMoveEvent(50, 70); - - expect(pointerEvent).to.have.been.calledTwice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 10, 10, 0x1); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 50, 70, 0x1); - - pointerEvent.resetHistory(); - - sendMouseButtonEvent(50, 70, false, 0); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 50, 70, 0x0); - }); - - describe('Event Aggregation', function () { - it('should send a single pointer event on mouse movement', function () { - sendMouseMoveEvent(50, 70); - clock.tick(100); - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 50, 70, 0x0); - }); - - it('should delay one move if two events are too close', function () { - sendMouseMoveEvent(18, 30); - sendMouseMoveEvent(20, 50); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 18, 30, 0x0); - pointerEvent.resetHistory(); - - clock.tick(100); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 20, 50, 0x0); - }); - - it('should only send first and last move of many close events', function () { - sendMouseMoveEvent(18, 30); - sendMouseMoveEvent(20, 50); - sendMouseMoveEvent(21, 55); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 18, 30, 0x0); - pointerEvent.resetHistory(); - - clock.tick(100); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 21, 55, 0x0); - }); - - // We selected the 17ms since that is ~60 FPS - it('should send move events every 17 ms', function () { - sendMouseMoveEvent(1, 10); // instant send - clock.tick(10); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 1, 10, 0x0); - pointerEvent.resetHistory(); - - sendMouseMoveEvent(2, 20); // delayed - clock.tick(10); // timeout send - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 2, 20, 0x0); - pointerEvent.resetHistory(); - - sendMouseMoveEvent(3, 30); // delayed - clock.tick(10); - sendMouseMoveEvent(4, 40); // delayed - clock.tick(10); // timeout send - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 4, 40, 0x0); - pointerEvent.resetHistory(); - - sendMouseMoveEvent(5, 50); // delayed - - expect(pointerEvent).to.not.have.been.called; - }); - - it('should send waiting move events before a button press', function () { - sendMouseMoveEvent(13, 9); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 13, 9, 0x0); - pointerEvent.resetHistory(); - - sendMouseMoveEvent(20, 70); - - expect(pointerEvent).to.not.have.been.called; - - sendMouseButtonEvent(20, 70, true, 0); - - expect(pointerEvent).to.have.been.calledTwice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 20, 70, 0x0); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 20, 70, 0x1); - }); - - it('should send move events with enough time apart normally', function () { - sendMouseMoveEvent(58, 60); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 58, 60, 0x0); - pointerEvent.resetHistory(); - - clock.tick(20); - - sendMouseMoveEvent(25, 60); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 25, 60, 0x0); - pointerEvent.resetHistory(); - }); - - it('should not send waiting move events if disconnected', function () { - sendMouseMoveEvent(88, 99); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 88, 99, 0x0); - pointerEvent.resetHistory(); - - sendMouseMoveEvent(66, 77); - client.disconnect(); - clock.tick(20); - - expect(pointerEvent).to.not.have.been.called; - }); - }); - - it.skip('should block click events', function () { - /* FIXME */ - }); - - it.skip('should block contextmenu events', function () { - /* FIXME */ - }); - }); - - describe('Wheel Events', function () { - function sendWheelEvent(x, y, dx, dy, mode=0) { - let pos = elementToClient(x, y); - let ev; - - ev = new WheelEvent('wheel', - { 'screenX': pos.x + window.screenX, - 'screenY': pos.y + window.screenY, - 'clientX': pos.x, - 'clientY': pos.y, - 'deltaX': dx, - 'deltaY': dy, - 'deltaMode': mode }); - client._canvas.dispatchEvent(ev); - } - - it('should handle wheel up event', function () { - sendWheelEvent(10, 10, 0, -50); - - expect(pointerEvent).to.have.been.calledTwice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 10, 10, 1<<3); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 10, 10, 0); - }); - - it('should handle wheel down event', function () { - sendWheelEvent(10, 10, 0, 50); - - expect(pointerEvent).to.have.been.calledTwice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 10, 10, 1<<4); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 10, 10, 0); - }); - - it('should handle wheel left event', function () { - sendWheelEvent(10, 10, -50, 0); - - expect(pointerEvent).to.have.been.calledTwice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 10, 10, 1<<5); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 10, 10, 0); - }); - - it('should handle wheel right event', function () { - sendWheelEvent(10, 10, 50, 0); - - expect(pointerEvent).to.have.been.calledTwice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 10, 10, 1<<6); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 10, 10, 0); - }); - - it('should ignore wheel when in view only', function () { - client._viewOnly = true; - - sendWheelEvent(10, 10, 50, 0); - - expect(pointerEvent).to.not.have.been.called; - }); - - it('should accumulate wheel events if small enough', function () { - sendWheelEvent(10, 10, 0, 20); - sendWheelEvent(10, 10, 0, 20); - - expect(pointerEvent).to.not.have.been.called; - - sendWheelEvent(10, 10, 0, 20); - - expect(pointerEvent).to.have.been.calledTwice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 10, 10, 1<<4); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 10, 10, 0); - }); - - it('should not accumulate large wheel events', function () { - sendWheelEvent(10, 10, 0, 400); - - expect(pointerEvent).to.have.been.calledTwice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 10, 10, 1<<4); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 10, 10, 0); - }); - - it('should handle line based wheel event', function () { - sendWheelEvent(10, 10, 0, 3, 1); - - expect(pointerEvent).to.have.been.calledTwice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 10, 10, 1<<4); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 10, 10, 0); - }); - - it('should handle page based wheel event', function () { - sendWheelEvent(10, 10, 0, 3, 2); - - expect(pointerEvent).to.have.been.calledTwice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 10, 10, 1<<4); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 10, 10, 0); - }); - }); - - describe('Keyboard Events', function () { - it('should send a key message on a key press', function () { - let esock = new Websock(); - let ews = new FakeWebSocket(); - ews._open(); - esock.attach(ews); - RFB.messages.keyEvent(esock, 0x41, 1); - let expected = ews._getSentData(); - - client._handleKeyEvent(0x41, 'KeyA', true); - - expect(client._sock).to.have.sent(expected); - }); - - it('should not send messages in view-only mode', function () { - client._viewOnly = true; - sinon.spy(client._sock, 'flush'); - client._handleKeyEvent('a', 'KeyA', true); - expect(client._sock.flush).to.not.have.been.called; - }); - }); - - describe('Gesture event handlers', function () { - function gestureStart(gestureType, x, y, - magnitudeX = 0, magnitudeY = 0) { - let pos = elementToClient(x, y); - let detail = {type: gestureType, clientX: pos.x, clientY: pos.y}; - - detail.magnitudeX = magnitudeX; - detail.magnitudeY = magnitudeY; - - let ev = new CustomEvent('gesturestart', { detail: detail }); - client._canvas.dispatchEvent(ev); - } - - function gestureMove(gestureType, x, y, - magnitudeX = 0, magnitudeY = 0) { - let pos = elementToClient(x, y); - let detail = {type: gestureType, clientX: pos.x, clientY: pos.y}; - - detail.magnitudeX = magnitudeX; - detail.magnitudeY = magnitudeY; - - let ev = new CustomEvent('gesturemove', { detail: detail }); - client._canvas.dispatchEvent(ev); - } - - function gestureEnd(gestureType, x, y) { - let pos = elementToClient(x, y); - let detail = {type: gestureType, clientX: pos.x, clientY: pos.y}; - let ev = new CustomEvent('gestureend', { detail: detail }); - client._canvas.dispatchEvent(ev); - } - - describe('Gesture onetap', function () { - it('should handle onetap events', function () { - let bmask = 0x1; - - gestureStart('onetap', 20, 40); - gestureEnd('onetap', 20, 40); - - expect(pointerEvent).to.have.been.calledThrice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 20, 40, bmask); - expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - }); - - it('should keep same position for multiple onetap events', function () { - let bmask = 0x1; - - gestureStart('onetap', 20, 40); - gestureEnd('onetap', 20, 40); - - expect(pointerEvent).to.have.been.calledThrice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 20, 40, bmask); - expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - - pointerEvent.resetHistory(); - - gestureStart('onetap', 20, 50); - gestureEnd('onetap', 20, 50); - - expect(pointerEvent).to.have.been.calledThrice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 20, 40, bmask); - expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - - pointerEvent.resetHistory(); - - gestureStart('onetap', 30, 50); - gestureEnd('onetap', 30, 50); - - expect(pointerEvent).to.have.been.calledThrice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 20, 40, bmask); - expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - }); - - it('should not keep same position for onetap events when too far apart', function () { - let bmask = 0x1; - - gestureStart('onetap', 20, 40); - gestureEnd('onetap', 20, 40); - - expect(pointerEvent).to.have.been.calledThrice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 20, 40, bmask); - expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - - pointerEvent.resetHistory(); - - gestureStart('onetap', 80, 95); - gestureEnd('onetap', 80, 95); - - expect(pointerEvent).to.have.been.calledThrice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 80, 95, 0x0); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 80, 95, bmask); - expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock, - 80, 95, 0x0); - }); - - it('should not keep same position for onetap events when enough time inbetween', function () { - let bmask = 0x1; - - gestureStart('onetap', 10, 20); - gestureEnd('onetap', 10, 20); - - expect(pointerEvent).to.have.been.calledThrice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 10, 20, 0x0); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 10, 20, bmask); - expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock, - 10, 20, 0x0); - - pointerEvent.resetHistory(); - this.clock.tick(1500); - - gestureStart('onetap', 15, 20); - gestureEnd('onetap', 15, 20); - - expect(pointerEvent).to.have.been.calledThrice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 15, 20, 0x0); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 15, 20, bmask); - expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock, - 15, 20, 0x0); - - pointerEvent.resetHistory(); - }); - }); - - describe('Gesture twotap', function () { - it('should handle gesture twotap events', function () { - let bmask = 0x4; - - gestureStart("twotap", 20, 40); - - expect(pointerEvent).to.have.been.calledThrice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 20, 40, bmask); - expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - }); - - it('should keep same position for multiple twotap events', function () { - let bmask = 0x4; - - for (let offset = 0;offset < 30;offset += 10) { - pointerEvent.resetHistory(); - - gestureStart('twotap', 20, 40 + offset); - gestureEnd('twotap', 20, 40 + offset); - - expect(pointerEvent).to.have.been.calledThrice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 20, 40, bmask); - expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - } - }); - }); - - describe('Gesture threetap', function () { - it('should handle gesture start for threetap events', function () { - let bmask = 0x2; - - gestureStart("threetap", 20, 40); - - expect(pointerEvent).to.have.been.calledThrice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 20, 40, bmask); - expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - }); - - it('should keep same position for multiple threetap events', function () { - let bmask = 0x2; - - for (let offset = 0;offset < 30;offset += 10) { - pointerEvent.resetHistory(); - - gestureStart('threetap', 20, 40 + offset); - gestureEnd('threetap', 20, 40 + offset); - - expect(pointerEvent).to.have.been.calledThrice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 20, 40, bmask); - expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - } - }); - }); - - describe('Gesture drag', function () { - it('should handle gesture drag events', function () { - let bmask = 0x1; - - gestureStart('drag', 20, 40); - - expect(pointerEvent).to.have.been.calledTwice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 20, 40, bmask); - - pointerEvent.resetHistory(); - - gestureMove('drag', 30, 50); - clock.tick(50); - - expect(pointerEvent).to.have.been.calledOnce; - expect(pointerEvent).to.have.been.calledWith(client._sock, - 30, 50, bmask); - - pointerEvent.resetHistory(); - - gestureEnd('drag', 30, 50); - - expect(pointerEvent).to.have.been.calledTwice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 30, 50, bmask); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 30, 50, 0x0); - }); - }); - - describe('Gesture long press', function () { - it('should handle long press events', function () { - let bmask = 0x4; - - gestureStart('longpress', 20, 40); - - expect(pointerEvent).to.have.been.calledTwice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 20, 40, bmask); - pointerEvent.resetHistory(); - - gestureMove('longpress', 40, 60); - clock.tick(50); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 40, 60, bmask); - - pointerEvent.resetHistory(); - - gestureEnd('longpress', 40, 60); - - expect(pointerEvent).to.have.been.calledTwice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 40, 60, bmask); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 40, 60, 0x0); - }); - }); - - describe('Gesture twodrag', function () { - it('should handle gesture twodrag up events', function () { - let bmask = 0x10; // Button mask for scroll down - - gestureStart('twodrag', 20, 40, 0, 0); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 20, 40, 0x0); - - pointerEvent.resetHistory(); - - gestureMove('twodrag', 20, 40, 0, -60); - - expect(pointerEvent).to.have.been.calledThrice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 20, 40, bmask); - expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - }); - - it('should handle gesture twodrag down events', function () { - let bmask = 0x8; // Button mask for scroll up - - gestureStart('twodrag', 20, 40, 0, 0); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 20, 40, 0x0); - - pointerEvent.resetHistory(); - - gestureMove('twodrag', 20, 40, 0, 60); - - expect(pointerEvent).to.have.been.calledThrice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 20, 40, bmask); - expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - }); - - it('should handle gesture twodrag right events', function () { - let bmask = 0x20; // Button mask for scroll right - - gestureStart('twodrag', 20, 40, 0, 0); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 20, 40, 0x0); - - pointerEvent.resetHistory(); - - gestureMove('twodrag', 20, 40, 60, 0); - - expect(pointerEvent).to.have.been.calledThrice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 20, 40, bmask); - expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - }); - - it('should handle gesture twodrag left events', function () { - let bmask = 0x40; // Button mask for scroll left - - gestureStart('twodrag', 20, 40, 0, 0); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 20, 40, 0x0); - - pointerEvent.resetHistory(); - - gestureMove('twodrag', 20, 40, -60, 0); - - expect(pointerEvent).to.have.been.calledThrice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 20, 40, bmask); - expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - }); - - it('should handle gesture twodrag diag events', function () { - let scrlUp = 0x8; // Button mask for scroll up - let scrlRight = 0x20; // Button mask for scroll right - - gestureStart('twodrag', 20, 40, 0, 0); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 20, 40, 0x0); - - pointerEvent.resetHistory(); - - gestureMove('twodrag', 20, 40, 60, 60); - - expect(pointerEvent).to.have.been.callCount(5); - expect(pointerEvent.getCall(0)).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - expect(pointerEvent.getCall(1)).to.have.been.calledWith(client._sock, - 20, 40, scrlUp); - expect(pointerEvent.getCall(2)).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - expect(pointerEvent.getCall(3)).to.have.been.calledWith(client._sock, - 20, 40, scrlRight); - expect(pointerEvent.getCall(4)).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - }); - - it('should handle multiple small gesture twodrag events', function () { - let bmask = 0x8; // Button mask for scroll up - - gestureStart('twodrag', 20, 40, 0, 0); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 20, 40, 0x0); - - pointerEvent.resetHistory(); - - gestureMove('twodrag', 20, 40, 0, 10); - clock.tick(50); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 20, 40, 0x0); - - pointerEvent.resetHistory(); - - gestureMove('twodrag', 20, 40, 0, 20); - clock.tick(50); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 20, 40, 0x0); - - pointerEvent.resetHistory(); - - gestureMove('twodrag', 20, 40, 0, 60); - - expect(pointerEvent).to.have.been.calledThrice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 20, 40, bmask); - expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - }); - - it('should handle large gesture twodrag events', function () { - let bmask = 0x8; // Button mask for scroll up - - gestureStart('twodrag', 30, 50, 0, 0); - - expect(pointerEvent). - to.have.been.calledOnceWith(client._sock, 30, 50, 0x0); - - pointerEvent.resetHistory(); - - gestureMove('twodrag', 30, 50, 0, 200); - - expect(pointerEvent).to.have.callCount(7); - expect(pointerEvent.getCall(0)).to.have.been.calledWith(client._sock, - 30, 50, 0x0); - expect(pointerEvent.getCall(1)).to.have.been.calledWith(client._sock, - 30, 50, bmask); - expect(pointerEvent.getCall(2)).to.have.been.calledWith(client._sock, - 30, 50, 0x0); - expect(pointerEvent.getCall(3)).to.have.been.calledWith(client._sock, - 30, 50, bmask); - expect(pointerEvent.getCall(4)).to.have.been.calledWith(client._sock, - 30, 50, 0x0); - expect(pointerEvent.getCall(5)).to.have.been.calledWith(client._sock, - 30, 50, bmask); - expect(pointerEvent.getCall(6)).to.have.been.calledWith(client._sock, - 30, 50, 0x0); - }); - }); - - describe('Gesture pinch', function () { - it('should handle gesture pinch in events', function () { - let keysym = KeyTable.XK_Control_L; - let bmask = 0x10; // Button mask for scroll down - - gestureStart('pinch', 20, 40, 90, 90); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 20, 40, 0x0); - expect(keyEvent).to.not.have.been.called; - - pointerEvent.resetHistory(); - - gestureMove('pinch', 20, 40, 30, 30); - - expect(pointerEvent).to.have.been.calledThrice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 20, 40, bmask); - expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - - expect(keyEvent).to.have.been.calledTwice; - expect(keyEvent.firstCall).to.have.been.calledWith(client._sock, - keysym, 1); - expect(keyEvent.secondCall).to.have.been.calledWith(client._sock, - keysym, 0); - - expect(keyEvent.firstCall).to.have.been.calledBefore(pointerEvent.secondCall); - expect(keyEvent.lastCall).to.have.been.calledAfter(pointerEvent.lastCall); - - pointerEvent.resetHistory(); - keyEvent.resetHistory(); - - gestureEnd('pinch', 20, 40); - - expect(pointerEvent).to.not.have.been.called; - expect(keyEvent).to.not.have.been.called; - }); - - it('should handle gesture pinch out events', function () { - let keysym = KeyTable.XK_Control_L; - let bmask = 0x8; // Button mask for scroll up - - gestureStart('pinch', 10, 20, 10, 20); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 10, 20, 0x0); - expect(keyEvent).to.not.have.been.called; - - pointerEvent.resetHistory(); - - gestureMove('pinch', 10, 20, 70, 80); - - expect(pointerEvent).to.have.been.calledThrice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 10, 20, 0x0); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 10, 20, bmask); - expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock, - 10, 20, 0x0); - - expect(keyEvent).to.have.been.calledTwice; - expect(keyEvent.firstCall).to.have.been.calledWith(client._sock, - keysym, 1); - expect(keyEvent.secondCall).to.have.been.calledWith(client._sock, - keysym, 0); - - expect(keyEvent.firstCall).to.have.been.calledBefore(pointerEvent.secondCall); - expect(keyEvent.lastCall).to.have.been.calledAfter(pointerEvent.lastCall); - - pointerEvent.resetHistory(); - keyEvent.resetHistory(); - - gestureEnd('pinch', 10, 20); - - expect(pointerEvent).to.not.have.been.called; - expect(keyEvent).to.not.have.been.called; - }); - - it('should handle large gesture pinch', function () { - let keysym = KeyTable.XK_Control_L; - let bmask = 0x10; // Button mask for scroll down - - gestureStart('pinch', 20, 40, 150, 150); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 20, 40, 0x0); - expect(keyEvent).to.not.have.been.called; - - pointerEvent.resetHistory(); - - gestureMove('pinch', 20, 40, 30, 30); - - expect(pointerEvent).to.have.been.callCount(5); - expect(pointerEvent.getCall(0)).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - expect(pointerEvent.getCall(1)).to.have.been.calledWith(client._sock, - 20, 40, bmask); - expect(pointerEvent.getCall(2)).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - expect(pointerEvent.getCall(3)).to.have.been.calledWith(client._sock, - 20, 40, bmask); - expect(pointerEvent.getCall(4)).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - - expect(keyEvent).to.have.been.calledTwice; - expect(keyEvent.firstCall).to.have.been.calledWith(client._sock, - keysym, 1); - expect(keyEvent.secondCall).to.have.been.calledWith(client._sock, - keysym, 0); - - expect(keyEvent.firstCall).to.have.been.calledBefore(pointerEvent.secondCall); - expect(keyEvent.lastCall).to.have.been.calledAfter(pointerEvent.lastCall); - - pointerEvent.resetHistory(); - keyEvent.resetHistory(); - - gestureEnd('pinch', 20, 40); - - expect(pointerEvent).to.not.have.been.called; - expect(keyEvent).to.not.have.been.called; - }); - - it('should handle multiple small gesture pinch out events', function () { - let keysym = KeyTable.XK_Control_L; - let bmask = 0x8; // Button mask for scroll down - - gestureStart('pinch', 20, 40, 0, 10); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 20, 40, 0x0); - expect(keyEvent).to.not.have.been.called; - - pointerEvent.resetHistory(); - - gestureMove('pinch', 20, 40, 0, 30); - clock.tick(50); - - expect(pointerEvent).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - - pointerEvent.resetHistory(); - - gestureMove('pinch', 20, 40, 0, 60); - clock.tick(50); - - expect(pointerEvent).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - - pointerEvent.resetHistory(); - keyEvent.resetHistory(); - - gestureMove('pinch', 20, 40, 0, 90); - - expect(pointerEvent).to.have.been.calledThrice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 20, 40, bmask); - expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - - expect(keyEvent).to.have.been.calledTwice; - expect(keyEvent.firstCall).to.have.been.calledWith(client._sock, - keysym, 1); - expect(keyEvent.secondCall).to.have.been.calledWith(client._sock, - keysym, 0); - - expect(keyEvent.firstCall).to.have.been.calledBefore(pointerEvent.secondCall); - expect(keyEvent.lastCall).to.have.been.calledAfter(pointerEvent.lastCall); - - pointerEvent.resetHistory(); - keyEvent.resetHistory(); - - gestureEnd('pinch', 20, 40); - - expect(keyEvent).to.not.have.been.called; - }); - - it('should send correct key control code', function () { - let keysym = KeyTable.XK_Control_L; - let code = 0x1d; - let bmask = 0x10; // Button mask for scroll down - - client._qemuExtKeyEventSupported = true; - - gestureStart('pinch', 20, 40, 90, 90); - - expect(pointerEvent).to.have.been.calledOnceWith(client._sock, - 20, 40, 0x0); - expect(qemuKeyEvent).to.not.have.been.called; - - pointerEvent.resetHistory(); - - gestureMove('pinch', 20, 40, 30, 30); - - expect(pointerEvent).to.have.been.calledThrice; - expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock, - 20, 40, bmask); - expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock, - 20, 40, 0x0); - - expect(qemuKeyEvent).to.have.been.calledTwice; - expect(qemuKeyEvent.firstCall).to.have.been.calledWith(client._sock, - keysym, - true, - code); - expect(qemuKeyEvent.secondCall).to.have.been.calledWith(client._sock, - keysym, - false, - code); - - expect(qemuKeyEvent.firstCall).to.have.been.calledBefore(pointerEvent.secondCall); - expect(qemuKeyEvent.lastCall).to.have.been.calledAfter(pointerEvent.lastCall); - - pointerEvent.resetHistory(); - qemuKeyEvent.resetHistory(); - - gestureEnd('pinch', 20, 40); - - expect(pointerEvent).to.not.have.been.called; - expect(qemuKeyEvent).to.not.have.been.called; - }); - }); - }); - - describe('WebSocket Events', function () { - // message events - it('should do nothing if we receive an empty message and have nothing in the queue', function () { - sinon.spy(client, "_normalMsg"); - client._sock._websocket._receiveData(new Uint8Array([])); - expect(client._normalMsg).to.not.have.been.called; - }); - - it('should handle a message in the connected state as a normal message', function () { - sinon.spy(client, "_normalMsg"); - client._sock._websocket._receiveData(new Uint8Array([1, 2, 3])); - expect(client._normalMsg).to.have.been.called; - }); - - it('should handle a message in any non-disconnected/failed state like an init message', function () { - client._rfbConnectionState = 'connecting'; - client._rfbInitState = 'ProtocolVersion'; - sinon.spy(client, "_initMsg"); - client._sock._websocket._receiveData(new Uint8Array([1, 2, 3])); - expect(client._initMsg).to.have.been.called; - }); - - it('should process all normal messages directly', function () { - const spy = sinon.spy(); - client.addEventListener("bell", spy); - client._sock._websocket._receiveData(new Uint8Array([0x02, 0x02])); - expect(spy).to.have.been.calledTwice; - }); - - // open events - it('should update the state to ProtocolVersion on open (if the state is "connecting")', function () { - client = new RFB(document.createElement('div'), 'wss://host:8675'); - this.clock.tick(); - client._sock._websocket._open(); - expect(client._rfbInitState).to.equal('ProtocolVersion'); - }); - - it('should fail if we are not currently ready to connect and we get an "open" event', function () { - sinon.spy(client, "_fail"); - client._rfbConnectionState = 'connected'; - client._sock._websocket._open(); - expect(client._fail).to.have.been.calledOnce; - }); - - // close events - it('should transition to "disconnected" from "disconnecting" on a close event', function () { - const real = client._sock._websocket.close; - client._sock._websocket.close = () => {}; - client.disconnect(); - expect(client._rfbConnectionState).to.equal('disconnecting'); - client._sock._websocket.close = real; - client._sock._websocket.close(); - expect(client._rfbConnectionState).to.equal('disconnected'); - }); - - it('should fail if we get a close event while connecting', function () { - sinon.spy(client, "_fail"); - client._rfbConnectionState = 'connecting'; - client._sock._websocket.close(); - expect(client._fail).to.have.been.calledOnce; - }); - - it('should unregister close event handler', function () { - sinon.spy(client._sock, 'off'); - client.disconnect(); - client._sock._websocket.close(); - expect(client._sock.off).to.have.been.calledWith('close'); - }); - - // error events do nothing - }); - }); - - describe('Quality level setting', function () { - const defaultQuality = 6; - - let client; - - beforeEach(function () { - client = makeRFB(); - sinon.spy(RFB.messages, "clientEncodings"); - }); - - afterEach(function () { - RFB.messages.clientEncodings.restore(); - }); - - it(`should equal ${defaultQuality} by default`, function () { - expect(client._qualityLevel).to.equal(defaultQuality); - }); - - it('should ignore non-integers when set', function () { - client.qualityLevel = '1'; - expect(RFB.messages.clientEncodings).to.not.have.been.called; - - RFB.messages.clientEncodings.resetHistory(); - - client.qualityLevel = 1.5; - expect(RFB.messages.clientEncodings).to.not.have.been.called; - - RFB.messages.clientEncodings.resetHistory(); - - client.qualityLevel = null; - expect(RFB.messages.clientEncodings).to.not.have.been.called; - - RFB.messages.clientEncodings.resetHistory(); - - client.qualityLevel = undefined; - expect(RFB.messages.clientEncodings).to.not.have.been.called; - - RFB.messages.clientEncodings.resetHistory(); - - client.qualityLevel = {}; - expect(RFB.messages.clientEncodings).to.not.have.been.called; - }); - - it('should ignore integers out of range [0, 9]', function () { - client.qualityLevel = -1; - expect(RFB.messages.clientEncodings).to.not.have.been.called; - - RFB.messages.clientEncodings.resetHistory(); - - client.qualityLevel = 10; - expect(RFB.messages.clientEncodings).to.not.have.been.called; - }); - - it('should send clientEncodings with new quality value', function () { - let newQuality; - - newQuality = 8; - client.qualityLevel = newQuality; - expect(client.qualityLevel).to.equal(newQuality); - expect(RFB.messages.clientEncodings).to.have.been.calledOnce; - expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.pseudoEncodingQualityLevel0 + newQuality); - }); - - it('should not send clientEncodings if quality is the same', function () { - let newQuality; - - newQuality = 2; - client.qualityLevel = newQuality; - expect(RFB.messages.clientEncodings).to.have.been.calledOnce; - expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.pseudoEncodingQualityLevel0 + newQuality); - - RFB.messages.clientEncodings.resetHistory(); - - client.qualityLevel = newQuality; - expect(RFB.messages.clientEncodings).to.not.have.been.called; - }); - - it('should not send clientEncodings if not in connected state', function () { - let newQuality; - - client._rfbConnectionState = ''; - newQuality = 2; - client.qualityLevel = newQuality; - expect(RFB.messages.clientEncodings).to.not.have.been.called; - - RFB.messages.clientEncodings.resetHistory(); - - client._rfbConnectionState = 'connnecting'; - newQuality = 6; - client.qualityLevel = newQuality; - expect(RFB.messages.clientEncodings).to.not.have.been.called; - - RFB.messages.clientEncodings.resetHistory(); - - client._rfbConnectionState = 'connected'; - newQuality = 5; - client.qualityLevel = newQuality; - expect(RFB.messages.clientEncodings).to.have.been.calledOnce; - expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.pseudoEncodingQualityLevel0 + newQuality); - }); - }); - - describe('Compression level setting', function () { - const defaultCompression = 2; - - let client; - - beforeEach(function () { - client = makeRFB(); - sinon.spy(RFB.messages, "clientEncodings"); - }); - - afterEach(function () { - RFB.messages.clientEncodings.restore(); - }); - - it(`should equal ${defaultCompression} by default`, function () { - expect(client._compressionLevel).to.equal(defaultCompression); - }); - - it('should ignore non-integers when set', function () { - client.compressionLevel = '1'; - expect(RFB.messages.clientEncodings).to.not.have.been.called; - - RFB.messages.clientEncodings.resetHistory(); - - client.compressionLevel = 1.5; - expect(RFB.messages.clientEncodings).to.not.have.been.called; - - RFB.messages.clientEncodings.resetHistory(); - - client.compressionLevel = null; - expect(RFB.messages.clientEncodings).to.not.have.been.called; - - RFB.messages.clientEncodings.resetHistory(); - - client.compressionLevel = undefined; - expect(RFB.messages.clientEncodings).to.not.have.been.called; - - RFB.messages.clientEncodings.resetHistory(); - - client.compressionLevel = {}; - expect(RFB.messages.clientEncodings).to.not.have.been.called; - }); - - it('should ignore integers out of range [0, 9]', function () { - client.compressionLevel = -1; - expect(RFB.messages.clientEncodings).to.not.have.been.called; - - RFB.messages.clientEncodings.resetHistory(); - - client.compressionLevel = 10; - expect(RFB.messages.clientEncodings).to.not.have.been.called; - }); - - it('should send clientEncodings with new compression value', function () { - let newCompression; - - newCompression = 5; - client.compressionLevel = newCompression; - expect(client.compressionLevel).to.equal(newCompression); - expect(RFB.messages.clientEncodings).to.have.been.calledOnce; - expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.pseudoEncodingCompressLevel0 + newCompression); - }); - - it('should not send clientEncodings if compression is the same', function () { - let newCompression; - - newCompression = 9; - client.compressionLevel = newCompression; - expect(RFB.messages.clientEncodings).to.have.been.calledOnce; - expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.pseudoEncodingCompressLevel0 + newCompression); - - RFB.messages.clientEncodings.resetHistory(); - - client.compressionLevel = newCompression; - expect(RFB.messages.clientEncodings).to.not.have.been.called; - }); - - it('should not send clientEncodings if not in connected state', function () { - let newCompression; - - client._rfbConnectionState = ''; - newCompression = 7; - client.compressionLevel = newCompression; - expect(RFB.messages.clientEncodings).to.not.have.been.called; - - RFB.messages.clientEncodings.resetHistory(); - - client._rfbConnectionState = 'connnecting'; - newCompression = 6; - client.compressionLevel = newCompression; - expect(RFB.messages.clientEncodings).to.not.have.been.called; - - RFB.messages.clientEncodings.resetHistory(); - - client._rfbConnectionState = 'connected'; - newCompression = 5; - client.compressionLevel = newCompression; - expect(RFB.messages.clientEncodings).to.have.been.calledOnce; - expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.pseudoEncodingCompressLevel0 + newCompression); - }); - }); -}); - -describe('RFB messages', function () { - let sock; - - beforeEach(function () { - let websock = new FakeWebSocket(); - websock._open(); - sock = new Websock(); - sock.attach(websock); - }); - - describe('Input Events', function () { - it('should send correct data for keyboard events', function () { - // FIXME: down should be boolean - RFB.messages.keyEvent(sock, 0x12345678, 0); - let expected = - [ 4, 0, 0, 0, 0x12, 0x34, 0x56, 0x78]; - expect(sock).to.have.sent(new Uint8Array(expected)); - - RFB.messages.keyEvent(sock, 0x90abcdef, 1); - expected = - [ 4, 1, 0, 0, 0x90, 0xab, 0xcd, 0xef]; - expect(sock).to.have.sent(new Uint8Array(expected)); - }); - - it('should send correct data for QEMU keyboard events', function () { - // FIXME: down should be boolean - RFB.messages.QEMUExtendedKeyEvent(sock, 0x12345678, 0, 0x55); - let expected = - [ 255, 0, 0, 0, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x55]; - expect(sock).to.have.sent(new Uint8Array(expected)); - - RFB.messages.QEMUExtendedKeyEvent(sock, 0x90abcdef, 1, 0xe055); - expected = - [ 255, 0, 0, 1, 0x90, 0xab, 0xcd, 0xef, 0x00, 0x00, 0x00, 0xd5]; - expect(sock).to.have.sent(new Uint8Array(expected)); - }); - - it('should send correct data for pointer events', function () { - RFB.messages.pointerEvent(sock, 12345, 54321, 0xab); - let expected = - [ 5, 0xab, 0x30, 0x39, 0xd4, 0x31]; - expect(sock).to.have.sent(new Uint8Array(expected)); - }); - }); - - describe('Clipboard Events', function () { - it('should send correct data for clipboard events', function () { - RFB.messages.clientCutText(sock, new Uint8Array([ 0x01, 0x23, 0x45, 0x67 ])); - let expected = - [ 6, 0, 0, 0, 0x00, 0x00, 0x00, 0x04, - 0x01, 0x23, 0x45, 0x67 ]; - expect(sock).to.have.sent(new Uint8Array(expected)); - }); - }); - - describe('Extended Clipboard Handling Send', function () { - it('should call clientCutText with correct Caps data', function () { - let formats = { - 0: 2, - 2: 4121 - }; - let expectedData = new Uint8Array([0x06, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xF4, - 0x1F, 0x00, 0x00, 0x05, - 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x10, 0x19]); - let actions = [ - 1 << 24, // Caps - 1 << 25, // Request - 1 << 26, // Peek - 1 << 27, // Notify - 1 << 28 // Provide - ]; - - RFB.messages.extendedClipboardCaps(sock, actions, formats); - - expect(sock).to.have.sent(expectedData); - }); - - it('should call clientCutText with correct Request data', function () { - let formats = new Uint8Array([0x01]); - let expectedData = new Uint8Array([0x06, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFC, - 0x02, 0x00, 0x00, 0x01]); - - RFB.messages.extendedClipboardRequest(sock, formats); - - expect(sock).to.have.sent(expectedData); - }); - - it('should call clientCutText with correct Notify data', function () { - let formats = new Uint8Array([0x01]); - let expectedData = new Uint8Array([0x06, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFC, - 0x08, 0x00, 0x00, 0x01]); - - RFB.messages.extendedClipboardNotify(sock, formats); - - expect(sock).to.have.sent(expectedData); - }); - - it('should call clientCutText with correct Provide data', function () { - let testText = "Test string"; - let expectedText = encodeUTF8(testText + "\0"); - - let deflatedData = deflateWithSize(expectedText); - - // Build Expected with flags and deflated data - let expectedData = new Uint8Array(8 + 4 + deflatedData.length); - expectedData[0] = 0x06; // Message type - expectedData[1] = 0x00; - expectedData[2] = 0x00; - expectedData[3] = 0x00; - expectedData[4] = 0xFF; // Size - expectedData[5] = 0xFF; - expectedData[6] = 0xFF; - expectedData[7] = 256 - (4 + deflatedData.length); - expectedData[8] = 0x10; // The client capabilities - expectedData[9] = 0x00; // Reserved flags - expectedData[10] = 0x00; // Reserved flags - expectedData[11] = 0x01; // The formats client supports - expectedData.set(deflatedData, 12); - - RFB.messages.extendedClipboardProvide(sock, [0x01], [testText]); - - expect(sock).to.have.sent(expectedData); - - }); - - describe('End of line characters', function () { - it('Carriage return', function () { - - let testText = "Hello\rworld\r\r!"; - let expectedText = encodeUTF8("Hello\r\nworld\r\n\r\n!\0"); - - let deflatedData = deflateWithSize(expectedText); - - // Build Expected with flags and deflated data - let expectedData = new Uint8Array(8 + 4 + deflatedData.length); - expectedData[0] = 0x06; // Message type - expectedData[1] = 0x00; - expectedData[2] = 0x00; - expectedData[3] = 0x00; - expectedData[4] = 0xFF; // Size - expectedData[5] = 0xFF; - expectedData[6] = 0xFF; - expectedData[7] = 256 - (4 + deflatedData.length); - expectedData[8] = 0x10; // The client capabilities - expectedData[9] = 0x00; // Reserved flags - expectedData[10] = 0x00; // Reserved flags - expectedData[11] = 0x01; // The formats client supports - expectedData.set(deflatedData, 12); - - RFB.messages.extendedClipboardProvide(sock, [0x01], [testText]); - - expect(sock).to.have.sent(expectedData); - }); - - it('Carriage return Line feed', function () { - - let testText = "Hello\r\n\r\nworld\r\n!"; - let expectedText = encodeUTF8(testText + "\0"); - - let deflatedData = deflateWithSize(expectedText); - - // Build Expected with flags and deflated data - let expectedData = new Uint8Array(8 + 4 + deflatedData.length); - expectedData[0] = 0x06; // Message type - expectedData[1] = 0x00; - expectedData[2] = 0x00; - expectedData[3] = 0x00; - expectedData[4] = 0xFF; // Size - expectedData[5] = 0xFF; - expectedData[6] = 0xFF; - expectedData[7] = 256 - (4 + deflatedData.length); - expectedData[8] = 0x10; // The client capabilities - expectedData[9] = 0x00; // Reserved flags - expectedData[10] = 0x00; // Reserved flags - expectedData[11] = 0x01; // The formats client supports - expectedData.set(deflatedData, 12); - - RFB.messages.extendedClipboardProvide(sock, [0x01], [testText]); - - expect(sock).to.have.sent(expectedData); - }); - - it('Line feed', function () { - let testText = "Hello\n\n\nworld\n!"; - let expectedText = encodeUTF8("Hello\r\n\r\n\r\nworld\r\n!\0"); - - let deflatedData = deflateWithSize(expectedText); - - // Build Expected with flags and deflated data - let expectedData = new Uint8Array(8 + 4 + deflatedData.length); - expectedData[0] = 0x06; // Message type - expectedData[1] = 0x00; - expectedData[2] = 0x00; - expectedData[3] = 0x00; - expectedData[4] = 0xFF; // Size - expectedData[5] = 0xFF; - expectedData[6] = 0xFF; - expectedData[7] = 256 - (4 + deflatedData.length); - expectedData[8] = 0x10; // The client capabilities - expectedData[9] = 0x00; // Reserved flags - expectedData[10] = 0x00; // Reserved flags - expectedData[11] = 0x01; // The formats client supports - expectedData.set(deflatedData, 12); - - RFB.messages.extendedClipboardProvide(sock, [0x01], [testText]); - - expect(sock).to.have.sent(expectedData); - }); - - it('Carriage return and Line feed mixed', function () { - let testText = "\rHello\r\n\rworld\n\n!"; - let expectedText = encodeUTF8("\r\nHello\r\n\r\nworld\r\n\r\n!\0"); - - let deflatedData = deflateWithSize(expectedText); - - // Build Expected with flags and deflated data - let expectedData = new Uint8Array(8 + 4 + deflatedData.length); - expectedData[0] = 0x06; // Message type - expectedData[1] = 0x00; - expectedData[2] = 0x00; - expectedData[3] = 0x00; - expectedData[4] = 0xFF; // Size - expectedData[5] = 0xFF; - expectedData[6] = 0xFF; - expectedData[7] = 256 - (4 + deflatedData.length); - expectedData[8] = 0x10; // The client capabilities - expectedData[9] = 0x00; // Reserved flags - expectedData[10] = 0x00; // Reserved flags - expectedData[11] = 0x01; // The formats client supports - expectedData.set(deflatedData, 12); - - RFB.messages.extendedClipboardProvide(sock, [0x01], [testText]); - - expect(sock).to.have.sent(expectedData); - }); - }); - }); - - describe('Screen Layout', function () { - it('should send correct data for screen layout changes', function () { - RFB.messages.setDesktopSize(sock, 12345, 54321, 0x12345678, 0x90abcdef); - let expected = - [ 251, 0, 0x30, 0x39, 0xd4, 0x31, 0x01, 0x00, - 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x39, 0xd4, 0x31, 0x90, 0xab, 0xcd, 0xef ]; - expect(sock).to.have.sent(new Uint8Array(expected)); - }); - }); - - describe('Fences', function () { - it('should send correct data for fences', function () { - // FIXME: Payload should be a byte array - RFB.messages.clientFence(sock, 0x12345678, "text"); - let expected = - [ 248, 0, 0, 0, 0x12, 0x34, 0x56, 0x78, - 4, 0x74, 0x65, 0x78, 0x74 ]; - expect(sock).to.have.sent(new Uint8Array(expected)); - }); - }); - - describe('Continuous Updates', function () { - it('should send correct data for continuous updates configuration', function () { - // FIXME: enable should be boolean - RFB.messages.enableContinuousUpdates(sock, 0, 12345, 54321, 34343, 18181); - let expected = - [ 150, 0, 0x30, 0x39, 0xd4, 0x31, 0x86, 0x27, 0x47, 0x05 ]; - expect(sock).to.have.sent(new Uint8Array(expected)); - }); - }); - - describe('Pixel Format', function () { - it('should send correct data for normal depth', function () { - RFB.messages.pixelFormat(sock, 24, true); - let expected = - [ 0, 0, 0, 0, 32, 24, 0, 1, - 0, 255, 0, 255, 0, 255, 0, 8, 16, 0, 0, 0 ]; - expect(sock).to.have.sent(new Uint8Array(expected)); - }); - - it('should send correct data for low depth', function () { - RFB.messages.pixelFormat(sock, 8, true); - let expected = - [ 0, 0, 0, 0, 8, 8, 0, 1, - 0, 3, 0, 3, 0, 3, 0, 2, 4, 0, 0, 0 ]; - expect(sock).to.have.sent(new Uint8Array(expected)); - }); - }); - - describe('Encodings', function () { - it('should send correct data for supported encodings', function () { - RFB.messages.clientEncodings(sock, [ 0x12345678, - 0x90abcdef, - 0x10293847 ]); - let expected = - [ 2, 0, 0, 3, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, - 0xef, 0x10, 0x29, 0x38, 0x47 ]; - expect(sock).to.have.sent(new Uint8Array(expected)); - }); - }); - - describe('Update request', function () { - it('should send correct data for update request', function () { - RFB.messages.fbUpdateRequest(sock, true, 12345, 54321, 34343, 18181); - let expected = - [ 3, 1, 0x30, 0x39, 0xd4, 0x31, 0x86, 0x27, 0x47, 0x05 ]; - expect(sock).to.have.sent(new Uint8Array(expected)); - }); - }); - - describe('XVP operations', function () { - it('should send correct data for XVP operations', function () { - RFB.messages.xvpOp(sock, 123, 45); - let expected = - [ 250, 0, 123, 45 ]; - expect(sock).to.have.sent(new Uint8Array(expected)); - }); - }); -}); diff --git a/base/app/novnc/tests/test.rre.js b/base/app/novnc/tests/test.rre.js deleted file mode 100644 index c55d7f3..0000000 --- a/base/app/novnc/tests/test.rre.js +++ /dev/null @@ -1,115 +0,0 @@ -const expect = chai.expect; - -import Websock from '../core/websock.js'; -import Display from '../core/display.js'; - -import RREDecoder from '../core/decoders/rre.js'; - -import FakeWebSocket from './fake.websocket.js'; - -function testDecodeRect(decoder, x, y, width, height, data, display, depth) { - let sock; - let done = false; - - sock = new Websock; - sock.open("ws://example.com"); - - sock.on('message', () => { - done = decoder.decodeRect(x, y, width, height, sock, display, depth); - }); - - // Empty messages are filtered at multiple layers, so we need to - // do a direct call - if (data.length === 0) { - done = decoder.decodeRect(x, y, width, height, sock, display, depth); - } else { - sock._websocket._receiveData(new Uint8Array(data)); - } - - display.flip(); - - return done; -} - -function push16(arr, num) { - arr.push((num >> 8) & 0xFF, - num & 0xFF); -} - -function push32(arr, num) { - arr.push((num >> 24) & 0xFF, - (num >> 16) & 0xFF, - (num >> 8) & 0xFF, - num & 0xFF); -} - -describe('RRE Decoder', function () { - let decoder; - let display; - - before(FakeWebSocket.replace); - after(FakeWebSocket.restore); - - beforeEach(function () { - decoder = new RREDecoder(); - display = new Display(document.createElement('canvas')); - display.resize(4, 4); - }); - - // TODO(directxman12): test rre_chunk_sz? - - it('should handle the RRE encoding', function () { - let data = []; - push32(data, 2); // 2 subrects - push32(data, 0x00ff0000); // becomes 00ff0000 --> #00FF00 bg color - data.push(0x00); // becomes 0000ff00 --> #0000FF fg color - data.push(0x00); - data.push(0xff); - data.push(0x00); - push16(data, 0); // x: 0 - push16(data, 0); // y: 0 - push16(data, 2); // width: 2 - push16(data, 2); // height: 2 - data.push(0x00); // becomes 0000ff00 --> #0000FF fg color - data.push(0x00); - data.push(0xff); - data.push(0x00); - push16(data, 2); // x: 2 - push16(data, 2); // y: 2 - push16(data, 2); // width: 2 - push16(data, 2); // height: 2 - - let done = testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 - ]); - - expect(done).to.be.true; - expect(display).to.have.displayed(targetData); - }); - - it('should handle empty rects', function () { - display.fillRect(0, 0, 4, 4, [ 0x00, 0x00, 0xff ]); - display.fillRect(2, 0, 2, 2, [ 0x00, 0xff, 0x00 ]); - display.fillRect(0, 2, 2, 2, [ 0x00, 0xff, 0x00 ]); - - let done = testDecodeRect(decoder, 1, 2, 0, 0, - [ 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff ], - display, 24); - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 - ]); - - expect(done).to.be.true; - expect(display).to.have.displayed(targetData); - }); -}); diff --git a/base/app/novnc/tests/test.tight.js b/base/app/novnc/tests/test.tight.js deleted file mode 100644 index 141d7b6..0000000 --- a/base/app/novnc/tests/test.tight.js +++ /dev/null @@ -1,482 +0,0 @@ -const expect = chai.expect; - -import Websock from '../core/websock.js'; -import Display from '../core/display.js'; - -import TightDecoder from '../core/decoders/tight.js'; - -import FakeWebSocket from './fake.websocket.js'; - -function testDecodeRect(decoder, x, y, width, height, data, display, depth) { - let sock; - let done = false; - - sock = new Websock; - sock.open("ws://example.com"); - - sock.on('message', () => { - done = decoder.decodeRect(x, y, width, height, sock, display, depth); - }); - - // Empty messages are filtered at multiple layers, so we need to - // do a direct call - if (data.length === 0) { - done = decoder.decodeRect(x, y, width, height, sock, display, depth); - } else { - sock._websocket._receiveData(new Uint8Array(data)); - } - - display.flip(); - - return done; -} - -describe('Tight Decoder', function () { - let decoder; - let display; - - before(FakeWebSocket.replace); - after(FakeWebSocket.restore); - - beforeEach(function () { - decoder = new TightDecoder(); - display = new Display(document.createElement('canvas')); - display.resize(4, 4); - }); - - it('should handle fill rects', function () { - let done = testDecodeRect(decoder, 0, 0, 4, 4, - [0x80, 0xff, 0x88, 0x44], - display, 24); - - let targetData = new Uint8Array([ - 0xff, 0x88, 0x44, 255, 0xff, 0x88, 0x44, 255, 0xff, 0x88, 0x44, 255, 0xff, 0x88, 0x44, 255, - 0xff, 0x88, 0x44, 255, 0xff, 0x88, 0x44, 255, 0xff, 0x88, 0x44, 255, 0xff, 0x88, 0x44, 255, - 0xff, 0x88, 0x44, 255, 0xff, 0x88, 0x44, 255, 0xff, 0x88, 0x44, 255, 0xff, 0x88, 0x44, 255, - 0xff, 0x88, 0x44, 255, 0xff, 0x88, 0x44, 255, 0xff, 0x88, 0x44, 255, 0xff, 0x88, 0x44, 255, - ]); - - expect(done).to.be.true; - expect(display).to.have.displayed(targetData); - }); - - it('should handle uncompressed copy rects', function () { - let done; - let blueData = [ 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff ]; - let greenData = [ 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00 ]; - - done = testDecodeRect(decoder, 0, 0, 2, 1, blueData, display, 24); - expect(done).to.be.true; - done = testDecodeRect(decoder, 0, 1, 2, 1, blueData, display, 24); - expect(done).to.be.true; - done = testDecodeRect(decoder, 2, 0, 2, 1, greenData, display, 24); - expect(done).to.be.true; - done = testDecodeRect(decoder, 2, 1, 2, 1, greenData, display, 24); - expect(done).to.be.true; - done = testDecodeRect(decoder, 0, 2, 2, 1, greenData, display, 24); - expect(done).to.be.true; - done = testDecodeRect(decoder, 0, 3, 2, 1, greenData, display, 24); - expect(done).to.be.true; - done = testDecodeRect(decoder, 2, 2, 2, 1, blueData, display, 24); - expect(done).to.be.true; - done = testDecodeRect(decoder, 2, 3, 2, 1, blueData, display, 24); - expect(done).to.be.true; - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 - ]); - - expect(display).to.have.displayed(targetData); - }); - - it('should handle compressed copy rects', function () { - let data = [ - // Control byte - 0x00, - // Pixels (compressed) - 0x15, - 0x78, 0x9c, 0x63, 0x60, 0xf8, 0xcf, 0x00, 0x44, - 0x60, 0x82, 0x01, 0x99, 0x8d, 0x29, 0x02, 0xa6, - 0x00, 0x7e, 0xbf, 0x0f, 0xf1 ]; - - let done = testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 - ]); - - expect(done).to.be.true; - expect(display).to.have.displayed(targetData); - }); - - it('should handle uncompressed mono rects', function () { - let data = [ - // Control bytes - 0x40, 0x01, - // Palette - 0x01, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, - // Pixels - 0x30, 0x30, 0xc0, 0xc0 ]; - - let done = testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 - ]); - - expect(done).to.be.true; - expect(display).to.have.displayed(targetData); - }); - - it('should handle compressed mono rects', function () { - display.resize(4, 12); - - let data = [ - // Control bytes - 0x40, 0x01, - // Palette - 0x01, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, - // Pixels (compressed) - 0x0e, - 0x78, 0x9c, 0x33, 0x30, 0x38, 0x70, 0xc0, 0x00, - 0x8a, 0x01, 0x21, 0x3c, 0x05, 0xa1 ]; - - let done = testDecodeRect(decoder, 0, 0, 4, 12, data, display, 24); - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 - ]); - - expect(done).to.be.true; - expect(display).to.have.displayed(targetData); - }); - - it('should handle uncompressed palette rects', function () { - let done; - let data1 = [ - // Control bytes - 0x40, 0x01, - // Palette - 0x02, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, - // Pixels - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01 ]; - let data2 = [ - // Control bytes - 0x40, 0x01, - // Palette - 0x02, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, - // Pixels - 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00 ]; - - done = testDecodeRect(decoder, 0, 0, 4, 2, data1, display, 24); - expect(done).to.be.true; - done = testDecodeRect(decoder, 0, 2, 4, 2, data2, display, 24); - expect(done).to.be.true; - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 - ]); - - expect(display).to.have.displayed(targetData); - }); - - it('should handle compressed palette rects', function () { - let data = [ - // Control bytes - 0x40, 0x01, - // Palette - 0x02, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, - // Pixels (compressed) - 0x12, - 0x78, 0x9c, 0x63, 0x60, 0x60, 0x64, 0x64, 0x00, - 0x62, 0x08, 0xc9, 0xc0, 0x00, 0x00, 0x00, 0x54, - 0x00, 0x09 ]; - - let done = testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 - ]); - - expect(done).to.be.true; - expect(display).to.have.displayed(targetData); - }); - - it('should handle uncompressed gradient rects', function () { - let done; - let blueData = [ 0x40, 0x02, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00 ]; - let greenData = [ 0x40, 0x02, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 ]; - - done = testDecodeRect(decoder, 0, 0, 2, 1, blueData, display, 24); - expect(done).to.be.true; - done = testDecodeRect(decoder, 0, 1, 2, 1, blueData, display, 24); - expect(done).to.be.true; - done = testDecodeRect(decoder, 2, 0, 2, 1, greenData, display, 24); - expect(done).to.be.true; - done = testDecodeRect(decoder, 2, 1, 2, 1, greenData, display, 24); - expect(done).to.be.true; - done = testDecodeRect(decoder, 0, 2, 2, 1, greenData, display, 24); - expect(done).to.be.true; - done = testDecodeRect(decoder, 0, 3, 2, 1, greenData, display, 24); - expect(done).to.be.true; - done = testDecodeRect(decoder, 2, 2, 2, 1, blueData, display, 24); - expect(done).to.be.true; - done = testDecodeRect(decoder, 2, 3, 2, 1, blueData, display, 24); - expect(done).to.be.true; - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 - ]); - - expect(display).to.have.displayed(targetData); - }); - - it('should handle compressed gradient rects', function () { - let data = [ - // Control byte - 0x40, 0x02, - // Pixels (compressed) - 0x18, - 0x78, 0x9c, 0x62, 0x60, 0xf8, 0xcf, 0x00, 0x04, - 0xff, 0x19, 0x19, 0xd0, 0x00, 0x44, 0x84, 0xf1, - 0x3f, 0x9a, 0x30, 0x00, 0x00, 0x00, 0xff, 0xff ]; - - let done = testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 - ]); - - expect(done).to.be.true; - expect(display).to.have.displayed(targetData); - }); - - it('should handle empty copy rects', function () { - display.fillRect(0, 0, 4, 4, [ 0x00, 0x00, 0xff ]); - display.fillRect(2, 0, 2, 2, [ 0x00, 0xff, 0x00 ]); - display.fillRect(0, 2, 2, 2, [ 0x00, 0xff, 0x00 ]); - - let done = testDecodeRect(decoder, 1, 2, 0, 0, [ 0x00 ], display, 24); - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 - ]); - - expect(done).to.be.true; - expect(display).to.have.displayed(targetData); - }); - - it('should handle empty palette rects', function () { - display.fillRect(0, 0, 4, 4, [ 0x00, 0x00, 0xff ]); - display.fillRect(2, 0, 2, 2, [ 0x00, 0xff, 0x00 ]); - display.fillRect(0, 2, 2, 2, [ 0x00, 0xff, 0x00 ]); - - let done = testDecodeRect(decoder, 1, 2, 0, 0, - [ 0x40, 0x01, 0x01, - 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff ], display, 24); - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 - ]); - - expect(done).to.be.true; - expect(display).to.have.displayed(targetData); - }); - - it('should handle empty gradient rects', function () { - display.fillRect(0, 0, 4, 4, [ 0x00, 0x00, 0xff ]); - display.fillRect(2, 0, 2, 2, [ 0x00, 0xff, 0x00 ]); - display.fillRect(0, 2, 2, 2, [ 0x00, 0xff, 0x00 ]); - - let done = testDecodeRect(decoder, 1, 2, 0, 0, - [ 0x40, 0x02 ], display, 24); - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 - ]); - - expect(done).to.be.true; - expect(display).to.have.displayed(targetData); - }); - - it('should handle empty fill rects', function () { - display.fillRect(0, 0, 4, 4, [ 0x00, 0x00, 0xff ]); - display.fillRect(2, 0, 2, 2, [ 0x00, 0xff, 0x00 ]); - display.fillRect(0, 2, 2, 2, [ 0x00, 0xff, 0x00 ]); - - let done = testDecodeRect(decoder, 1, 2, 0, 0, - [ 0x80, 0xff, 0xff, 0xff ], - display, 24); - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 - ]); - - expect(done).to.be.true; - expect(display).to.have.displayed(targetData); - }); - - it('should handle JPEG rects', async function () { - let data = [ - // Control bytes - 0x90, 0xd6, 0x05, - // JPEG data - 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, - 0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x00, 0x48, - 0x00, 0x48, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x13, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, - 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, - 0x50, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xff, 0xdb, - 0x00, 0x43, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0xff, 0xc2, 0x00, 0x11, 0x08, - 0x00, 0x04, 0x00, 0x04, 0x03, 0x01, 0x11, 0x00, - 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, - 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x07, 0xff, 0xc4, 0x00, 0x14, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x08, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, - 0x00, 0x02, 0x10, 0x03, 0x10, 0x00, 0x00, 0x01, - 0x1e, 0x0a, 0xa7, 0x7f, 0xff, 0xc4, 0x00, 0x14, - 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x05, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01, - 0x00, 0x01, 0x05, 0x02, 0x5d, 0x74, 0x41, 0x47, - 0xff, 0xc4, 0x00, 0x1f, 0x11, 0x00, 0x01, 0x04, - 0x02, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x04, 0x05, - 0x07, 0x08, 0x14, 0x16, 0x03, 0x15, 0x17, 0x25, - 0x26, 0xff, 0xda, 0x00, 0x08, 0x01, 0x03, 0x01, - 0x01, 0x3f, 0x01, 0xad, 0x35, 0xa6, 0x13, 0xb8, - 0x10, 0x98, 0x5d, 0x8a, 0xb1, 0x41, 0x7e, 0x43, - 0x99, 0x24, 0x3d, 0x8f, 0x70, 0x30, 0xd8, 0xcb, - 0x44, 0xbb, 0x7d, 0x48, 0xb5, 0xf8, 0x18, 0x7f, - 0xe7, 0xc1, 0x9f, 0x86, 0x45, 0x9b, 0xfa, 0xf1, - 0x61, 0x96, 0x46, 0xbf, 0x56, 0xc8, 0x8b, 0x2b, - 0x0b, 0x35, 0x6e, 0x4b, 0x8a, 0x95, 0x6a, 0xf9, - 0xff, 0x00, 0xff, 0xc4, 0x00, 0x1f, 0x11, 0x00, - 0x01, 0x04, 0x02, 0x02, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, - 0x02, 0x04, 0x05, 0x12, 0x13, 0x14, 0x01, 0x06, - 0x11, 0x22, 0x23, 0xff, 0xda, 0x00, 0x08, 0x01, - 0x02, 0x01, 0x01, 0x3f, 0x01, 0x85, 0x85, 0x8c, - 0xec, 0x31, 0x8d, 0xa6, 0x26, 0x1b, 0x6e, 0x48, - 0xbc, 0xcd, 0xb0, 0xe3, 0x33, 0x86, 0xf9, 0x35, - 0xdc, 0x15, 0xa8, 0xbe, 0x4d, 0x4a, 0x10, 0x22, - 0x80, 0x00, 0x91, 0xe8, 0x24, 0xda, 0xb6, 0x57, - 0x95, 0xf2, 0xa5, 0x73, 0xff, 0xc4, 0x00, 0x1e, - 0x10, 0x00, 0x01, 0x04, 0x03, 0x00, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x01, 0x02, 0x04, 0x12, 0x05, 0x11, - 0x13, 0x14, 0x22, 0x23, 0xff, 0xda, 0x00, 0x08, - 0x01, 0x01, 0x00, 0x06, 0x3f, 0x02, 0x91, 0x89, - 0xc4, 0xc8, 0xf1, 0x60, 0x45, 0xe5, 0xc0, 0x1c, - 0x80, 0x7a, 0x77, 0x00, 0xe4, 0x97, 0xeb, 0x24, - 0x66, 0x33, 0xac, 0x63, 0x11, 0xfe, 0xe4, 0x76, - 0xad, 0x56, 0xe9, 0xa8, 0x88, 0x9f, 0xff, 0xc4, - 0x00, 0x14, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xda, 0x00, 0x08, - 0x01, 0x01, 0x00, 0x01, 0x3f, 0x21, 0x68, 0x3f, - 0x92, 0x17, 0x81, 0x1f, 0x7f, 0xff, 0xda, 0x00, - 0x0c, 0x03, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, - 0x00, 0x00, 0x10, 0x5f, 0xff, 0xc4, 0x00, 0x14, - 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xff, 0xda, 0x00, 0x08, 0x01, 0x03, - 0x01, 0x01, 0x3f, 0x10, 0x03, 0xeb, 0x11, 0xe4, - 0xa7, 0xe3, 0xff, 0x00, 0xff, 0xc4, 0x00, 0x14, - 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xff, 0xda, 0x00, 0x08, 0x01, 0x02, - 0x01, 0x01, 0x3f, 0x10, 0x6b, 0xd3, 0x02, 0xdc, - 0x9a, 0xf4, 0xff, 0x00, 0xff, 0xc4, 0x00, 0x14, - 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01, - 0x00, 0x01, 0x3f, 0x10, 0x62, 0x7b, 0x3a, 0xd0, - 0x3f, 0xeb, 0xff, 0x00, 0xff, 0xd9, - ]; - - let decodeDone = testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); - expect(decodeDone).to.be.true; - - let targetData = new Uint8Array([ - 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255 - ]); - - // Browsers have rounding errors, so we need an approximate - // comparing function - function almost(a, b) { - let diff = Math.abs(a - b); - return diff < 5; - } - - await display.flush(); - expect(display).to.have.displayed(targetData, almost); - }); -}); diff --git a/base/app/novnc/tests/test.tightpng.js b/base/app/novnc/tests/test.tightpng.js deleted file mode 100644 index 02c66d9..0000000 --- a/base/app/novnc/tests/test.tightpng.js +++ /dev/null @@ -1,145 +0,0 @@ -const expect = chai.expect; - -import Websock from '../core/websock.js'; -import Display from '../core/display.js'; - -import TightPngDecoder from '../core/decoders/tightpng.js'; - -import FakeWebSocket from './fake.websocket.js'; - -function testDecodeRect(decoder, x, y, width, height, data, display, depth) { - let sock; - let done = false; - - sock = new Websock; - sock.open("ws://example.com"); - - sock.on('message', () => { - done = decoder.decodeRect(x, y, width, height, sock, display, depth); - }); - - // Empty messages are filtered at multiple layers, so we need to - // do a direct call - if (data.length === 0) { - done = decoder.decodeRect(x, y, width, height, sock, display, depth); - } else { - sock._websocket._receiveData(new Uint8Array(data)); - } - - display.flip(); - - return done; -} - -describe('TightPng Decoder', function () { - let decoder; - let display; - - before(FakeWebSocket.replace); - after(FakeWebSocket.restore); - - beforeEach(function () { - decoder = new TightPngDecoder(); - display = new Display(document.createElement('canvas')); - display.resize(4, 4); - }); - - it('should handle the TightPng encoding', async function () { - let data = [ - // Control bytes - 0xa0, 0xb4, 0x04, - // PNG data - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, - 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, - 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, - 0x08, 0x02, 0x00, 0x00, 0x00, 0x26, 0x93, 0x09, - 0x29, 0x00, 0x00, 0x01, 0x84, 0x69, 0x43, 0x43, - 0x50, 0x49, 0x43, 0x43, 0x20, 0x70, 0x72, 0x6f, - 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x28, 0x91, - 0x7d, 0x91, 0x3d, 0x48, 0xc3, 0x40, 0x18, 0x86, - 0xdf, 0xa6, 0x6a, 0x45, 0x2a, 0x0e, 0x76, 0x10, - 0x71, 0x08, 0x52, 0x9d, 0x2c, 0x88, 0x8a, 0x38, - 0x6a, 0x15, 0x8a, 0x50, 0x21, 0xd4, 0x0a, 0xad, - 0x3a, 0x98, 0x5c, 0xfa, 0x07, 0x4d, 0x1a, 0x92, - 0x14, 0x17, 0x47, 0xc1, 0xb5, 0xe0, 0xe0, 0xcf, - 0x62, 0xd5, 0xc1, 0xc5, 0x59, 0x57, 0x07, 0x57, - 0x41, 0x10, 0xfc, 0x01, 0x71, 0x72, 0x74, 0x52, - 0x74, 0x91, 0x12, 0xbf, 0x4b, 0x0a, 0x2d, 0x62, - 0xbc, 0xe3, 0xb8, 0x87, 0xf7, 0xbe, 0xf7, 0xe5, - 0xee, 0x3b, 0x40, 0xa8, 0x97, 0x99, 0x66, 0x75, - 0x8c, 0x03, 0x9a, 0x6e, 0x9b, 0xa9, 0x44, 0x5c, - 0xcc, 0x64, 0x57, 0xc5, 0xd0, 0x2b, 0xba, 0x68, - 0x86, 0x31, 0x8c, 0x2e, 0x99, 0x59, 0xc6, 0x9c, - 0x24, 0x25, 0xe1, 0x3b, 0xbe, 0xee, 0x11, 0xe0, - 0xfb, 0x5d, 0x8c, 0x67, 0xf9, 0xd7, 0xfd, 0x39, - 0x7a, 0xd5, 0x9c, 0xc5, 0x80, 0x80, 0x48, 0x3c, - 0xcb, 0x0c, 0xd3, 0x26, 0xde, 0x20, 0x9e, 0xde, - 0xb4, 0x0d, 0xce, 0xfb, 0xc4, 0x11, 0x56, 0x94, - 0x55, 0xe2, 0x73, 0xe2, 0x31, 0x93, 0x2e, 0x48, - 0xfc, 0xc8, 0x75, 0xc5, 0xe3, 0x37, 0xce, 0x05, - 0x97, 0x05, 0x9e, 0x19, 0x31, 0xd3, 0xa9, 0x79, - 0xe2, 0x08, 0xb1, 0x58, 0x68, 0x63, 0xa5, 0x8d, - 0x59, 0xd1, 0xd4, 0x88, 0xa7, 0x88, 0xa3, 0xaa, - 0xa6, 0x53, 0xbe, 0x90, 0xf1, 0x58, 0xe5, 0xbc, - 0xc5, 0x59, 0x2b, 0x57, 0x59, 0xf3, 0x9e, 0xfc, - 0x85, 0xe1, 0x9c, 0xbe, 0xb2, 0xcc, 0x75, 0x5a, - 0x43, 0x48, 0x60, 0x11, 0x4b, 0x90, 0x20, 0x42, - 0x41, 0x15, 0x25, 0x94, 0x61, 0x23, 0x46, 0xbb, - 0x4e, 0x8a, 0x85, 0x14, 0x9d, 0xc7, 0x7d, 0xfc, - 0x83, 0xae, 0x5f, 0x22, 0x97, 0x42, 0xae, 0x12, - 0x18, 0x39, 0x16, 0x50, 0x81, 0x06, 0xd9, 0xf5, - 0x83, 0xff, 0xc1, 0xef, 0xde, 0x5a, 0xf9, 0xc9, - 0x09, 0x2f, 0x29, 0x1c, 0x07, 0x3a, 0x5f, 0x1c, - 0xe7, 0x63, 0x04, 0x08, 0xed, 0x02, 0x8d, 0x9a, - 0xe3, 0x7c, 0x1f, 0x3b, 0x4e, 0xe3, 0x04, 0x08, - 0x3e, 0x03, 0x57, 0x7a, 0xcb, 0x5f, 0xa9, 0x03, - 0x33, 0x9f, 0xa4, 0xd7, 0x5a, 0x5a, 0xf4, 0x08, - 0xe8, 0xdb, 0x06, 0x2e, 0xae, 0x5b, 0x9a, 0xb2, - 0x07, 0x5c, 0xee, 0x00, 0x03, 0x4f, 0x86, 0x6c, - 0xca, 0xae, 0x14, 0xa4, 0x25, 0xe4, 0xf3, 0xc0, - 0xfb, 0x19, 0x7d, 0x53, 0x16, 0xe8, 0xbf, 0x05, - 0x7a, 0xd6, 0xbc, 0xbe, 0x35, 0xcf, 0x71, 0xfa, - 0x00, 0xa4, 0xa9, 0x57, 0xc9, 0x1b, 0xe0, 0xe0, - 0x10, 0x18, 0x2d, 0x50, 0xf6, 0xba, 0xcf, 0xbb, - 0xbb, 0xdb, 0xfb, 0xf6, 0x6f, 0x4d, 0xb3, 0x7f, - 0x3f, 0x0a, 0x27, 0x72, 0x7d, 0x49, 0x29, 0x8b, - 0xbb, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, - 0x73, 0x00, 0x00, 0x2e, 0x23, 0x00, 0x00, 0x2e, - 0x23, 0x01, 0x78, 0xa5, 0x3f, 0x76, 0x00, 0x00, - 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xe4, - 0x06, 0x06, 0x0c, 0x23, 0x1d, 0x3f, 0x9f, 0xbb, - 0x94, 0x00, 0x00, 0x00, 0x19, 0x74, 0x45, 0x58, - 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, - 0x00, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, - 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, - 0x4d, 0x50, 0x57, 0x81, 0x0e, 0x17, 0x00, 0x00, - 0x00, 0x1e, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, - 0x65, 0xc9, 0xb1, 0x0d, 0x00, 0x00, 0x08, 0x03, - 0x20, 0xea, 0xff, 0x3f, 0xd7, 0xd5, 0x44, 0x56, - 0x52, 0x90, 0xc2, 0x38, 0xa2, 0xd0, 0xbc, 0x59, - 0x8a, 0x9f, 0x04, 0x05, 0x6b, 0x38, 0x7b, 0xb2, - 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, - 0xae, 0x42, 0x60, 0x82, - ]; - - let decodeDone = testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24); - expect(decodeDone).to.be.true; - - let targetData = new Uint8Array([ - 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255, - 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0xff, 0x00, 0x00, 255, 0xff, 0x00, 0x00, 255 - ]); - - // Firefox currently has some very odd rounding bug: - // https://bugzilla.mozilla.org/show_bug.cgi?id=1667747 - function almost(a, b) { - let diff = Math.abs(a - b); - return diff < 30; - } - - await display.flush(); - expect(display).to.have.displayed(targetData, almost); - }); -}); diff --git a/base/app/novnc/tests/test.util.js b/base/app/novnc/tests/test.util.js deleted file mode 100644 index cd61f24..0000000 --- a/base/app/novnc/tests/test.util.js +++ /dev/null @@ -1,89 +0,0 @@ -/* eslint-disable no-console */ -const expect = chai.expect; - -import * as Log from '../core/util/logging.js'; -import { encodeUTF8, decodeUTF8 } from '../core/util/strings.js'; - -describe('Utils', function () { - "use strict"; - - describe('logging functions', function () { - beforeEach(function () { - sinon.spy(console, 'log'); - sinon.spy(console, 'debug'); - sinon.spy(console, 'warn'); - sinon.spy(console, 'error'); - sinon.spy(console, 'info'); - }); - - afterEach(function () { - console.log.restore(); - console.debug.restore(); - console.warn.restore(); - console.error.restore(); - console.info.restore(); - Log.initLogging(); - }); - - it('should use noop for levels lower than the min level', function () { - Log.initLogging('warn'); - Log.Debug('hi'); - Log.Info('hello'); - expect(console.log).to.not.have.been.called; - }); - - it('should use console.debug for Debug', function () { - Log.initLogging('debug'); - Log.Debug('dbg'); - expect(console.debug).to.have.been.calledWith('dbg'); - }); - - it('should use console.info for Info', function () { - Log.initLogging('debug'); - Log.Info('inf'); - expect(console.info).to.have.been.calledWith('inf'); - }); - - it('should use console.warn for Warn', function () { - Log.initLogging('warn'); - Log.Warn('wrn'); - expect(console.warn).to.have.been.called; - expect(console.warn).to.have.been.calledWith('wrn'); - }); - - it('should use console.error for Error', function () { - Log.initLogging('error'); - Log.Error('err'); - expect(console.error).to.have.been.called; - expect(console.error).to.have.been.calledWith('err'); - }); - }); - - describe('string functions', function () { - it('should decode UTF-8 to DOMString correctly', function () { - const utf8string = '\xd0\x9f'; - const domstring = decodeUTF8(utf8string); - expect(domstring).to.equal("П"); - }); - - it('should encode DOMString to UTF-8 correctly', function () { - const domstring = "åäöa"; - const utf8string = encodeUTF8(domstring); - expect(utf8string).to.equal('\xc3\xa5\xc3\xa4\xc3\xb6\x61'); - }); - - it('should allow Latin-1 strings if allowLatin1 is set when decoding', function () { - const latin1string = '\xe5\xe4\xf6'; - expect(() => decodeUTF8(latin1string)).to.throw(Error); - expect(decodeUTF8(latin1string, true)).to.equal('åäö'); - }); - }); - - // TODO(directxman12): test the conf_default and conf_defaults methods - // TODO(directxman12): test the event methods (addEvent, removeEvent, stopEvent) - // TODO(directxman12): figure out a good way to test getPosition and getEventPosition - // TODO(directxman12): figure out how to test the browser detection functions properly - // (we can't really test them against the browsers, except for Gecko - // via PhantomJS, the default test driver) -}); -/* eslint-enable no-console */ diff --git a/base/app/novnc/tests/test.websock.js b/base/app/novnc/tests/test.websock.js deleted file mode 100644 index dc361b7..0000000 --- a/base/app/novnc/tests/test.websock.js +++ /dev/null @@ -1,628 +0,0 @@ -const expect = chai.expect; - -import Websock from '../core/websock.js'; -import FakeWebSocket from './fake.websocket.js'; - -describe('Websock', function () { - "use strict"; - - describe('Receive queue methods', function () { - let sock, websock; - - beforeEach(function () { - sock = new Websock(); - websock = new FakeWebSocket(); - websock._open(); - sock.attach(websock); - }); - - describe('rQpeek8', function () { - it('should peek at the next byte without poping it off the queue', function () { - websock._receiveData(new Uint8Array([0xab, 0xcd])); - expect(sock.rQpeek8()).to.equal(0xab); - expect(sock.rQpeek8()).to.equal(0xab); - }); - }); - - describe('rQshift8()', function () { - it('should pop a single byte from the receive queue', function () { - websock._receiveData(new Uint8Array([0xab, 0xcd])); - expect(sock.rQshift8()).to.equal(0xab); - expect(sock.rQshift8()).to.equal(0xcd); - }); - }); - - describe('rQshift16()', function () { - it('should pop two bytes from the receive queue and return a single number', function () { - websock._receiveData(new Uint8Array([0xab, 0xcd, 0x12, 0x34])); - expect(sock.rQshift16()).to.equal(0xabcd); - expect(sock.rQshift16()).to.equal(0x1234); - }); - }); - - describe('rQshift32()', function () { - it('should pop four bytes from the receive queue and return a single number', function () { - websock._receiveData(new Uint8Array([0xab, 0xcd, 0x12, 0x34, - 0x88, 0xee, 0x11, 0x33])); - expect(sock.rQshift32()).to.equal(0xabcd1234); - expect(sock.rQshift32()).to.equal(0x88ee1133); - }); - }); - - describe('rQshiftStr', function () { - it('should shift the given number of bytes off of the receive queue and return a string', function () { - websock._receiveData(new Uint8Array([0xab, 0xcd, 0x12, 0x34, - 0x88, 0xee, 0x11, 0x33])); - expect(sock.rQshiftStr(4)).to.equal('\xab\xcd\x12\x34'); - expect(sock.rQshiftStr(4)).to.equal('\x88\xee\x11\x33'); - }); - - it('should be able to handle very large strings', function () { - const BIG_LEN = 500000; - const incoming = new Uint8Array(BIG_LEN); - let expected = ""; - let letterCode = 'a'.charCodeAt(0); - for (let i = 0; i < BIG_LEN; i++) { - incoming[i] = letterCode; - expected += String.fromCharCode(letterCode); - - if (letterCode < 'z'.charCodeAt(0)) { - letterCode++; - } else { - letterCode = 'a'.charCodeAt(0); - } - } - websock._receiveData(incoming); - - const shifted = sock.rQshiftStr(BIG_LEN); - - expect(shifted).to.be.equal(expected); - }); - }); - - describe('rQshiftBytes', function () { - it('should shift the given number of bytes of the receive queue and return an array', function () { - websock._receiveData(new Uint8Array([0xab, 0xcd, 0x12, 0x34, - 0x88, 0xee, 0x11, 0x33])); - expect(sock.rQshiftBytes(4)).to.array.equal(new Uint8Array([0xab, 0xcd, 0x12, 0x34])); - expect(sock.rQshiftBytes(4)).to.array.equal(new Uint8Array([0x88, 0xee, 0x11, 0x33])); - }); - - it('should return a shared array if requested', function () { - websock._receiveData(new Uint8Array([0xab, 0xcd, 0x12, 0x34, - 0x88, 0xee, 0x11, 0x33])); - const bytes = sock.rQshiftBytes(4, false); - expect(bytes).to.array.equal(new Uint8Array([0xab, 0xcd, 0x12, 0x34])); - expect(bytes.buffer.byteLength).to.not.equal(bytes.length); - }); - }); - - describe('rQpeekBytes', function () { - it('should not modify the receive queue', function () { - websock._receiveData(new Uint8Array([0xab, 0xcd, 0x12, 0x34, - 0x88, 0xee, 0x11, 0x33])); - expect(sock.rQpeekBytes(4)).to.array.equal(new Uint8Array([0xab, 0xcd, 0x12, 0x34])); - expect(sock.rQpeekBytes(4)).to.array.equal(new Uint8Array([0xab, 0xcd, 0x12, 0x34])); - }); - - it('should return a shared array if requested', function () { - websock._receiveData(new Uint8Array([0xab, 0xcd, 0x12, 0x34, - 0x88, 0xee, 0x11, 0x33])); - const bytes = sock.rQpeekBytes(4, false); - expect(bytes).to.array.equal(new Uint8Array([0xab, 0xcd, 0x12, 0x34])); - expect(bytes.buffer.byteLength).to.not.equal(bytes.length); - }); - }); - - describe('rQwait', function () { - beforeEach(function () { - websock._receiveData(new Uint8Array([0xab, 0xcd, 0x12, 0x34, - 0x88, 0xee, 0x11, 0x33])); - }); - - it('should return true if there are not enough bytes in the receive queue', function () { - expect(sock.rQwait('hi', 9)).to.be.true; - }); - - it('should return false if there are enough bytes in the receive queue', function () { - expect(sock.rQwait('hi', 8)).to.be.false; - }); - - it('should return true and reduce rQi by "goback" if there are not enough bytes', function () { - expect(sock.rQshift32()).to.equal(0xabcd1234); - expect(sock.rQwait('hi', 8, 2)).to.be.true; - expect(sock.rQshift32()).to.equal(0x123488ee); - }); - - it('should raise an error if we try to go back more than possible', function () { - expect(sock.rQshift32()).to.equal(0xabcd1234); - expect(() => sock.rQwait('hi', 8, 6)).to.throw(Error); - }); - - it('should not reduce rQi if there are enough bytes', function () { - expect(sock.rQshift32()).to.equal(0xabcd1234); - expect(sock.rQwait('hi', 4, 2)).to.be.false; - expect(sock.rQshift32()).to.equal(0x88ee1133); - }); - }); - }); - - describe('Send queue methods', function () { - let sock; - - const bufferSize = 10 * 1024; - - beforeEach(function () { - let websock = new FakeWebSocket(); - websock._open(); - sock = new Websock(); - sock.attach(websock); - }); - - describe('sQpush8()', function () { - it('should send a single byte', function () { - sock.sQpush8(42); - sock.flush(); - expect(sock).to.have.sent(new Uint8Array([42])); - }); - it('should not send any data until flushing', function () { - sock.sQpush8(42); - expect(sock).to.have.sent(new Uint8Array([])); - }); - it('should implicitly flush if the queue is full', function () { - for (let i = 0;i <= bufferSize;i++) { - sock.sQpush8(42); - } - - let expected = []; - for (let i = 0;i < bufferSize;i++) { - expected.push(42); - } - - expect(sock).to.have.sent(new Uint8Array(expected)); - }); - }); - - describe('sQpush16()', function () { - it('should send a number as two bytes', function () { - sock.sQpush16(420); - sock.flush(); - expect(sock).to.have.sent(new Uint8Array([1, 164])); - }); - it('should not send any data until flushing', function () { - sock.sQpush16(420); - expect(sock).to.have.sent(new Uint8Array([])); - }); - it('should implicitly flush if the queue is full', function () { - for (let i = 0;i <= bufferSize/2;i++) { - sock.sQpush16(420); - } - - let expected = []; - for (let i = 0;i < bufferSize/2;i++) { - expected.push(1); - expected.push(164); - } - - expect(sock).to.have.sent(new Uint8Array(expected)); - }); - }); - - describe('sQpush32()', function () { - it('should send a number as two bytes', function () { - sock.sQpush32(420420); - sock.flush(); - expect(sock).to.have.sent(new Uint8Array([0, 6, 106, 68])); - }); - it('should not send any data until flushing', function () { - sock.sQpush32(420420); - expect(sock).to.have.sent(new Uint8Array([])); - }); - it('should implicitly flush if the queue is full', function () { - for (let i = 0;i <= bufferSize/4;i++) { - sock.sQpush32(420420); - } - - let expected = []; - for (let i = 0;i < bufferSize/4;i++) { - expected.push(0); - expected.push(6); - expected.push(106); - expected.push(68); - } - - expect(sock).to.have.sent(new Uint8Array(expected)); - }); - }); - - describe('sQpushString()', function () { - it('should send a string buffer', function () { - sock.sQpushString('\x12\x34\x56\x78\x90'); - sock.flush(); - expect(sock).to.have.sent(new Uint8Array([0x12, 0x34, 0x56, 0x78, 0x90])); - }); - it('should not send any data until flushing', function () { - sock.sQpushString('\x12\x34\x56\x78\x90'); - expect(sock).to.have.sent(new Uint8Array([])); - }); - it('should implicitly flush if the queue is full', function () { - for (let i = 0;i <= bufferSize/5;i++) { - sock.sQpushString('\x12\x34\x56\x78\x90'); - } - - let expected = []; - for (let i = 0;i < bufferSize/5;i++) { - expected.push(0x12); - expected.push(0x34); - expected.push(0x56); - expected.push(0x78); - expected.push(0x90); - } - - expect(sock).to.have.sent(new Uint8Array(expected)); - }); - it('should implicitly split a large buffer', function () { - let str = ''; - for (let i = 0;i <= bufferSize/5;i++) { - str += '\x12\x34\x56\x78\x90'; - } - - sock.sQpushString(str); - - let expected = []; - for (let i = 0;i < bufferSize/5;i++) { - expected.push(0x12); - expected.push(0x34); - expected.push(0x56); - expected.push(0x78); - expected.push(0x90); - } - - expect(sock).to.have.sent(new Uint8Array(expected)); - }); - }); - - describe('sQpushBytes()', function () { - it('should send a byte buffer', function () { - sock.sQpushBytes(new Uint8Array([0x12, 0x34, 0x56, 0x78, 0x90])); - sock.flush(); - expect(sock).to.have.sent(new Uint8Array([0x12, 0x34, 0x56, 0x78, 0x90])); - }); - it('should not send any data until flushing', function () { - sock.sQpushBytes(new Uint8Array([0x12, 0x34, 0x56, 0x78, 0x90])); - expect(sock).to.have.sent(new Uint8Array([])); - }); - it('should implicitly flush if the queue is full', function () { - for (let i = 0;i <= bufferSize/5;i++) { - sock.sQpushBytes(new Uint8Array([0x12, 0x34, 0x56, 0x78, 0x90])); - } - - let expected = []; - for (let i = 0;i < bufferSize/5;i++) { - expected.push(0x12); - expected.push(0x34); - expected.push(0x56); - expected.push(0x78); - expected.push(0x90); - } - - expect(sock).to.have.sent(new Uint8Array(expected)); - }); - it('should implicitly split a large buffer', function () { - let buffer = []; - for (let i = 0;i <= bufferSize/5;i++) { - buffer.push(0x12); - buffer.push(0x34); - buffer.push(0x56); - buffer.push(0x78); - buffer.push(0x90); - } - - sock.sQpushBytes(new Uint8Array(buffer)); - - let expected = []; - for (let i = 0;i < bufferSize/5;i++) { - expected.push(0x12); - expected.push(0x34); - expected.push(0x56); - expected.push(0x78); - expected.push(0x90); - } - - expect(sock).to.have.sent(new Uint8Array(expected)); - }); - }); - - describe('flush', function () { - it('should actually send on the websocket', function () { - sock._sQ = new Uint8Array([1, 2, 3]); - sock._sQlen = 3; - - sock.flush(); - expect(sock).to.have.sent(new Uint8Array([1, 2, 3])); - }); - - it('should not call send if we do not have anything queued up', function () { - sock._sQlen = 0; - - sock.flush(); - - expect(sock).to.have.sent(new Uint8Array([])); - }); - }); - }); - - describe('lifecycle methods', function () { - let oldWS; - before(function () { - oldWS = WebSocket; - }); - - let sock; - beforeEach(function () { - sock = new Websock(); - // eslint-disable-next-line no-global-assign - WebSocket = sinon.spy(FakeWebSocket); - }); - - describe('opening', function () { - it('should pick the correct protocols if none are given', function () { - - }); - - it('should open the actual websocket', function () { - sock.open('ws://localhost:8675', 'binary'); - expect(WebSocket).to.have.been.calledWith('ws://localhost:8675', 'binary'); - }); - - // it('should initialize the event handlers')? - }); - - describe('attaching', function () { - it('should attach to an existing websocket', function () { - let ws = new FakeWebSocket('ws://localhost:8675'); - sock.attach(ws); - expect(WebSocket).to.not.have.been.called; - }); - }); - - describe('closing', function () { - beforeEach(function () { - sock.open('ws://localhost'); - sock._websocket.close = sinon.spy(); - }); - - it('should close the actual websocket if it is open', function () { - sock._websocket.readyState = WebSocket.OPEN; - sock.close(); - expect(sock._websocket.close).to.have.been.calledOnce; - }); - - it('should close the actual websocket if it is connecting', function () { - sock._websocket.readyState = WebSocket.CONNECTING; - sock.close(); - expect(sock._websocket.close).to.have.been.calledOnce; - }); - - it('should not try to close the actual websocket if closing', function () { - sock._websocket.readyState = WebSocket.CLOSING; - sock.close(); - expect(sock._websocket.close).not.to.have.been.called; - }); - - it('should not try to close the actual websocket if closed', function () { - sock._websocket.readyState = WebSocket.CLOSED; - sock.close(); - expect(sock._websocket.close).not.to.have.been.called; - }); - - it('should reset onmessage to not call _recvMessage', function () { - sinon.spy(sock, '_recvMessage'); - sock.close(); - sock._websocket.onmessage(null); - try { - expect(sock._recvMessage).not.to.have.been.called; - } finally { - sock._recvMessage.restore(); - } - }); - }); - - describe('event handlers', function () { - beforeEach(function () { - sock._recvMessage = sinon.spy(); - sock.on('open', sinon.spy()); - sock.on('close', sinon.spy()); - sock.on('error', sinon.spy()); - sock.open('ws://localhost'); - }); - - it('should call _recvMessage on a message', function () { - sock._websocket.onmessage(null); - expect(sock._recvMessage).to.have.been.calledOnce; - }); - - it('should call the open event handler on opening', function () { - sock._websocket.onopen(); - expect(sock._eventHandlers.open).to.have.been.calledOnce; - }); - - it('should call the close event handler on closing', function () { - sock._websocket.onclose(); - expect(sock._eventHandlers.close).to.have.been.calledOnce; - }); - - it('should call the error event handler on error', function () { - sock._websocket.onerror(); - expect(sock._eventHandlers.error).to.have.been.calledOnce; - }); - }); - - describe('ready state', function () { - it('should be "unused" after construction', function () { - let sock = new Websock(); - expect(sock.readyState).to.equal('unused'); - }); - - it('should be "connecting" if WebSocket is connecting', function () { - let sock = new Websock(); - let ws = new FakeWebSocket(); - ws.readyState = WebSocket.CONNECTING; - sock.attach(ws); - expect(sock.readyState).to.equal('connecting'); - }); - - it('should be "open" if WebSocket is open', function () { - let sock = new Websock(); - let ws = new FakeWebSocket(); - ws.readyState = WebSocket.OPEN; - sock.attach(ws); - expect(sock.readyState).to.equal('open'); - }); - - it('should be "closing" if WebSocket is closing', function () { - let sock = new Websock(); - let ws = new FakeWebSocket(); - ws.readyState = WebSocket.CLOSING; - sock.attach(ws); - expect(sock.readyState).to.equal('closing'); - }); - - it('should be "closed" if WebSocket is closed', function () { - let sock = new Websock(); - let ws = new FakeWebSocket(); - ws.readyState = WebSocket.CLOSED; - sock.attach(ws); - expect(sock.readyState).to.equal('closed'); - }); - - it('should be "unknown" if WebSocket state is unknown', function () { - let sock = new Websock(); - let ws = new FakeWebSocket(); - ws.readyState = 666; - sock.attach(ws); - expect(sock.readyState).to.equal('unknown'); - }); - - it('should be "connecting" if RTCDataChannel is connecting', function () { - let sock = new Websock(); - let ws = new FakeWebSocket(); - ws.readyState = 'connecting'; - sock.attach(ws); - expect(sock.readyState).to.equal('connecting'); - }); - - it('should be "open" if RTCDataChannel is open', function () { - let sock = new Websock(); - let ws = new FakeWebSocket(); - ws.readyState = 'open'; - sock.attach(ws); - expect(sock.readyState).to.equal('open'); - }); - - it('should be "closing" if RTCDataChannel is closing', function () { - let sock = new Websock(); - let ws = new FakeWebSocket(); - ws.readyState = 'closing'; - sock.attach(ws); - expect(sock.readyState).to.equal('closing'); - }); - - it('should be "closed" if RTCDataChannel is closed', function () { - let sock = new Websock(); - let ws = new FakeWebSocket(); - ws.readyState = 'closed'; - sock.attach(ws); - expect(sock.readyState).to.equal('closed'); - }); - - it('should be "unknown" if RTCDataChannel state is unknown', function () { - let sock = new Websock(); - let ws = new FakeWebSocket(); - ws.readyState = 'foobar'; - sock.attach(ws); - expect(sock.readyState).to.equal('unknown'); - }); - }); - - after(function () { - // eslint-disable-next-line no-global-assign - WebSocket = oldWS; - }); - }); - - describe('WebSocket Receiving', function () { - let sock; - beforeEach(function () { - sock = new Websock(); - sock._allocateBuffers(); - }); - - it('should support adding data to the receive queue', function () { - const msg = { data: new Uint8Array([1, 2, 3]) }; - sock._recvMessage(msg); - expect(sock.rQshiftStr(3)).to.equal('\x01\x02\x03'); - }); - - it('should call the message event handler if present', function () { - sock._eventHandlers.message = sinon.spy(); - const msg = { data: new Uint8Array([1, 2, 3]).buffer }; - sock._mode = 'binary'; - sock._recvMessage(msg); - expect(sock._eventHandlers.message).to.have.been.calledOnce; - }); - - it('should not call the message event handler if there is nothing in the receive queue', function () { - sock._eventHandlers.message = sinon.spy(); - const msg = { data: new Uint8Array([]).buffer }; - sock._mode = 'binary'; - sock._recvMessage(msg); - expect(sock._eventHandlers.message).not.to.have.been.called; - }); - - it('should compact the receive queue when fully read', function () { - sock._rQ = new Uint8Array([0, 1, 2, 3, 4, 5, 0, 0, 0, 0]); - sock._rQlen = 6; - sock._rQi = 6; - const msg = { data: new Uint8Array([1, 2, 3]).buffer }; - sock._recvMessage(msg); - expect(sock._rQlen).to.equal(3); - expect(sock._rQi).to.equal(0); - }); - - it('should compact the receive queue when we reach the end of the buffer', function () { - sock._rQ = new Uint8Array(20); - sock._rQbufferSize = 20; - sock._rQlen = 20; - sock._rQi = 10; - const msg = { data: new Uint8Array([1, 2]).buffer }; - sock._recvMessage(msg); - expect(sock._rQlen).to.equal(12); - expect(sock._rQi).to.equal(0); - }); - - it('should automatically resize the receive queue if the incoming message is larger than the buffer', function () { - sock._rQ = new Uint8Array(20); - sock._rQlen = 0; - sock._rQi = 0; - sock._rQbufferSize = 20; - const msg = { data: new Uint8Array(30).buffer }; - sock._recvMessage(msg); - expect(sock._rQlen).to.equal(30); - expect(sock._rQi).to.equal(0); - expect(sock._rQ.length).to.equal(240); // keep the invariant that rQbufferSize / 8 >= rQlen - }); - - it('should automatically resize the receive queue if the incoming message is larger than 1/8th of the buffer and we reach the end of the buffer', function () { - sock._rQ = new Uint8Array(20); - sock._rQlen = 16; - sock._rQi = 15; - sock._rQbufferSize = 20; - const msg = { data: new Uint8Array(6).buffer }; - sock._recvMessage(msg); - expect(sock._rQlen).to.equal(7); - expect(sock._rQi).to.equal(0); - expect(sock._rQ.length).to.equal(56); - }); - }); -}); diff --git a/base/app/novnc/tests/test.webutil.js b/base/app/novnc/tests/test.webutil.js deleted file mode 100644 index df8227a..0000000 --- a/base/app/novnc/tests/test.webutil.js +++ /dev/null @@ -1,240 +0,0 @@ -/* jshint expr: true */ - -const expect = chai.expect; - -import * as WebUtil from '../app/webutil.js'; - -describe('WebUtil', function () { - "use strict"; - - describe('config variables', function () { - let origState, origHref; - beforeEach(function () { - origState = history.state; - origHref = location.href; - }); - afterEach(function () { - history.replaceState(origState, '', origHref); - }); - - it('should parse query string variables', function () { - // history.pushState() will not cause the browser to attempt loading - // the URL, this is exactly what we want here for the tests. - history.replaceState({}, '', "test?myvar=myval"); - expect(WebUtil.getConfigVar("myvar")).to.be.equal("myval"); - }); - it('should return default value when no query match', function () { - history.replaceState({}, '', "test?myvar=myval"); - expect(WebUtil.getConfigVar("other", "def")).to.be.equal("def"); - }); - it('should handle no query match and no default value', function () { - history.replaceState({}, '', "test?myvar=myval"); - expect(WebUtil.getConfigVar("other")).to.be.equal(null); - }); - it('should parse fragment variables', function () { - history.replaceState({}, '', "test#myvar=myval"); - expect(WebUtil.getConfigVar("myvar")).to.be.equal("myval"); - }); - it('should return default value when no fragment match', function () { - history.replaceState({}, '', "test#myvar=myval"); - expect(WebUtil.getConfigVar("other", "def")).to.be.equal("def"); - }); - it('should handle no fragment match and no default value', function () { - history.replaceState({}, '', "test#myvar=myval"); - expect(WebUtil.getConfigVar("other")).to.be.equal(null); - }); - it('should handle both query and fragment', function () { - history.replaceState({}, '', "test?myquery=1#myhash=2"); - expect(WebUtil.getConfigVar("myquery")).to.be.equal("1"); - expect(WebUtil.getConfigVar("myhash")).to.be.equal("2"); - }); - it('should prioritize fragment if both provide same var', function () { - history.replaceState({}, '', "test?myvar=1#myvar=2"); - expect(WebUtil.getConfigVar("myvar")).to.be.equal("2"); - }); - }); - - describe('cookies', function () { - // TODO - }); - - describe('settings', function () { - - describe('localStorage', function () { - let chrome = window.chrome; - before(function () { - chrome = window.chrome; - window.chrome = null; - }); - after(function () { - window.chrome = chrome; - }); - - let origLocalStorage; - beforeEach(function () { - origLocalStorage = Object.getOwnPropertyDescriptor(window, "localStorage"); - - Object.defineProperty(window, "localStorage", {value: {}}); - - window.localStorage.setItem = sinon.stub(); - window.localStorage.getItem = sinon.stub(); - window.localStorage.removeItem = sinon.stub(); - - return WebUtil.initSettings(); - }); - afterEach(function () { - Object.defineProperty(window, "localStorage", origLocalStorage); - }); - - describe('writeSetting', function () { - it('should save the setting value to local storage', function () { - WebUtil.writeSetting('test', 'value'); - expect(window.localStorage.setItem).to.have.been.calledWithExactly('test', 'value'); - expect(WebUtil.readSetting('test')).to.equal('value'); - }); - - it('should not crash when local storage save fails', function () { - localStorage.setItem.throws(new DOMException()); - expect(WebUtil.writeSetting('test', 'value')).to.not.throw; - }); - }); - - describe('setSetting', function () { - it('should update the setting but not save to local storage', function () { - WebUtil.setSetting('test', 'value'); - expect(window.localStorage.setItem).to.not.have.been.called; - expect(WebUtil.readSetting('test')).to.equal('value'); - }); - }); - - describe('readSetting', function () { - it('should read the setting value from local storage', function () { - localStorage.getItem.returns('value'); - expect(WebUtil.readSetting('test')).to.equal('value'); - }); - - it('should return the default value when not in local storage', function () { - expect(WebUtil.readSetting('test', 'default')).to.equal('default'); - }); - - it('should return the cached value even if local storage changed', function () { - localStorage.getItem.returns('value'); - expect(WebUtil.readSetting('test')).to.equal('value'); - localStorage.getItem.returns('something else'); - expect(WebUtil.readSetting('test')).to.equal('value'); - }); - - it('should cache the value even if it is not initially in local storage', function () { - expect(WebUtil.readSetting('test')).to.be.null; - localStorage.getItem.returns('value'); - expect(WebUtil.readSetting('test')).to.be.null; - }); - - it('should return the default value always if the first read was not in local storage', function () { - expect(WebUtil.readSetting('test', 'default')).to.equal('default'); - localStorage.getItem.returns('value'); - expect(WebUtil.readSetting('test', 'another default')).to.equal('another default'); - }); - - it('should return the last local written value', function () { - localStorage.getItem.returns('value'); - expect(WebUtil.readSetting('test')).to.equal('value'); - WebUtil.writeSetting('test', 'something else'); - expect(WebUtil.readSetting('test')).to.equal('something else'); - }); - - it('should not crash when local storage read fails', function () { - localStorage.getItem.throws(new DOMException()); - expect(WebUtil.readSetting('test')).to.not.throw; - }); - }); - - // this doesn't appear to be used anywhere - describe('eraseSetting', function () { - it('should remove the setting from local storage', function () { - WebUtil.eraseSetting('test'); - expect(window.localStorage.removeItem).to.have.been.calledWithExactly('test'); - }); - - it('should not crash when local storage remove fails', function () { - localStorage.removeItem.throws(new DOMException()); - expect(WebUtil.eraseSetting('test')).to.not.throw; - }); - }); - }); - - describe('chrome.storage', function () { - let chrome = window.chrome; - let settings = {}; - before(function () { - chrome = window.chrome; - window.chrome = { - storage: { - sync: { - get(cb) { cb(settings); }, - set() {}, - remove() {} - } - } - }; - }); - after(function () { - window.chrome = chrome; - }); - - const csSandbox = sinon.createSandbox(); - - beforeEach(function () { - settings = {}; - csSandbox.spy(window.chrome.storage.sync, 'set'); - csSandbox.spy(window.chrome.storage.sync, 'remove'); - return WebUtil.initSettings(); - }); - afterEach(function () { - csSandbox.restore(); - }); - - describe('writeSetting', function () { - it('should save the setting value to chrome storage', function () { - WebUtil.writeSetting('test', 'value'); - expect(window.chrome.storage.sync.set).to.have.been.calledWithExactly(sinon.match({ test: 'value' })); - expect(WebUtil.readSetting('test')).to.equal('value'); - }); - }); - - describe('setSetting', function () { - it('should update the setting but not save to chrome storage', function () { - WebUtil.setSetting('test', 'value'); - expect(window.chrome.storage.sync.set).to.not.have.been.called; - expect(WebUtil.readSetting('test')).to.equal('value'); - }); - }); - - describe('readSetting', function () { - it('should read the setting value from chrome storage', function () { - settings.test = 'value'; - expect(WebUtil.readSetting('test')).to.equal('value'); - }); - - it('should return the default value when not in chrome storage', function () { - expect(WebUtil.readSetting('test', 'default')).to.equal('default'); - }); - - it('should return the last local written value', function () { - settings.test = 'value'; - expect(WebUtil.readSetting('test')).to.equal('value'); - WebUtil.writeSetting('test', 'something else'); - expect(WebUtil.readSetting('test')).to.equal('something else'); - }); - }); - - // this doesn't appear to be used anywhere - describe('eraseSetting', function () { - it('should remove the setting from chrome storage', function () { - WebUtil.eraseSetting('test'); - expect(window.chrome.storage.sync.remove).to.have.been.calledWithExactly('test'); - }); - }); - }); - }); -}); diff --git a/base/app/novnc/tests/test.zrle.js b/base/app/novnc/tests/test.zrle.js deleted file mode 100644 index be04640..0000000 --- a/base/app/novnc/tests/test.zrle.js +++ /dev/null @@ -1,144 +0,0 @@ -const expect = chai.expect; - -import Websock from '../core/websock.js'; -import Display from '../core/display.js'; - -import ZRLEDecoder from '../core/decoders/zrle.js'; - -import FakeWebSocket from './fake.websocket.js'; - -function testDecodeRect(decoder, x, y, width, height, data, display, depth) { - let sock; - let done = false; - - sock = new Websock; - sock.open("ws://example.com"); - - sock.on('message', () => { - done = decoder.decodeRect(x, y, width, height, sock, display, depth); - }); - - // Empty messages are filtered at multiple layers, so we need to - // do a direct call - if (data.length === 0) { - done = decoder.decodeRect(x, y, width, height, sock, display, depth); - } else { - sock._websocket._receiveData(new Uint8Array(data)); - } - - display.flip(); - - return done; -} - -describe('ZRLE Decoder', function () { - let decoder; - let display; - - before(FakeWebSocket.replace); - after(FakeWebSocket.restore); - - beforeEach(function () { - decoder = new ZRLEDecoder(); - display = new Display(document.createElement('canvas')); - display.resize(4, 4); - }); - - it('should handle the Raw subencoding', function () { - let done = testDecodeRect(decoder, 0, 0, 4, 4, - [0x00, 0x00, 0x00, 0x0e, 0x78, 0x5e, - 0x62, 0x60, 0x60, 0xf8, 0x4f, 0x12, - 0x02, 0x00, 0x00, 0x00, 0xff, 0xff], - display, 24); - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, - 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, - 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, - 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff - ]); - - expect(done).to.be.true; - expect(display).to.have.displayed(targetData); - }); - - it('should handle the Solid subencoding', function () { - let done = testDecodeRect(decoder, 0, 0, 4, 4, - [0x00, 0x00, 0x00, 0x0c, 0x78, 0x5e, - 0x62, 0x64, 0x60, 0xf8, 0x0f, 0x00, - 0x00, 0x00, 0xff, 0xff], - display, 24); - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, - 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, - 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, - 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff - ]); - - expect(done).to.be.true; - expect(display).to.have.displayed(targetData); - }); - - - it('should handle the Palette Tile subencoding', function () { - let done = testDecodeRect(decoder, 0, 0, 4, 4, - [0x00, 0x00, 0x00, 0x12, 0x78, 0x5E, - 0x62, 0x62, 0x60, 248, 0xff, 0x9F, - 0x01, 0x08, 0x3E, 0x7C, 0x00, 0x00, - 0x00, 0x00, 0xff, 0xff], - display, 24); - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, - 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, - 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, - 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff - ]); - - expect(done).to.be.true; - expect(display).to.have.displayed(targetData); - }); - - it('should handle the RLE Tile subencoding', function () { - let done = testDecodeRect(decoder, 0, 0, 4, 4, - [0x00, 0x00, 0x00, 0x0d, 0x78, 0x5e, - 0x6a, 0x60, 0x60, 0xf8, 0x2f, 0x00, - 0x00, 0x00, 0x00, 0xff, 0xff], - display, 24); - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, - 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, - 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, - 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff - ]); - - expect(done).to.be.true; - expect(display).to.have.displayed(targetData); - }); - - it('should handle the RLE Palette Tile subencoding', function () { - let done = testDecodeRect(decoder, 0, 0, 4, 4, - [0x00, 0x00, 0x00, 0x11, 0x78, 0x5e, - 0x6a, 0x62, 0x60, 0xf8, 0xff, 0x9f, - 0x81, 0xa1, 0x81, 0x1f, 0x00, 0x00, - 0x00, 0xff, 0xff], - display, 24); - - let targetData = new Uint8Array([ - 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, - 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, - 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, - 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff - ]); - - expect(done).to.be.true; - expect(display).to.have.displayed(targetData); - }); - - it('should fail on an invalid subencoding', function () { - let data = [0x00, 0x00, 0x00, 0x0c, 0x78, 0x5e, 0x6a, 0x64, 0x60, 0xf8, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff]; - expect(() => testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24)).to.throw(); - }); -}); diff --git a/base/app/novnc/tests/vnc_playback.html b/base/app/novnc/tests/vnc_playback.html deleted file mode 100644 index ffa6990..0000000 --- a/base/app/novnc/tests/vnc_playback.html +++ /dev/null @@ -1,26 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <title>VNC Playback - - - - - Iterations:   - Perftest:  - Realtime:   - -   - -

- - Results:
- - -

- -
-
Loading
-
- - diff --git a/base/app/novnc/utils/README.md b/base/app/novnc/utils/README.md deleted file mode 100644 index 05d75c9..0000000 --- a/base/app/novnc/utils/README.md +++ /dev/null @@ -1,14 +0,0 @@ -## WebSockets Proxy/Bridge - -Websockify has been forked out into its own project. `novnc_proxy` will -automatically download it here if it is not already present and not -installed as system-wide. - -For more detailed description and usage information please refer to -the [websockify README](https://github.com/novnc/websockify/blob/master/README.md). - -The other versions of websockify (C, Node.js) and the associated test -programs have been moved to -[websockify](https://github.com/novnc/websockify). Websockify was -formerly named wsproxy. - diff --git a/base/app/novnc/utils/b64-to-binary.pl b/base/app/novnc/utils/b64-to-binary.pl deleted file mode 100755 index 280e28c..0000000 --- a/base/app/novnc/utils/b64-to-binary.pl +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env perl -use MIME::Base64; - -for (<>) { - unless (/^'([{}])(\d+)\1(.+?)',$/) { - print; - next; - } - - my ($dir, $amt, $b64) = ($1, $2, $3); - - my $decoded = MIME::Base64::decode($b64) or die "Could not base64-decode line `$_`"; - - my $decoded_escaped = join "", map { "\\x$_" } unpack("(H2)*", $decoded); - - print "'${dir}${amt}${dir}${decoded_escaped}',\n"; -} diff --git a/base/app/novnc/utils/convert.js b/base/app/novnc/utils/convert.js deleted file mode 100755 index 617f4ed..0000000 --- a/base/app/novnc/utils/convert.js +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/env node - -const path = require('path'); -const { program } = require('commander'); -const fs = require('fs'); -const fse = require('fs-extra'); -const babel = require('@babel/core'); - -program - .option('-m, --with-source-maps [type]', 'output source maps when not generating a bundled app (type may be empty for external source maps, inline for inline source maps, or both) ') - .option('--clean', 'clear the lib folder before building') - .parse(process.argv); - -// the various important paths -const paths = { - main: path.resolve(__dirname, '..'), - core: path.resolve(__dirname, '..', 'core'), - vendor: path.resolve(__dirname, '..', 'vendor'), - libDirBase: path.resolve(__dirname, '..', 'lib'), -}; - -// util.promisify requires Node.js 8.x, so we have our own -function promisify(original) { - return function promiseWrap() { - const args = Array.prototype.slice.call(arguments); - return new Promise((resolve, reject) => { - original.apply(this, args.concat((err, value) => { - if (err) return reject(err); - resolve(value); - })); - }); - }; -} - -const writeFile = promisify(fs.writeFile); - -const readdir = promisify(fs.readdir); -const lstat = promisify(fs.lstat); - -const ensureDir = promisify(fse.ensureDir); - -const babelTransformFile = promisify(babel.transformFile); - -// walkDir *recursively* walks directories trees, -// calling the callback for all normal files found. -function walkDir(basePath, cb, filter) { - return readdir(basePath) - .then((files) => { - const paths = files.map(filename => path.join(basePath, filename)); - return Promise.all(paths.map(filepath => lstat(filepath) - .then((stats) => { - if (filter !== undefined && !filter(filepath, stats)) return; - - if (stats.isSymbolicLink()) return; - if (stats.isFile()) return cb(filepath); - if (stats.isDirectory()) return walkDir(filepath, cb, filter); - }))); - }); -} - -function makeLibFiles(sourceMaps) { - // NB: we need to make a copy of babelOpts, since babel sets some defaults on it - const babelOpts = () => ({ - plugins: [], - presets: [ - [ '@babel/preset-env', - { modules: 'commonjs' } ] - ], - ast: false, - sourceMaps: sourceMaps, - }); - - fse.ensureDirSync(paths.libDirBase); - - const outFiles = []; - - const handleDir = (vendorRewrite, inPathBase, filename) => Promise.resolve() - .then(() => { - const outPath = path.join(paths.libDirBase, path.relative(inPathBase, filename)); - - if (path.extname(filename) !== '.js') { - return; // skip non-javascript files - } - return Promise.resolve() - .then(() => ensureDir(path.dirname(outPath))) - .then(() => { - const opts = babelOpts(); - // Adjust for the fact that we move the core files relative - // to the vendor directory - if (vendorRewrite) { - opts.plugins.push(["import-redirect", - {"root": paths.libDirBase, - "redirect": { "vendor/(.+)": "./vendor/$1"}}]); - } - - return babelTransformFile(filename, opts) - .then((res) => { - console.log(`Writing ${outPath}`); - const {map} = res; - let {code} = res; - if (sourceMaps === true) { - // append URL for external source map - code += `\n//# sourceMappingURL=${path.basename(outPath)}.map\n`; - } - outFiles.push(`${outPath}`); - return writeFile(outPath, code) - .then(() => { - if (sourceMaps === true || sourceMaps === 'both') { - console.log(` and ${outPath}.map`); - outFiles.push(`${outPath}.map`); - return writeFile(`${outPath}.map`, JSON.stringify(map)); - } - }); - }); - }); - }); - - Promise.resolve() - .then(() => { - const handler = handleDir.bind(null, false, paths.main); - return walkDir(paths.vendor, handler); - }) - .then(() => { - const handler = handleDir.bind(null, true, paths.core); - return walkDir(paths.core, handler); - }) - .catch((err) => { - console.error(`Failure converting modules: ${err}`); - process.exit(1); - }); -} - -let options = program.opts(); - -if (options.clean) { - console.log(`Removing ${paths.libDirBase}`); - fse.removeSync(paths.libDirBase); -} - -makeLibFiles(options.withSourceMaps); diff --git a/base/app/novnc/utils/genkeysymdef.js b/base/app/novnc/utils/genkeysymdef.js deleted file mode 100755 index f539a0b..0000000 --- a/base/app/novnc/utils/genkeysymdef.js +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/env node -/* - * genkeysymdef: X11 keysymdef.h to JavaScript converter - * Copyright (C) 2018 The noVNC Authors - * Licensed under MPL 2.0 (see LICENSE.txt) - */ - -"use strict"; - -const fs = require('fs'); - -let showHelp = process.argv.length === 2; -let filename; - -for (let i = 2; i < process.argv.length; ++i) { - switch (process.argv[i]) { - case "--help": - case "-h": - showHelp = true; - break; - case "--file": - case "-f": - default: - filename = process.argv[i]; - } -} - -if (!filename) { - showHelp = true; - console.log("Error: No filename specified\n"); -} - -if (showHelp) { - console.log("Parses a *nix keysymdef.h to generate Unicode code point mappings"); - console.log("Usage: node parse.js [options] filename:"); - console.log(" -h [ --help ] Produce this help message"); - console.log(" filename The keysymdef.h file to parse"); - process.exit(0); -} - -const buf = fs.readFileSync(filename); -const str = buf.toString('utf8'); - -const re = /^#define XK_([a-zA-Z_0-9]+)\s+0x([0-9a-fA-F]+)\s*(\/\*\s*(.*)\s*\*\/)?\s*$/m; - -const arr = str.split('\n'); - -const codepoints = {}; - -for (let i = 0; i < arr.length; ++i) { - const result = re.exec(arr[i]); - if (result) { - const keyname = result[1]; - const keysym = parseInt(result[2], 16); - const remainder = result[3]; - - const unicodeRes = /U\+([0-9a-fA-F]+)/.exec(remainder); - if (unicodeRes) { - const unicode = parseInt(unicodeRes[1], 16); - // The first entry is the preferred one - if (!codepoints[unicode]) { - codepoints[unicode] = { keysym: keysym, name: keyname }; - } - } - } -} - -let out = -"/*\n" + -" * Mapping from Unicode codepoints to X11/RFB keysyms\n" + -" *\n" + -" * This file was automatically generated from keysymdef.h\n" + -" * DO NOT EDIT!\n" + -" */\n" + -"\n" + -"/* Functions at the bottom */\n" + -"\n" + -"const codepoints = {\n"; - -function toHex(num) { - let s = num.toString(16); - if (s.length < 4) { - s = ("0000" + s).slice(-4); - } - return "0x" + s; -} - -for (let codepoint in codepoints) { - codepoint = parseInt(codepoint); - - // Latin-1? - if ((codepoint >= 0x20) && (codepoint <= 0xff)) { - continue; - } - - // Handled by the general Unicode mapping? - if ((codepoint | 0x01000000) === codepoints[codepoint].keysym) { - continue; - } - - out += " " + toHex(codepoint) + ": " + - toHex(codepoints[codepoint].keysym) + - ", // XK_" + codepoints[codepoint].name + "\n"; -} - -out += -"};\n" + -"\n" + -"export default {\n" + -" lookup(u) {\n" + -" // Latin-1 is one-to-one mapping\n" + -" if ((u >= 0x20) && (u <= 0xff)) {\n" + -" return u;\n" + -" }\n" + -"\n" + -" // Lookup table (fairly random)\n" + -" const keysym = codepoints[u];\n" + -" if (keysym !== undefined) {\n" + -" return keysym;\n" + -" }\n" + -"\n" + -" // General mapping as final fallback\n" + -" return 0x01000000 | u;\n" + -" },\n" + -"};"; - -console.log(out); diff --git a/base/app/novnc/utils/novnc_proxy b/base/app/novnc/utils/novnc_proxy deleted file mode 100755 index 4b2e303..0000000 --- a/base/app/novnc/utils/novnc_proxy +++ /dev/null @@ -1,234 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (C) 2018 The noVNC Authors -# Licensed under MPL 2.0 or any later version (see LICENSE.txt) - -usage() { - if [ "$*" ]; then - echo "$*" - echo - fi - echo "Usage: ${NAME} [--listen [HOST:]PORT] [--vnc VNC_HOST:PORT] [--cert CERT] [--ssl-only]" - echo - echo "Starts the WebSockets proxy and a mini-webserver and " - echo "provides a cut-and-paste URL to go to." - echo - echo " --listen [HOST:]PORT Port for proxy/webserver to listen on" - echo " Default: 6080 (on all interfaces)" - echo " --vnc VNC_HOST:PORT VNC server host:port proxy target" - echo " Default: localhost:5900" - echo " --cert CERT Path to combined cert/key file, or just" - echo " the cert file if used with --key" - echo " Default: self.pem" - echo " --key KEY Path to key file, when not combined with cert" - echo " --web WEB Path to web files (e.g. vnc.html)" - echo " Default: ./" - echo " --ssl-only Disable non-https connections." - echo " " - echo " --file-only Disable directory listing in web server." - echo " " - echo " --record FILE Record traffic to FILE.session.js" - echo " " - echo " --syslog SERVER Can be local socket such as /dev/log, or a UDP host:port pair." - echo " " - echo " --heartbeat SEC send a ping to the client every SEC seconds" - echo " --timeout SEC after SEC seconds exit when not connected" - echo " --idle-timeout SEC server exits after SEC seconds if there are no" - echo " " - echo " --web-auth enable authentication" - echo " --auth-plugin CLASS authentication plugin to use" - echo " --auth-source ARG plugin configuration" - echo " " - echo " active connections" - echo " " - exit 2 -} - -NAME="$(basename $0)" -REAL_NAME="$(readlink -f $0)" -HERE="$(cd "$(dirname "$REAL_NAME")" && pwd)" -HOST="" -PORT="6080" -LISTEN="$PORT" -VNC_DEST="localhost:5900" -CERT="" -KEY="" -WEB="" -proxy_pid="" -SSLONLY="" -RECORD="" -SYSLOG_ARG="" -HEARTBEAT_ARG="" -IDLETIMEOUT_ARG="" -TIMEOUT_ARG="" -WEBAUTH_ARG="" -AUTHPLUGIN_ARG="" -AUTHSOURCE_ARG="" -FILEONLY_ARG="" - - -die() { - echo "$*" - exit 1 -} - -cleanup() { - trap - TERM QUIT INT EXIT - trap "true" CHLD # Ignore cleanup messages - echo - if [ -n "${proxy_pid}" ]; then - echo "Terminating WebSockets proxy (${proxy_pid})" - kill ${proxy_pid} - fi -} - -# Process Arguments - -# Arguments that only apply to chrooter itself -while [ "$*" ]; do - param=$1; shift; OPTARG=$1 - case $param in - --listen) LISTEN="${OPTARG}"; shift ;; - --vnc) VNC_DEST="${OPTARG}"; shift ;; - --cert) CERT="${OPTARG}"; shift ;; - --key) KEY="${OPTARG}"; shift ;; - --web) WEB="${OPTARG}"; shift ;; - --ssl-only) SSLONLY="--ssl-only" ;; - --file-only) FILEONLY_ARG="--file-only" ;; - --record) RECORD="${OPTARG}"; shift ;; - --syslog) SYSLOG_ARG="--syslog ${OPTARG}"; shift ;; - --heartbeat) HEARTBEAT_ARG="--heartbeat ${OPTARG}"; shift ;; - --idle-timeout) IDLETIMEOUT_ARG="--idle-timeout ${OPTARG}"; shift ;; - --timeout) TIMEOUT_ARG="--timeout ${OPTARG}"; shift ;; - --web-auth) WEBAUTH_ARG="--web-auth" ;; - --auth-plugin) AUTHPLUGIN_ARG="--auth-plugin ${OPTARG}"; shift ;; - --auth-source) AUTHSOURCE_ARG="--auth-source ${OPTARG}"; shift ;; - -h|--help) usage ;; - -*) usage "Unknown chrooter option: ${param}" ;; - *) break ;; - esac -done - -if [ "$LISTEN" != "$PORT" ]; then - HOST=${LISTEN%:*} - PORT=${LISTEN##*:} - # if no host was given, restore - [ "$HOST" = "$PORT" ] && HOST="" -fi - -# Sanity checks -if [ -z "${HOST}" ]; then - if bash -c "exec 7<>/dev/tcp/localhost/${PORT}" &> /dev/null; then - exec 7<&- - exec 7>&- - die "Port ${PORT} in use. Try --listen PORT" - else - exec 7<&- - exec 7>&- - fi -fi - -trap "cleanup" TERM QUIT INT EXIT - -# Find vnc.html -if [ -n "${WEB}" ]; then - if [ ! -e "${WEB}/vnc.html" ]; then - die "Could not find ${WEB}/vnc.html" - fi -elif [ -e "$(pwd)/vnc.html" ]; then - WEB=$(pwd) -elif [ -e "${HERE}/../vnc.html" ]; then - WEB=${HERE}/../ -elif [ -e "${HERE}/vnc.html" ]; then - WEB=${HERE} -elif [ -e "${HERE}/../share/novnc/vnc.html" ]; then - WEB=${HERE}/../share/novnc/ -else - die "Could not find vnc.html" -fi - -# Find self.pem -if [ -n "${CERT}" ]; then - if [ ! -e "${CERT}" ]; then - die "Could not find ${CERT}" - fi -elif [ -e "$(pwd)/self.pem" ]; then - CERT="$(pwd)/self.pem" -elif [ -e "${HERE}/../self.pem" ]; then - CERT="${HERE}/../self.pem" -elif [ -e "${HERE}/self.pem" ]; then - CERT="${HERE}/self.pem" -else - echo "Warning: could not find self.pem" -fi - -# Check key file -if [ -n "${KEY}" ]; then - if [ ! -e "${KEY}" ]; then - die "Could not find ${KEY}" - fi -fi - -# try to find websockify (prefer local, try global, then download local) -if [[ -d ${HERE}/websockify ]]; then - WEBSOCKIFY=${HERE}/websockify/run - - if [[ ! -x $WEBSOCKIFY ]]; then - echo "The path ${HERE}/websockify exists, but $WEBSOCKIFY either does not exist or is not executable." - echo "If you intended to use an installed websockify package, please remove ${HERE}/websockify." - exit 1 - fi - - echo "Using local websockify at $WEBSOCKIFY" -else - WEBSOCKIFY_FROMSYSTEM=$(which websockify 2>/dev/null) - WEBSOCKIFY_FROMSNAP=${HERE}/../usr/bin/python2-websockify - [ -f $WEBSOCKIFY_FROMSYSTEM ] && WEBSOCKIFY=$WEBSOCKIFY_FROMSYSTEM - [ -f $WEBSOCKIFY_FROMSNAP ] && WEBSOCKIFY=$WEBSOCKIFY_FROMSNAP - - if [ ! -f "$WEBSOCKIFY" ]; then - echo "No installed websockify, attempting to clone websockify..." - WEBSOCKIFY=${HERE}/websockify/run - git clone https://github.com/novnc/websockify ${HERE}/websockify - - if [[ ! -e $WEBSOCKIFY ]]; then - echo "Unable to locate ${HERE}/websockify/run after downloading" - exit 1 - fi - - echo "Using local websockify at $WEBSOCKIFY" - else - echo "Using installed websockify at $WEBSOCKIFY" - fi -fi - -# Make all file paths absolute as websockify changes working directory -WEB=`realpath "${WEB}"` -[ -n "${CERT}" ] && CERT=`realpath "${CERT}"` -[ -n "${KEY}" ] && KEY=`realpath "${KEY}"` -[ -n "${RECORD}" ] && RECORD=`realpath "${RECORD}"` - -echo "Starting webserver and WebSockets proxy on${HOST:+ host ${HOST}} port ${PORT}" -${WEBSOCKIFY} ${SYSLOG_ARG} ${SSLONLY} ${FILEONLY_ARG} --web ${WEB} ${CERT:+--cert ${CERT}} ${KEY:+--key ${KEY}} ${LISTEN} ${VNC_DEST} ${HEARTBEAT_ARG} ${IDLETIMEOUT_ARG} ${RECORD:+--record ${RECORD}} ${TIMEOUT_ARG} ${WEBAUTH_ARG} ${AUTHPLUGIN_ARG} ${AUTHSOURCE_ARG} & -proxy_pid="$!" -sleep 1 -if [ -z "$proxy_pid" ] || ! ps -eo pid= | grep -w "$proxy_pid" > /dev/null; then - proxy_pid= - echo "Failed to start WebSockets proxy" - exit 1 -fi - -if [ -z "$HOST" ]; then - HOST=$(hostname) -fi - -echo -e "\n\nNavigate to this URL:\n" -if [ "x$SSLONLY" == "x" ]; then - echo -e " http://${HOST}:${PORT}/vnc.html?host=${HOST}&port=${PORT}\n" -else - echo -e " https://${HOST}:${PORT}/vnc.html?host=${HOST}&port=${PORT}\n" -fi - -echo -e "Press Ctrl-C to exit\n\n" - -wait ${proxy_pid} diff --git a/base/app/novnc/utils/u2x11 b/base/app/novnc/utils/u2x11 deleted file mode 100755 index fd3e4ba..0000000 --- a/base/app/novnc/utils/u2x11 +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash -# -# Convert "U+..." commented entries in /usr/include/X11/keysymdef.h -# into JavaScript for use by noVNC. Note this is likely to produce -# a few duplicate properties with clashing values, that will need -# resolving manually. -# -# Colin Dean -# - -regex="^#define[ \t]+XK_[A-Za-z0-9_]+[ \t]+0x([0-9a-fA-F]+)[ \t]+\/\*[ \t]+U\+([0-9a-fA-F]+)[ \t]+[^*]+.[ \t]+\*\/[ \t]*$" -echo "unicodeTable = {" -while read line; do - if echo "${line}" | egrep -qs "${regex}"; then - - x11=$(echo "${line}" | sed -r "s/${regex}/\1/") - vnc=$(echo "${line}" | sed -r "s/${regex}/\2/") - - if echo "${vnc}" | egrep -qs "^00[2-9A-F][0-9A-F]$"; then - : # skip ISO Latin-1 (U+0020 to U+00FF) as 1-to-1 mapping - else - # note 1-to-1 is possible (e.g. for Euro symbol, U+20AC) - echo " 0x${vnc} : 0x${x11}," - fi - fi -done < /usr/include/X11/keysymdef.h | uniq -echo "};" - diff --git a/base/app/novnc/utils/validate b/base/app/novnc/utils/validate deleted file mode 100755 index a6b5507..0000000 --- a/base/app/novnc/utils/validate +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -set -e - -RET=0 - -OUT=`mktemp` - -for fn in "$@"; do - echo "Validating $fn..." - echo - - case $fn in - *.html) - type="text/html" - ;; - *.css) - type="text/css" - ;; - *) - echo "Unknown format!" - echo - RET=1 - continue - ;; - esac - - curl --silent \ - --header "Content-Type: ${type}; charset=utf-8" \ - --data-binary @${fn} \ - https://validator.w3.org/nu/?out=text > $OUT - cat $OUT - echo - - # We don't fail the check for warnings as some warnings are - # not relevant for us, and we don't currently have a way to - # ignore just those - if grep -q -s -E "^Error:" $OUT; then - RET=1 - fi -done - -rm $OUT - -exit $RET diff --git a/base/app/novnc/utils/websockify/.github/workflows/test.yml b/base/app/novnc/utils/websockify/.github/workflows/test.yml deleted file mode 100644 index 33b475f..0000000 --- a/base/app/novnc/utils/websockify/.github/workflows/test.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Test - -on: [push, pull_request] - -jobs: - test: - runs-on: ubuntu-20.04 - strategy: - matrix: - python-version: - - 3.6 - - 3.7 - - 3.8 - - 3.9 - - "3.10" - - 3.11 - - 3.12 - fail-fast: false - steps: - - uses: actions/checkout@v4 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - name: Update pip and setuptools - run: | - python -m pip install --upgrade pip - python -m pip install setuptools - - name: Install dependencies - run: | - python -m pip install -e . - python -m pip install -r test-requirements.txt - - name: Install old numpy - run: | - python -m pip install 'numpy<1.17' - if: ${{ matrix.python-version >= '3.4' && matrix.python-version < '3.7' }} - - name: Run tests - run: | - python -m nose2 --verbosity=3 diff --git a/base/app/novnc/utils/websockify/.gitmodules b/base/app/novnc/utils/websockify/.gitmodules deleted file mode 100644 index e69de29..0000000 diff --git a/base/app/novnc/utils/websockify/CHANGES.txt b/base/app/novnc/utils/websockify/CHANGES.txt deleted file mode 100644 index 29ac5ed..0000000 --- a/base/app/novnc/utils/websockify/CHANGES.txt +++ /dev/null @@ -1,131 +0,0 @@ -Changes -======= - -0.12.0 ------- - -* The simplejson module is no longer needed for redis token support -* redis tokens can now be either JSON or plain text -* websockify can now listen to a Unix socket for incoming connections - -0.11.0 ------- - -* Command line now supports disabling directory listings -* Basic Dockerfile included - -0.10.0 ------- - -* Python 3.4 or newer is now required -* Empty message frames are now supported -* Tokens can now specify a Unix domain socket file to connect to -* Time limits on JWT tokens are now respected -* Whitespace is better tolerated in token files -* Lots of minor fixes... - -0.9.0 ------ - -* Base64 support removed and binary mode is now required -* Low level WebSocket protocol handling now has its own class -* Authentication now optionally required for web server -* Server hostname can be used as the token -* JWT/JWS/JWE can be used for the token -* redis can be used for the token -* Can now log to syslog -* Improved latency by disabling Nagle for proxied connection -* Added client certificate authentication -* Support for password protected certificate key file -* TLS ciphers and options are now configurable -* Can be invoked via inetd -* Lots of minor fixes... - -0.8.0 ------ - -* Make websockify properly terminate children on SIGTERM (#226) -* Remove logging in signal handlers (this can cause Python to hang under certain conditions) (#219) -* Make it easier to log to a file (#205) -* Add support for IPv6 addresses in tokens in the TokenFile token plugins (#197) -* Improve auth plugin framework to enable better support for HTTP auth (#194, #201) -* Fix bug in JSONTokenAPI token plugin (#192) -* Fix a missing variable in the exception handler (#178) - -0.7.0 ------ - -* Python 3 support fixes (#140, #155, #159) -* Generic token-parsing plugins support (#162) -* Generic authentication plugins support (#172) -* Fixed frame corruption on big-endian systems (#161) -* Support heartbeats (via PING) and automatic responses to PONG (#169) -* Automatically reject unmasked client frames by default (strict mode) (#174) -* Automatically restart interrupted select calls (#175) -* Make 'run' respect environment settings (including virtualenv) (#176) - -0.6.1 - May 11, 2015 --------------------- - -* **PATCH RELEASE**: Fixes a bug causing file_only to not be passed properly - -0.6.0 - Feb 18, 2014 --------------------- - -* **NOTE** : 0.6.0 will break existing code that sub-classes WebsocketProxy -* Refactor to use standard SocketServer RequestHandler design -* Fix zombie process bug on certain systems when using multiprocessing -* Add better unit tests -* Log information via python `logging` module - -0.5.1 - Jun 27, 2013 --------------------- - - * use upstream einaros/ws (>=0.4.27) with websockify.js - * file_only and no_parent security options for WSRequestHandler - * Update build of web-socket-js (c0855c6cae) - * add include/web-socket-js-project submodule to gimite/web-socket-js - for DSFG compliance. - * drop Hixie protocol support - -0.4.1 - Mar 12, 2013 --------------------- - - * ***NOTE*** : 0.5.0 will drop Hixie protocol support - * add include/ directory and remove some dev files from source - distribution. - -0.4.0 - Mar 12, 2013 --------------------- - - * ***NOTE*** : 0.5.0 will drop Hixie protocol support - * use Buffer base64 support in Node.js implementation - -0.3.0 - Jan 15, 2013 --------------------- - - * refactor into modules: websocket, websocketproxy - * switch to web-socket-js that uses IETF 6455 - * change to MPL 2.0 license for include/*.js - * fix session recording - -0.2.1 - Oct 15, 2012 --------------------- - - * re-released with updated version number - -0.2.0 - Sep 17, 2012 --------------------- - - * Binary data support in websock.js - * Target config file/dir and multiple targets with token selector - * IPv6 fixes - * SSL target support - * Proxy to/from unix socket - - -0.1.0 - May 11, 2012 --------------------- - - * Initial versioned release. - diff --git a/base/app/novnc/utils/websockify/COPYING b/base/app/novnc/utils/websockify/COPYING deleted file mode 100644 index 65c5ca8..0000000 --- a/base/app/novnc/utils/websockify/COPYING +++ /dev/null @@ -1,165 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. diff --git a/base/app/novnc/utils/websockify/MANIFEST.in b/base/app/novnc/utils/websockify/MANIFEST.in deleted file mode 100644 index e6139bc..0000000 --- a/base/app/novnc/utils/websockify/MANIFEST.in +++ /dev/null @@ -1 +0,0 @@ -include CHANGES.txt README.md COPYING diff --git a/base/app/novnc/utils/websockify/Makefile b/base/app/novnc/utils/websockify/Makefile deleted file mode 100644 index 7dc1bc4..0000000 --- a/base/app/novnc/utils/websockify/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -TARGETS=rebind.so -CFLAGS += -fPIC - -all: $(TARGETS) - -rebind.so: rebind.o - $(CC) $(LDFLAGS) $^ -shared -fPIC -ldl -o $@ - -clean: - rm -f rebind.o rebind.so - diff --git a/base/app/novnc/utils/websockify/README.md b/base/app/novnc/utils/websockify/README.md deleted file mode 100644 index c5d14cc..0000000 --- a/base/app/novnc/utils/websockify/README.md +++ /dev/null @@ -1,208 +0,0 @@ -## websockify: WebSockets support for any application/server - -websockify was formerly named wsproxy and was part of the -[noVNC](https://github.com/novnc/noVNC) project. - -At the most basic level, websockify just translates WebSockets traffic -to normal socket traffic. Websockify accepts the WebSockets handshake, -parses it, and then begins forwarding traffic between the client and -the target in both directions. - -### News/help/contact - -Notable commits, announcements and news are posted to -@noVNC - -If you are a websockify developer/integrator/user (or want to be) -please join the noVNC/websockify -discussion group - -Bugs and feature requests can be submitted via [github -issues](https://github.com/novnc/websockify/issues). - -If you want to show appreciation for websockify you could donate to a great -non-profits such as: [Compassion -International](http://www.compassion.com/), [SIL](http://www.sil.org), -[Habitat for Humanity](http://www.habitat.org), [Electronic Frontier -Foundation](https://www.eff.org/), [Against Malaria -Foundation](http://www.againstmalaria.com/), [Nothing But -Nets](http://www.nothingbutnets.net/), etc. Please tweet @noVNC if you do. - -### WebSockets binary data - -Starting with websockify 0.5.0, only the HyBi / IETF -6455 WebSocket protocol is supported. There is no support for the older -Base64 encoded data format. - - -### Encrypted WebSocket connections (wss://) - -To encrypt the traffic using the WebSocket 'wss://' URI scheme you need to -generate a certificate and key for Websockify to load. By default, Websockify -loads a certificate file name `self.pem` but the `--cert=CERT` and `--key=KEY` -options can override the file name. You can generate a self-signed certificate -using openssl. When asked for the common name, use the hostname of the server -where the proxy will be running: - -``` -openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem -``` - -For a self-signed certificate to work, you need to make your client/browser -understand it. You can do this by installing it as accepted certificate, or by -using that same certificate for a HTTPS connection to which you navigate first -and approve. Browsers generally don't give you the "trust certificate?" prompt -by opening a WSS socket with invalid certificate, hence you need to have it -accept it by either of those two methods. - -The ports may be considered as distinguishing connections by the browser, -for example, if your website url is https://my.local:8443 and your WebSocket -url is wss://my.local:8001, first browse to https://my.local:8001, add the -exception, then browse to https://my.local:8443 and add another exception. -Then an html page served over :8443 will be able to open WSS to :8001 - -If you have a commercial/valid SSL certificate with one or more intermediate -certificates, concat them into one file, server certificate first, then the -intermediate(s) from the CA, etc. Point to this file with the `--cert` option -and then also to the key with `--key`. Finally, use `--ssl-only` as needed. - - -### Additional websockify features - -These are not necessary for the basic operation. - -* Daemonizing: When the `-D` option is specified, websockify runs - in the background as a daemon process. - -* SSL (the wss:// WebSockets URI): This is detected automatically by - websockify by sniffing the first byte sent from the client and then - wrapping the socket if the data starts with '\x16' or '\x80' - (indicating SSL). - -* Session recording: This feature that allows recording of the traffic - sent and received from the client to a file using the `--record` - option. - -* Mini-webserver: websockify can detect and respond to normal web - requests on the same port as the WebSockets proxy. This functionality - is activated with the `--web DIR` option where DIR is the root of the - web directory to serve. - -* Wrap a program: see the "Wrap a Program" section below. - -* Log files: websockify can save all logging information in a file. - This functionality is activated with the `--log-file FILE` option - where FILE is the file where the logs should be saved. - -* Authentication plugins: websockify can demand authentication for - websocket connections and, if you use `--web-auth`, also for normal - web requests. This functionality is activated with the - `--auth-plugin CLASS` and `--auth-source ARG` options, where CLASS is - usually one from auth_plugins.py and ARG is the plugin's configuration. - -* Token plugins: a single instance of websockify can connect clients to - multiple different pre-configured targets, depending on the token sent - by the client using the `token` URL parameter, or the hostname used to - reach websockify, if you use `--host-token`. This functionality is - activated with the `--token-plugin CLASS` and `--token-source ARG` - options, where CLASS is usually one from token_plugins.py and ARG is - the plugin's configuration. - -### Other implementations of websockify - -The primary implementation of websockify is in python. There are -several alternate implementations in other languages available in -our sister repositories [websockify-js](https://github.com/novnc/websockify-js) -(JavaScript/Node.js) and [websockify-other](https://github.com/novnc/websockify-other) - (C, Clojure, Ruby). - -In addition there are several other external projects that implement -the websockify "protocol". See the alternate implementation [Feature -Matrix](https://github.com/novnc/websockify/wiki/Feature_Matrix) for -more information. - - -### Wrap a Program - -In addition to proxying from a source address to a target address -(which may be on a different system), websockify has the ability to -launch a program on the local system and proxy WebSockets traffic to -a normal TCP port owned/bound by the program. - -This is accomplished by the LD_PRELOAD library (`rebind.so`) -which intercepts bind() system calls by the program. The specified -port is moved to a new localhost/loopback free high port. websockify -then proxies WebSockets traffic directed to the original port to the -new (moved) port of the program. - -The program wrap mode is invoked by replacing the target with `--` -followed by the program command line to wrap. - - `./run 2023 -- PROGRAM ARGS` - -The `--wrap-mode` option can be used to indicate what action to take -when the wrapped program exits or daemonizes. - -Here is an example of using websockify to wrap the vncserver command -(which backgrounds itself) for use with -[noVNC](https://github.com/novnc/noVNC): - - `./run 5901 --wrap-mode=ignore -- vncserver -geometry 1024x768 :1` - -Here is an example of wrapping telnetd (from krb5-telnetd). telnetd -exits after the connection closes so the wrap mode is set to respawn -the command: - - `sudo ./run 2023 --wrap-mode=respawn -- telnetd -debug 2023` - -The `wstelnet.html` page in the [websockify-js](https://github.com/novnc/websockify-js) -project demonstrates a simple WebSockets based telnet client (use -'localhost' and '2023' for the host and port respectively). - - -### Installing websockify - -Download one of the releases or the latest development version, extract -it and run `python3 setup.py install` as root in the directory where you -extracted the files. Normally, this will also install numpy for better -performance, if you don't have it installed already. However, numpy is -optional. If you don't want to install numpy or if you can't compile it, -you can edit setup.py and remove the `install_requires=['numpy'],` line -before running `python3 setup.py install`. - -Afterwards, websockify should be available in your path. Run -`websockify --help` to confirm it's installed correctly. - - -### Running with Docker/Podman -You can also run websockify using Docker, Podman, Singularity, udocker or -your favourite container runtime that support OCI container images. - -The entrypoint of the image is the `run` command. - -To build the image: -``` -./docker/build.sh -``` - -Once built you can just launch it with the same -arguments you would give to the `run` command and taking care of -assigning the port mappings: -``` -docker run -it --rm -p : novnc/websockify -``` - -For example to forward traffic from local port 7000 to 10.1.1.1:5902 -you can use: -``` -docker run -it --rm -p 7000:80 novnc/websockify 80 10.1.1.1:5902 -``` - -If you need to include files, like for example for the `--web` or `--cert` -options you can just mount the required files in the `/data` volume and then -you can reference them in the usual way: -``` -docker run -it --rm -p 443:443 -v websockify-data:/data novnc/websockify --cert /data/self.pem --web /data/noVNC :443 --token-plugin TokenRedis --token-source myredis.local:6379 --ssl-only --ssl-version tlsv1_2 -``` diff --git a/base/app/novnc/utils/websockify/Windows/Windows Service Readme.md b/base/app/novnc/utils/websockify/Windows/Windows Service Readme.md deleted file mode 100644 index 069692e..0000000 --- a/base/app/novnc/utils/websockify/Windows/Windows Service Readme.md +++ /dev/null @@ -1,61 +0,0 @@ -Running Websockify as a Windows service -======================================= - -Installation and configuration ------------------------------- - -Download the following software: - - * Python, from https://www.python.org/downloads/windows/ - * SrvAny, from http://simpleauto.byethost8.com/Zip/SrvAny.zip - -Note that there is [a modern alternative for SrvAny](https://github.com/rwmjones/rhsrvany), -but that project does not provide binaries. - -Install Python for all users, not just the current one. Extract Websockify -into a directory, e.g. `C:\Program Files\websockify`, so that e.g. -`README.md` ends up there. Extract the `SrvAny.zip` archive, copy the -`WIN7\SrvAny.exe` file into `C:\Program Files\websockify`. - -Then create a batch file, `C:\Program Files\websockify\run.bat`, that runs -Websockify from its directory with the correct options under the correct -Python interpreter: - -``` -C: -cd "\Program Files\websockify" -"C:\Program Files\Python39\python.exe" -m websockify 5901 127.0.0.1:5900 -``` - -Run it by hand once so that Windows asks you about a firewall exception. -After confirming the exception, press `Ctrl+C` to terminate the script. - -Then create a Windows service for Websockify (use an Administrator command -prompt for that). For paths with spaces, like in this example, double-escaping -is needed: once for `cmd.exe` and once for `SrvAny.exe`. - -``` -C: -cd "\Program Files\websockify" -SrvAny.exe -install Websockify 10s \\\"C:\Program Files\websockify\run.bat\\\" -``` - -In the Windows Control Panel, under Services, a new "Websockify" service will -appear. In its properties dialog, you can change the startup type, e.g. make -it start automatically at boot. Or, you can start the service manually. - -Uninstallation --------------- - -If you want to remove the service, first set its startup type to Manual, then -reboot the PC. Then run this command using the Administrator command prompt: - -``` -C: -cd "\Program Files\websockify" -SrvAny.exe -remove Websockify -``` - -After that, you will be able to remove the `C:\Program Files\websockify` -directory completely. - diff --git a/base/app/novnc/utils/websockify/docker/Dockerfile b/base/app/novnc/utils/websockify/docker/Dockerfile deleted file mode 100644 index 1ae078b..0000000 --- a/base/app/novnc/utils/websockify/docker/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -FROM python - -COPY websockify-*.tar.gz / - -RUN python3 -m pip install websockify-*.tar.gz -RUN rm -rf /websockify-* /root/.cache - -VOLUME /data - -EXPOSE 80 -EXPOSE 443 - -WORKDIR /opt/websockify - -ENTRYPOINT ["/usr/local/bin/websockify"] -CMD ["--help"] diff --git a/base/app/novnc/utils/websockify/docker/build.sh b/base/app/novnc/utils/websockify/docker/build.sh deleted file mode 100755 index 1f85626..0000000 --- a/base/app/novnc/utils/websockify/docker/build.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env sh -set -e -x -cd "$(dirname "$0")" -(cd .. && python3 setup.py sdist --dist-dir docker/) -docker build -t novnc/websockify . diff --git a/base/app/novnc/utils/websockify/docs/latency_results.txt b/base/app/novnc/utils/websockify/docs/latency_results.txt deleted file mode 100644 index 454a590..0000000 --- a/base/app/novnc/utils/websockify/docs/latency_results.txt +++ /dev/null @@ -1,114 +0,0 @@ -This data is raw copy from the latency tester set to send a frame with -a little over 2000 KB of data every 10ms. - -The number of packets sent and received is just a visual counter and -is just the total when I chose to stop the test (around 3000 or so -packets). - -The latency measure are from the point the packet was sent to when it -was received back again in milliseconds. One notable data point -missing from this is how long it actually took for the client to send -3000 packets because sending large packets can put load on the browser -and it may be a lot longer than 10ms before the timer event to -send the next packet fires. So even with low latency numbers, the -actual send rate may be fairly low because sending the WebSockets -frames is impacting the performance of the browser in general. - - ------------------------------------------------------------- - -Native WebSockets implementations, 2000 byte payload, 10ms delay - -Chrome 8.0.552 - native WebSockets - Packets sent: 2998 - Packets Received: 2998 - Average Latency: 1.84 - 40 Frame Running Average Latency: 1.90 - Minimum Latency: 1.00 - Maximum Latency: 10.00 - -firefox 4.0b9 - WebSockets enabled - Packets sent: 3011 - Packets Received: 3011 - Average Latency: 6.45 - 40 Frame Running Average Latency: 6.08 - Minimum Latency: 5.00 - Maximum Latency: 119.00 - -Opera 11 - WebSockets enabled - Packets sent: 3065 - Packets Received: 3064 - Average Latency: 9.56 - 40 Frame Running Average Latency: 8.15 - Minimum Latency: 4.00 - Maximum Latency: 53.00 - ------------------------------------------------------------- - -New web-socket-js (20f837425d4), 2000 byte payload, 10ms delay - -firefox 4.0b9 - no WebSockets - Packets sent: 3088 - Packets Received: 3087 - Average Latency: 16.71 - 40 Frame Running Average Latency: 16.80 - Minimum Latency: 7.00 - Maximum Latency: 75.00 - - - First 1000 sent in 13 seconds - - Second 1000 sent in 12 seconds - - Third 1000 sent in 12 seconds - -firefox 3.6.10 - no WebSockets - Packets sent: 3100 - Packets Received: 3099 - Average Latency: 17.32 - 40 Frame Running Average Latency: 16.73 - Minimum Latency: 6.00 - Maximum Latency: 72.00 - -Opera 11 - no WebSockets - Packets sent: 3007 - Packets Received: 3007 - Average Latency: 465.91 - 40 Frame Running Average Latency: 147.95 - Minimum Latency: 12.00 - Maximum Latency: 9143.00 - - - average starts at around 28ms - - time for each 500 packets: 13s, 16s, 25s, 37s, 50s, 72s - - also start seeing sent, receive lags around 1200 packets - ---------------------------------------------------------------- - -Old web-socket-js (9e7663771), 2000 byte payload, 10ms delay - -firefox 4.0b9 - no WebSockets - Packets sent: 3024 - Packets Received: 3020 - Average Latency: 80.59 - 40 Frame Running Average Latency: 60.15 - Minimum Latency: 10.00 - Maximm Latency: 348.00 - - -firefox 3.6.10 - no WebSockets - Packets sent: 2777 - Packets Received: 2775 - Average Latency: 34.89 - 40 Frame Running Average Latency: 24.50 - Minimum Latency: 10.00 - Maximum Latency: 208.00 - - -Opera 11 - no Websockets - Packets sent: 3012 - Packets Received: 3011 - Average Latency: 380.87 - 40 Frame Running Average Latency: 341.90 - Minimum Latency: 28.00 - Maximum Latency: 2175.00 - - - average starts at around 290ms - - time for each 1000 packets: 23s, 38s, 65s - diff --git a/base/app/novnc/utils/websockify/docs/notes b/base/app/novnc/utils/websockify/docs/notes deleted file mode 100644 index edb6b1d..0000000 --- a/base/app/novnc/utils/websockify/docs/notes +++ /dev/null @@ -1,6 +0,0 @@ -Building release tarball: - - not really necessary since tagged revision can be downloaded - from github as tarballs - - git archive --format=tar --prefix=websockify-${WVER}/ v${WVER} > websockify-${WVER}.tar - gzip websockify-${WVER}.tar diff --git a/base/app/novnc/utils/websockify/docs/release.txt b/base/app/novnc/utils/websockify/docs/release.txt deleted file mode 100644 index 813af02..0000000 --- a/base/app/novnc/utils/websockify/docs/release.txt +++ /dev/null @@ -1,10 +0,0 @@ -- Update setup.py and CHANGES.txt and commit -- Create version tag and tarball from tag - WVER=0.1.0 - git tag v${WVER} - git push origin master - git push origin v${WVER} -- Create the source distribution - python3 setup.py sdist -- Upload the source distribution - python3 -m twine upload dist/websockify-${WVER}.tar.gz diff --git a/base/app/novnc/utils/websockify/docs/websockify.1 b/base/app/novnc/utils/websockify/docs/websockify.1 deleted file mode 100644 index f2aa988..0000000 --- a/base/app/novnc/utils/websockify/docs/websockify.1 +++ /dev/null @@ -1,110 +0,0 @@ -.TH websockify 1 "June 7, 2012" "version 0.3" "USER COMMANDS" - -.SH NAME - -websockify - WebSockets to TCP socket bridge - -.SH SYNOPSIS - - websockify [options] [source_addr:]source_port target_addr:target_port - websockify [options] [source_addr:]source_port \-\- WRAP_COMMAND_LINE - -.SH OPTIONS - - -h, --help show this help message and exit - -v, --verbose verbose messages and per frame traffic - --record=FILE record sessions to FILE.[session_number] - -D, --daemon become a daemon (background process) - --run-once handle a single WebSocket connection and exit - --timeout=TIMEOUT after TIMEOUT seconds exit when not connected - --cert=CERT SSL certificate file - --key=KEY SSL key file (if separate from cert) - --ssl-only disallow non-encrypted connections - --web=DIR run webserver on same port. Serve files from DIR. - --wrap-mode=MODE action to take when the wrapped program exits or - daemonizes: exit (default), ignore, respawn - -.SH DESCRIPTION - -At the most basic level, websockify just translates WebSockets traffic to normal TCP socket traffic. Websockify accepts the WebSockets handshake, parses it, and then begins forwarding traffic between the client and the target in both directions. - -websockify was formerly named wsproxy and was part of the noVNC project. - -.SH NOTES - -.SS WebSockets binary data - -Websockify supports all versions of the WebSockets protocol (Hixie and HyBI). The older Hixie versions of the protocol only support UTF-8 text payloads. In order to transport binary data over UTF-8 an encoding must used to encapsulate the data within UTF-8. Websockify uses base64 to encode all traffic to and from the client. This does not affect the data between websockify and the server. - -.SS Encrypted WebSocket connections (wss://) - -To encrypt the traffic using the WebSocket 'wss://' URI scheme you need to generate a certificate for websockify to load. By default websockify loads a certificate file name self.pem but the --cert=CERT option can override the file name. You can generate a self-signed certificate using openssl. When asked for the common name, use the hostname of the server where the proxy will be running: - -openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem - -.SS Additional websockify features - -These are not necessary for the basic operation. - -.IP * -Daemonizing: When the -D option is specified, websockify runs in the background as a daemon process. - -.IP * -SSL (the wss:// WebSockets URI): This is detected automatically by websockify by sniffing the first byte sent from the client and then wrapping the socket if the data starts with '\\x16' or '\\x80' (indicating SSL). - -.IP * -Session recording: This feature that allows recording of the traffic sent and received from the client to a file using the --record option. - -.IP * -Mini-webserver: websockify can detect and respond to normal web requests on the same port as the WebSockets proxy. This functionality is activate with the --web DIR option where DIR is the root of the web directory to serve. - -.IP * -Wrap a program: see the "Wrap a Program" section below. - -.SS Wrap a Program - -In addition to proxying from a source address to a target address (which may be on a different system), websockify has the ability to launch a program on the local system and proxy WebSockets traffic to a normal TCP port owned/bound by the program. - -The is accomplished with a small LD_PRELOAD library (rebind.so) which intercepts bind() system calls by the program. The specified port is moved to a new localhost/loopback free high port. websockify then proxies WebSockets traffic directed to the original port to the new (moved) port of the program. - -The program wrap mode is invoked by replacing the target with -- followed by the program command line to wrap. - -`./websockify 2023 -- PROGRAM ARGS` - -The --wrap-mode option can be used to indicate what action to take when the wrapped program exits or daemonizes. - -Here is an example of using websockify to wrap the vncserver command (which backgrounds itself) for use with noVNC: - -`./websockify 5901 --wrap-mode=ignore -- vncserver -geometry 1024x768 :1` - -Here is an example of wrapping telnetd (from krb5-telnetd). telnetd exits after the connection closes so the wrap mode is set to respawn the command: - -`sudo ./websockify 2023 --wrap-mode=respawn -- telnetd -debug 2023` - -The wstelnet.html page demonstrates a simple WebSockets based telnet client. - -.SS Use client certificate verification - -This feature requires Python 2.7.9 or newer or Python 3.4 or newer. - -The --verify-client option makes the server ask the client for a SSL certificate. Presenting a valid (not expired and trusted by any supplied certificate authority) certificate is required for the client connection. With -auth-plugin=ClientCertCNAuth, the client certificate can be checked against a list of authorised certificate users. Non-encrypted connection attempts always fail during authentication. - -Here is an example of a vncsevrer with password-less, certificate-driven authentication: - -`./websockify 5901 --cert=fullchain.pem --key=privkey.pem --ssl-only --verify-client --cafile=ca-certificates.crt --auth-plugin=ClientCertCNAuth --auth-source='jane@example.com Joe User9824510' --web=noVNC/ --wrap-mode=ignore -- vncserver :1 -geometry 1024x768 -SecurityTypes=None` - -The --auth-source option takes a white-space separated list of common names. Depending on your clients certificates they can be verified email addresses, user-names or any other string used for identification. - -The --cafile option selects a file containing concatenated certificates of authorities trusted for validating clients. If this option is omitted, system default list of CAs is used. Upon connect, the client should supply the whole certificate chain. If your clients are known not to send intermediate certificates, they can be appended to the ca-file as well. - -Note: Most browsers ask the user to select a certificate only while connecting via HTTPS, not WebSockets. Connecting directly to the SSL secured WebSocket may cause the browser to abort the connection. If you want to connect via noVNC, the --web option should point to a copy of noVNC, so it is loaded from the same host. - -.SH AUTHOR -Joel Martin (github@martintribe.org) - -.SH SEE ALSO - -https://github.com/novnc/websockify/ - -https://github.com/novnc/websockify/wiki/ - diff --git a/base/app/novnc/utils/websockify/rebind b/base/app/novnc/utils/websockify/rebind deleted file mode 100755 index 2289aaa..0000000 --- a/base/app/novnc/utils/websockify/rebind +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash - -usage() { - echo "Usage: $(basename $0) OLD_PORT NEW_PORT COMMAND_LINE" - echo - echo "Launch COMMAND_LINE, but intercept system calls to bind" - echo "to OLD_PORT and instead bind them to localhost:NEW_PORT" - exit 2 -} - -# Parameter defaults -mydir=$(readlink -f $(dirname ${0})) - -export REBIND_PORT_OLD="${1}"; shift -export REBIND_PORT_NEW="${1}"; shift - -LD_PRELOAD=${mydir}/rebind.so "${@}" - diff --git a/base/app/novnc/utils/websockify/rebind.c b/base/app/novnc/utils/websockify/rebind.c deleted file mode 100644 index 811031c..0000000 --- a/base/app/novnc/utils/websockify/rebind.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * rebind: Intercept bind calls and bind to a different port - * Copyright 2010 Joel Martin - * Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3) - * - * Overload (LD_PRELOAD) bind system call. If REBIND_PORT_OLD and - * REBIND_PORT_NEW environment variables are set then bind on the new - * port (of localhost) instead of the old port. - * - * This allows a bridge/proxy (such as websockify) to run on the old port and - * translate traffic to/from the new port. - * - * Usage: - * LD_PRELOAD=./rebind.so \ - * REBIND_PORT_OLD=23 \ - * REBIND_PORT_NEW=2023 \ - * program - */ - -//#define DO_DEBUG 1 - -#include -#include - -#define __USE_GNU 1 // Pull in RTLD_NEXT -#include - -#include -#include - - -#if defined(DO_DEBUG) -#define DEBUG(...) \ - fprintf(stderr, "rebind: "); \ - fprintf(stderr, __VA_ARGS__); -#else -#define DEBUG(...) -#endif - - -int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) -{ - static void * (*func)(); - int do_move = 0; - struct sockaddr_in * addr_in = (struct sockaddr_in *)addr; - struct sockaddr_in addr_tmp; - socklen_t addrlen_tmp; - char * PORT_OLD, * PORT_NEW, * end1, * end2; - int ret, oldport, newport, askport = htons(addr_in->sin_port); - uint32_t askaddr = htons(addr_in->sin_addr.s_addr); - if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "bind"); - - DEBUG(">> bind(%d, _, %d), askaddr %d, askport %d\n", - sockfd, addrlen, askaddr, askport); - - /* Determine if we should move this socket */ - if (addr_in->sin_family == AF_INET) { - // TODO: support IPv6 - PORT_OLD = getenv("REBIND_OLD_PORT"); - PORT_NEW = getenv("REBIND_NEW_PORT"); - if (PORT_OLD && (*PORT_OLD != '\0') && - PORT_NEW && (*PORT_NEW != '\0')) { - oldport = strtol(PORT_OLD, &end1, 10); - newport = strtol(PORT_NEW, &end2, 10); - if (oldport && (*end1 == '\0') && - newport && (*end2 == '\0') && - (oldport == askport)) { - do_move = 1; - } - } - } - - if (! do_move) { - /* Just pass everything right through to the real bind */ - ret = (long) func(sockfd, addr, addrlen); - DEBUG("<< bind(%d, _, %d) ret %d\n", sockfd, addrlen, ret); - return ret; - } - - DEBUG("binding fd %d on localhost:%d instead of 0x%x:%d\n", - sockfd, newport, ntohl(addr_in->sin_addr.s_addr), oldport); - - /* Use a temporary location for the new address information */ - addrlen_tmp = sizeof(addr_tmp); - memcpy(&addr_tmp, addr, addrlen_tmp); - - /* Bind to other port on the loopback instead */ - addr_tmp.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - addr_tmp.sin_port = htons(newport); - ret = (long) func(sockfd, &addr_tmp, addrlen_tmp); - - DEBUG("<< bind(%d, _, %d) ret %d\n", sockfd, addrlen, ret); - return ret; -} diff --git a/base/app/novnc/utils/websockify/run b/base/app/novnc/utils/websockify/run deleted file mode 100755 index 8032dd2..0000000 --- a/base/app/novnc/utils/websockify/run +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env sh -set -e -cd "$(dirname "$0")" -exec python3 -m websockify "$@" diff --git a/base/app/novnc/utils/websockify/setup.py b/base/app/novnc/utils/websockify/setup.py deleted file mode 100644 index 51adeaf..0000000 --- a/base/app/novnc/utils/websockify/setup.py +++ /dev/null @@ -1,43 +0,0 @@ -from setuptools import setup, find_packages - -version = '0.12.0' -name = 'websockify' -long_description = open("README.md").read() + "\n" + \ - open("CHANGES.txt").read() + "\n" - -setup(name=name, - version=version, - description="Websockify.", - long_description=long_description, - long_description_content_type="text/markdown", - classifiers=[ - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.4", - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - ], - keywords='noVNC websockify', - license='LGPLv3', - url="https://github.com/novnc/websockify", - author="Joel Martin", - author_email="github@martintribe.org", - - packages=['websockify'], - include_package_data=True, - install_requires=[ - 'numpy', 'requests', - 'jwcrypto', - 'redis', - ], - zip_safe=False, - entry_points={ - 'console_scripts': [ - 'websockify = websockify.websocketproxy:websockify_init', - ] - }, - ) diff --git a/base/app/novnc/utils/websockify/test-requirements.txt b/base/app/novnc/utils/websockify/test-requirements.txt deleted file mode 100644 index 4eeff97..0000000 --- a/base/app/novnc/utils/websockify/test-requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -mock -nose2 -six -redis -wrapt<=1.12.1;python_version<="3.4" diff --git a/base/app/novnc/utils/websockify/tests/echo.html b/base/app/novnc/utils/websockify/tests/echo.html deleted file mode 100644 index 0e300b5..0000000 --- a/base/app/novnc/utils/websockify/tests/echo.html +++ /dev/null @@ -1,139 +0,0 @@ - - - - WebSockets Echo Test - - - - - Host:   - Port:   - Encrypt:   -   - - -
- Log:
- - - - - - - diff --git a/base/app/novnc/utils/websockify/tests/echo.py b/base/app/novnc/utils/websockify/tests/echo.py deleted file mode 100755 index 780891c..0000000 --- a/base/app/novnc/utils/websockify/tests/echo.py +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env python - -''' -A WebSocket server that echos back whatever it receives from the client. -Copyright 2010 Joel Martin -Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3) - -You can make a cert/key with openssl using: -openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem -as taken from http://docs.python.org/dev/library/ssl.html#certificates -''' - -import os, sys, select, optparse, logging -sys.path.insert(0,os.path.join(os.path.dirname(__file__), "..")) -from websockify.websockifyserver import WebSockifyServer, WebSockifyRequestHandler - -class WebSocketEcho(WebSockifyRequestHandler): - """ - WebSockets server that echos back whatever is received from the - client. """ - buffer_size = 8096 - - def new_websocket_client(self): - """ - Echo back whatever is received. - """ - - cqueue = [] - c_pend = 0 - cpartial = "" - rlist = [self.request] - - while True: - wlist = [] - - if cqueue or c_pend: wlist.append(self.request) - ins, outs, excepts = select.select(rlist, wlist, [], 1) - if excepts: raise Exception("Socket exception") - - if self.request in outs: - # Send queued target data to the client - c_pend = self.send_frames(cqueue) - cqueue = [] - - if self.request in ins: - # Receive client data, decode it, and send it back - frames, closed = self.recv_frames() - cqueue.extend(frames) - - if closed: - break - -if __name__ == '__main__': - parser = optparse.OptionParser(usage="%prog [options] listen_port") - parser.add_option("--verbose", "-v", action="store_true", - help="verbose messages and per frame traffic") - parser.add_option("--cert", default="self.pem", - help="SSL certificate file") - parser.add_option("--key", default=None, - help="SSL key file (if separate from cert)") - parser.add_option("--ssl-only", action="store_true", - help="disallow non-encrypted connections") - (opts, args) = parser.parse_args() - - try: - if len(args) != 1: raise ValueError - opts.listen_port = int(args[0]) - except ValueError: - parser.error("Invalid arguments") - - logging.basicConfig(level=logging.INFO) - - opts.web = "." - server = WebSockifyServer(WebSocketEcho, **opts.__dict__) - server.start_server() - diff --git a/base/app/novnc/utils/websockify/tests/echo_client.py b/base/app/novnc/utils/websockify/tests/echo_client.py deleted file mode 100755 index 4f238f6..0000000 --- a/base/app/novnc/utils/websockify/tests/echo_client.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -import optparse -import select - -sys.path.insert(0,os.path.join(os.path.dirname(__file__), "..")) -from websockify.websocket import WebSocket, \ - WebSocketWantReadError, WebSocketWantWriteError - -parser = optparse.OptionParser(usage="%prog URL") -(opts, args) = parser.parse_args() - -if len(args) == 1: - URL = args[0] -else: - parser.error("Invalid arguments") - -sock = WebSocket() -print("Connecting to %s..." % URL) -sock.connect(URL) -print("Connected.") - -def send(msg): - while True: - try: - sock.sendmsg(msg) - break - except WebSocketWantReadError: - msg = '' - ins, outs, excepts = select.select([sock], [], []) - if excepts: raise Exception("Socket exception") - except WebSocketWantWriteError: - msg = '' - ins, outs, excepts = select.select([], [sock], []) - if excepts: raise Exception("Socket exception") - -def read(): - while True: - try: - return sock.recvmsg() - except WebSocketWantReadError: - ins, outs, excepts = select.select([sock], [], []) - if excepts: raise Exception("Socket exception") - except WebSocketWantWriteError: - ins, outs, excepts = select.select([], [sock], []) - if excepts: raise Exception("Socket exception") - -counter = 1 -while True: - msg = "Message #%d" % counter - counter += 1 - send(msg) - print("Sent message: %r" % msg) - - while True: - ins, outs, excepts = select.select([sock], [], [], 1.0) - if excepts: raise Exception("Socket exception") - - if ins == []: - break - - while True: - msg = read() - print("Received message: %r" % msg) - - if not sock.pending(): - break diff --git a/base/app/novnc/utils/websockify/tests/fixtures/private.pem b/base/app/novnc/utils/websockify/tests/fixtures/private.pem deleted file mode 100644 index 413cee2..0000000 --- a/base/app/novnc/utils/websockify/tests/fixtures/private.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEArwNQal2INbSfoVT50dZ0s8lQ+yMhu45TDc91iuwtDjlFBJ50 -E4m3/M6ESBW0S7UTP1bIOGkd/M+u38h0Aruo4qkngdguu9N3BnsU2kOeicdjxc+v -tqRc7/kbkTdT4SrpG8EFP6T2U9U1gtBpLnau02gPrzjaQzyYDLGOBq+Ozt/mN0YJ -UhJ3hlwi18dNKreTzWgJ6mmXQWS0eAmHx8TIs2Nz9x3EfRo9CIMuaaeUjRogIEg5 -Tg4xC00ZtDO0/EfgpFkeHJGVQA2DgdBJsr6rq69MjhMfFxRJItxJMJzP6an2HkJ8 -onUPBtjEBmk3/fnfiaflzRyEb5zdii6r2TD8TwIDAQABAoIBAGDrzu742WQUoYqx -CqDAyWR/is9px1adHTW6vHexD8qewLAsKFBhpnjkzbE2A+EhaIVdRAipfifxxAC+ -fDC/SGouD2kDFe6Cz5nRM90kMXpP59s2hzL4l1d2d2PWZid+ohXysTtr2dbXbokB -bh6DL5J4QKdjLsypk/MDqYneU5IQ1k9ezWzcRgM8/V3M+t+1dLRFLIWsSLbNUgbF -px81efNw8E0voV/d7kZ+6RwUThPHqR0eyLm6djPwHE7/FarZIx4AImwV+9ex44CH -OkrTFOVYenF6jEtYoUuqYCouaWtG7jNVM/f1fksoR8SD6PTq2vn7F4wTLXG1b+K7 -45PKMhECgYEA22NH8mK9ICFVd7S6caeAnki+K9cpwmiOUWEIuDInmPn5FOlv7awE -uBFN86v14PqDBtF7Aagyib0NUPo7rIw5+V5SCBZv8gQatjZUkT0vZvpxPU5jmB++ -w58yfK7zgdAWCepLxIPyTA7CAT1dmiVmuosz2pJjbo4fecVG222IE10CgYEAzDg+ -RVlvMYGy04UMmUoUNeeRlW6km/W6dqQ7EtcxfDv4O7boRDTBSRBzfIsRdXHZhcHN -gCeB2Uiz8IO3s0Yt0+y/6cTI60uJ4S7Mb2JvWJvDCKWhS3pE1BL+LJJC4Hn7khJH -yHYFOLOfnuCbOs8VA7IMmbdTPHirIKWTT5j5H5sCgYEAygK/KweUUlOfWVyHGUQ9 -gIJG6iNzhlm0QmbxGnrET25N1t2kfNsadUsp1igPfhvuLocRltMDxiTYcCoabKWq -dF5PdrcCWX1CA2o/sIUAcvhE8UiPGHKSu5qJaJnIC05KHNMq9UbyAurL5UxWNiwe -TcMD+k01VYV0ojHvLvnKhNkCgYArkoh+xXE7D+A2zzl771lWkvz19DB88jYBoFLW -V0HArw7str7h5pui2ja5yPZFp6/woQQWptdGpAN4erIUNxIKGIZt+0WfJnPZruGB -lnAJaNp5GtXKQ+ExmofOvLo2KPCrHulf9QZyLakN/gBA0PQ74J5docbJrTld8tX2 -cr4cpwKBgHqr2zybmywAmjn8wY0bUjRAyhdN8eiwYaGPtOSFt6IcWxEnNbAo5Jc2 -KsywpagjFsXZsi4Obn2XsqR7VX5bNbpNXIyLaMwBOy7MixyecgPF8tu7I4zo/CWm -7gewTKBhwVPTDAOzHqIpJGrOnUgzJM3ijkCWMn3eAh4ccOjsrKq9 ------END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/base/app/novnc/utils/websockify/tests/fixtures/public.pem b/base/app/novnc/utils/websockify/tests/fixtures/public.pem deleted file mode 100644 index 7b1d284..0000000 --- a/base/app/novnc/utils/websockify/tests/fixtures/public.pem +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArwNQal2INbSfoVT50dZ0 -s8lQ+yMhu45TDc91iuwtDjlFBJ50E4m3/M6ESBW0S7UTP1bIOGkd/M+u38h0Aruo -4qkngdguu9N3BnsU2kOeicdjxc+vtqRc7/kbkTdT4SrpG8EFP6T2U9U1gtBpLnau -02gPrzjaQzyYDLGOBq+Ozt/mN0YJUhJ3hlwi18dNKreTzWgJ6mmXQWS0eAmHx8TI -s2Nz9x3EfRo9CIMuaaeUjRogIEg5Tg4xC00ZtDO0/EfgpFkeHJGVQA2DgdBJsr6r -q69MjhMfFxRJItxJMJzP6an2HkJ8onUPBtjEBmk3/fnfiaflzRyEb5zdii6r2TD8 -TwIDAQAB ------END PUBLIC KEY----- diff --git a/base/app/novnc/utils/websockify/tests/fixtures/symmetric.key b/base/app/novnc/utils/websockify/tests/fixtures/symmetric.key deleted file mode 100644 index 668b39c..0000000 --- a/base/app/novnc/utils/websockify/tests/fixtures/symmetric.key +++ /dev/null @@ -1 +0,0 @@ -secret_sauce \ No newline at end of file diff --git a/base/app/novnc/utils/websockify/tests/latency.html b/base/app/novnc/utils/websockify/tests/latency.html deleted file mode 100644 index 9904a02..0000000 --- a/base/app/novnc/utils/websockify/tests/latency.html +++ /dev/null @@ -1,261 +0,0 @@ - - - - WebSockets Latency Test - - - - - Host:   - Port:   - Encrypt: -
- Payload Size:   - Send Delay (ms):   -   - -

- - - - - - - - - - - - - - - - - - - - -
Packets sent:
Packets Received:
Average Latency:
40 Frame Running Average Latency:
Minimum Latency:
Maximum Latency:
- -
- Messages:
- - - - - - - diff --git a/base/app/novnc/utils/websockify/tests/latency.py b/base/app/novnc/utils/websockify/tests/latency.py deleted file mode 120000 index 3ae4d96..0000000 --- a/base/app/novnc/utils/websockify/tests/latency.py +++ /dev/null @@ -1 +0,0 @@ -echo.py \ No newline at end of file diff --git a/base/app/novnc/utils/websockify/tests/load.html b/base/app/novnc/utils/websockify/tests/load.html deleted file mode 100644 index cb55041..0000000 --- a/base/app/novnc/utils/websockify/tests/load.html +++ /dev/null @@ -1,223 +0,0 @@ - - - - WebSockets Load Test - - - - - Host:   - Port:   - Encrypt:   - Send Delay (ms):   -   - -

- - - - - - - - - - - -
Packets sent:
0
Good Packets Received:
0
Errors (Bad Packets Received:)
0
- -
- Errors:
- - - - - - - diff --git a/base/app/novnc/utils/websockify/tests/load.py b/base/app/novnc/utils/websockify/tests/load.py deleted file mode 100755 index 710b593..0000000 --- a/base/app/novnc/utils/websockify/tests/load.py +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/env python - -''' -WebSocket server-side load test program. Sends and receives traffic -that has a random payload (length and content) that is checksummed and -given a sequence number. Any errors are reported and counted. -''' - -import sys, os, select, random, time, optparse, logging -sys.path.insert(0,os.path.join(os.path.dirname(__file__), "..")) -from websockify.websockifyserver import WebSockifyServer, WebSockifyRequestHandler - -class WebSocketLoadServer(WebSockifyServer): - - recv_cnt = 0 - send_cnt = 0 - - def __init__(self, *args, **kwargs): - self.delay = kwargs.pop('delay') - - WebSockifyServer.__init__(self, *args, **kwargs) - - -class WebSocketLoad(WebSockifyRequestHandler): - - max_packet_size = 10000 - - def new_websocket_client(self): - print "Prepopulating random array" - self.rand_array = [] - for i in range(0, self.max_packet_size): - self.rand_array.append(random.randint(0, 9)) - - self.errors = 0 - self.send_cnt = 0 - self.recv_cnt = 0 - - self.responder(self.request) - - print "accumulated errors:", self.errors - self.errors = 0 - - def responder(self, client): - c_pend = 0 - cqueue = [] - cpartial = "" - socks = [client] - last_send = time.time() * 1000 - - while True: - ins, outs, excepts = select.select(socks, socks, socks, 1) - if excepts: raise Exception("Socket exception") - - if client in ins: - frames, closed = self.recv_frames() - - err = self.check(frames) - if err: - self.errors = self.errors + 1 - print err - - if closed: - break - - now = time.time() * 1000 - if client in outs: - if c_pend: - last_send = now - c_pend = self.send_frames() - elif now > (last_send + self.server.delay): - last_send = now - c_pend = self.send_frames([self.generate()]) - - def generate(self): - length = random.randint(10, self.max_packet_size) - numlist = self.rand_array[self.max_packet_size-length:] - # Error in length - #numlist.append(5) - chksum = sum(numlist) - # Error in checksum - #numlist[0] = 5 - nums = "".join( [str(n) for n in numlist] ) - data = "^%d:%d:%d:%s$" % (self.send_cnt, length, chksum, nums) - self.send_cnt += 1 - - return data - - - def check(self, frames): - - err = "" - for data in frames: - if data.count('$') > 1: - raise Exception("Multiple parts within single packet") - if len(data) == 0: - self.traffic("_") - continue - - if data[0] != "^": - err += "buf did not start with '^'\n" - continue - - try: - cnt, length, chksum, nums = data[1:-1].split(':') - cnt = int(cnt) - length = int(length) - chksum = int(chksum) - except ValueError: - print "\n" + repr(data) + "" - err += "Invalid data format\n" - continue - - if self.recv_cnt != cnt: - err += "Expected count %d but got %d\n" % (self.recv_cnt, cnt) - self.recv_cnt = cnt + 1 - continue - - self.recv_cnt += 1 - - if len(nums) != length: - err += "Expected length %d but got %d\n" % (length, len(nums)) - continue - - inv = nums.translate(None, "0123456789") - if inv: - err += "Invalid characters found: %s\n" % inv - continue - - real_chksum = 0 - for num in nums: - real_chksum += int(num) - - if real_chksum != chksum: - err += "Expected checksum %d but real chksum is %d\n" % (chksum, real_chksum) - return err - - -if __name__ == '__main__': - parser = optparse.OptionParser(usage="%prog [options] listen_port") - parser.add_option("--verbose", "-v", action="store_true", - help="verbose messages and per frame traffic") - parser.add_option("--cert", default="self.pem", - help="SSL certificate file") - parser.add_option("--key", default=None, - help="SSL key file (if separate from cert)") - parser.add_option("--ssl-only", action="store_true", - help="disallow non-encrypted connections") - (opts, args) = parser.parse_args() - - try: - if len(args) != 1: raise ValueError - opts.listen_port = int(args[0]) - - if len(args) not in [1,2]: raise ValueError - opts.listen_port = int(args[0]) - if len(args) == 2: - opts.delay = int(args[1]) - else: - opts.delay = 10 - except ValueError: - parser.error("Invalid arguments") - - logging.basicConfig(level=logging.INFO) - - opts.web = "." - server = WebSocketLoadServer(WebSocketLoad, **opts.__dict__) - server.start_server() - diff --git a/base/app/novnc/utils/websockify/tests/plain_echo.html b/base/app/novnc/utils/websockify/tests/plain_echo.html deleted file mode 100644 index be35e54..0000000 --- a/base/app/novnc/utils/websockify/tests/plain_echo.html +++ /dev/null @@ -1,145 +0,0 @@ - - - - WebSockets Echo Test - - - - - Host:   - Port:   - Encrypt:   -   - - -
- Log:
- - - - - - - diff --git a/base/app/novnc/utils/websockify/tests/simple.html b/base/app/novnc/utils/websockify/tests/simple.html deleted file mode 100644 index 8f6a73f..0000000 --- a/base/app/novnc/utils/websockify/tests/simple.html +++ /dev/null @@ -1,65 +0,0 @@ - - - - Websock Simple Client - - - - WebSocket/websockify URI:   - -

-   -   -

- Log:
- - - - - - diff --git a/base/app/novnc/utils/websockify/tests/test_auth_plugins.py b/base/app/novnc/utils/websockify/tests/test_auth_plugins.py deleted file mode 100644 index 4b3bfb5..0000000 --- a/base/app/novnc/utils/websockify/tests/test_auth_plugins.py +++ /dev/null @@ -1,28 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -""" Unit tests for Authentication plugins""" - -from websockify.auth_plugins import BasicHTTPAuth, AuthenticationError -import unittest - - -class BasicHTTPAuthTestCase(unittest.TestCase): - - def setUp(self): - self.plugin = BasicHTTPAuth('Aladdin:open sesame') - - def test_no_auth(self): - headers = {} - self.assertRaises(AuthenticationError, self.plugin.authenticate, headers, 'localhost', '1234') - - def test_invalid_password(self): - headers = {'Authorization': 'Basic QWxhZGRpbjpzZXNhbWUgc3RyZWV0'} - self.assertRaises(AuthenticationError, self.plugin.authenticate, headers, 'localhost', '1234') - - def test_valid_password(self): - headers = {'Authorization': 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='} - self.plugin.authenticate(headers, 'localhost', '1234') - - def test_garbage_auth(self): - headers = {'Authorization': 'Basic xxxxxxxxxxxxxxxxxxxxxxxxxxxx'} - self.assertRaises(AuthenticationError, self.plugin.authenticate, headers, 'localhost', '1234') diff --git a/base/app/novnc/utils/websockify/tests/test_token_plugins.py b/base/app/novnc/utils/websockify/tests/test_token_plugins.py deleted file mode 100644 index f09ae8a..0000000 --- a/base/app/novnc/utils/websockify/tests/test_token_plugins.py +++ /dev/null @@ -1,364 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -""" Unit tests for Token plugins""" - -import sys -import unittest -from unittest.mock import patch, mock_open, MagicMock -from jwcrypto import jwt, jwk - -from websockify.token_plugins import ReadOnlyTokenFile, JWTTokenApi, TokenRedis - -class ReadOnlyTokenFileTestCase(unittest.TestCase): - patch('os.path.isdir', MagicMock(return_value=False)) - def test_empty(self): - plugin = ReadOnlyTokenFile('configfile') - - config = "" - pyopen = mock_open(read_data=config) - - with patch("websockify.token_plugins.open", pyopen, create=True): - result = plugin.lookup('testhost') - - pyopen.assert_called_once_with('configfile') - self.assertIsNone(result) - - patch('os.path.isdir', MagicMock(return_value=False)) - def test_simple(self): - plugin = ReadOnlyTokenFile('configfile') - - config = "testhost: remote_host:remote_port" - pyopen = mock_open(read_data=config) - - with patch("websockify.token_plugins.open", pyopen, create=True): - result = plugin.lookup('testhost') - - pyopen.assert_called_once_with('configfile') - self.assertIsNotNone(result) - self.assertEqual(result[0], "remote_host") - self.assertEqual(result[1], "remote_port") - - patch('os.path.isdir', MagicMock(return_value=False)) - def test_tabs(self): - plugin = ReadOnlyTokenFile('configfile') - - config = "testhost:\tremote_host:remote_port" - pyopen = mock_open(read_data=config) - - with patch("websockify.token_plugins.open", pyopen, create=True): - result = plugin.lookup('testhost') - - pyopen.assert_called_once_with('configfile') - self.assertIsNotNone(result) - self.assertEqual(result[0], "remote_host") - self.assertEqual(result[1], "remote_port") - -class JWSTokenTestCase(unittest.TestCase): - def test_asymmetric_jws_token_plugin(self): - plugin = JWTTokenApi("./tests/fixtures/public.pem") - - key = jwk.JWK() - private_key = open("./tests/fixtures/private.pem", "rb").read() - key.import_from_pem(private_key) - jwt_token = jwt.JWT({"alg": "RS256"}, {'host': "remote_host", 'port': "remote_port"}) - jwt_token.make_signed_token(key) - - result = plugin.lookup(jwt_token.serialize()) - - self.assertIsNotNone(result) - self.assertEqual(result[0], "remote_host") - self.assertEqual(result[1], "remote_port") - - def test_asymmetric_jws_token_plugin_with_illigal_key_exception(self): - plugin = JWTTokenApi("wrong.pub") - - key = jwk.JWK() - private_key = open("./tests/fixtures/private.pem", "rb").read() - key.import_from_pem(private_key) - jwt_token = jwt.JWT({"alg": "RS256"}, {'host': "remote_host", 'port': "remote_port"}) - jwt_token.make_signed_token(key) - - result = plugin.lookup(jwt_token.serialize()) - - self.assertIsNone(result) - - @patch('time.time') - def test_jwt_valid_time(self, mock_time): - plugin = JWTTokenApi("./tests/fixtures/public.pem") - - key = jwk.JWK() - private_key = open("./tests/fixtures/private.pem", "rb").read() - key.import_from_pem(private_key) - jwt_token = jwt.JWT({"alg": "RS256"}, {'host': "remote_host", 'port': "remote_port", 'nbf': 100, 'exp': 200 }) - jwt_token.make_signed_token(key) - mock_time.return_value = 150 - - result = plugin.lookup(jwt_token.serialize()) - - self.assertIsNotNone(result) - self.assertEqual(result[0], "remote_host") - self.assertEqual(result[1], "remote_port") - - @patch('time.time') - def test_jwt_early_time(self, mock_time): - plugin = JWTTokenApi("./tests/fixtures/public.pem") - - key = jwk.JWK() - private_key = open("./tests/fixtures/private.pem", "rb").read() - key.import_from_pem(private_key) - jwt_token = jwt.JWT({"alg": "RS256"}, {'host': "remote_host", 'port': "remote_port", 'nbf': 100, 'exp': 200 }) - jwt_token.make_signed_token(key) - mock_time.return_value = 50 - - result = plugin.lookup(jwt_token.serialize()) - - self.assertIsNone(result) - - @patch('time.time') - def test_jwt_late_time(self, mock_time): - plugin = JWTTokenApi("./tests/fixtures/public.pem") - - key = jwk.JWK() - private_key = open("./tests/fixtures/private.pem", "rb").read() - key.import_from_pem(private_key) - jwt_token = jwt.JWT({"alg": "RS256"}, {'host': "remote_host", 'port': "remote_port", 'nbf': 100, 'exp': 200 }) - jwt_token.make_signed_token(key) - mock_time.return_value = 250 - - result = plugin.lookup(jwt_token.serialize()) - - self.assertIsNone(result) - - def test_symmetric_jws_token_plugin(self): - plugin = JWTTokenApi("./tests/fixtures/symmetric.key") - - secret = open("./tests/fixtures/symmetric.key").read() - key = jwk.JWK() - key.import_key(kty="oct",k=secret) - jwt_token = jwt.JWT({"alg": "HS256"}, {'host': "remote_host", 'port': "remote_port"}) - jwt_token.make_signed_token(key) - - result = plugin.lookup(jwt_token.serialize()) - - self.assertIsNotNone(result) - self.assertEqual(result[0], "remote_host") - self.assertEqual(result[1], "remote_port") - - def test_symmetric_jws_token_plugin_with_illigal_key_exception(self): - plugin = JWTTokenApi("wrong_sauce") - - secret = open("./tests/fixtures/symmetric.key").read() - key = jwk.JWK() - key.import_key(kty="oct",k=secret) - jwt_token = jwt.JWT({"alg": "HS256"}, {'host': "remote_host", 'port': "remote_port"}) - jwt_token.make_signed_token(key) - - result = plugin.lookup(jwt_token.serialize()) - - self.assertIsNone(result) - - def test_asymmetric_jwe_token_plugin(self): - plugin = JWTTokenApi("./tests/fixtures/private.pem") - - private_key = jwk.JWK() - public_key = jwk.JWK() - private_key_data = open("./tests/fixtures/private.pem", "rb").read() - public_key_data = open("./tests/fixtures/public.pem", "rb").read() - private_key.import_from_pem(private_key_data) - public_key.import_from_pem(public_key_data) - jwt_token = jwt.JWT({"alg": "RS256"}, {'host': "remote_host", 'port': "remote_port"}) - jwt_token.make_signed_token(private_key) - jwe_token = jwt.JWT(header={"alg": "RSA-OAEP", "enc": "A256CBC-HS512"}, - claims=jwt_token.serialize()) - jwe_token.make_encrypted_token(public_key) - - result = plugin.lookup(jwt_token.serialize()) - - self.assertIsNotNone(result) - self.assertEqual(result[0], "remote_host") - self.assertEqual(result[1], "remote_port") - -class TokenRedisTestCase(unittest.TestCase): - def setUp(self): - try: - import redis - except ImportError: - patcher = patch.dict(sys.modules, {'redis': MagicMock()}) - patcher.start() - self.addCleanup(patcher.stop) - - @patch('redis.Redis') - def test_empty(self, mock_redis): - plugin = TokenRedis('127.0.0.1:1234') - - instance = mock_redis.return_value - instance.get.return_value = None - - result = plugin.lookup('testhost') - - instance.get.assert_called_once_with('testhost') - self.assertIsNone(result) - - @patch('redis.Redis') - def test_simple(self, mock_redis): - plugin = TokenRedis('127.0.0.1:1234') - - instance = mock_redis.return_value - instance.get.return_value = b'{"host": "remote_host:remote_port"}' - - result = plugin.lookup('testhost') - - instance.get.assert_called_once_with('testhost') - self.assertIsNotNone(result) - self.assertEqual(result[0], 'remote_host') - self.assertEqual(result[1], 'remote_port') - - @patch('redis.Redis') - def test_json_token_with_spaces(self, mock_redis): - plugin = TokenRedis('127.0.0.1:1234') - - instance = mock_redis.return_value - instance.get.return_value = b' {"host": "remote_host:remote_port"} ' - - result = plugin.lookup('testhost') - - instance.get.assert_called_once_with('testhost') - self.assertIsNotNone(result) - self.assertEqual(result[0], 'remote_host') - self.assertEqual(result[1], 'remote_port') - - @patch('redis.Redis') - def test_text_token(self, mock_redis): - plugin = TokenRedis('127.0.0.1:1234') - - instance = mock_redis.return_value - instance.get.return_value = b'remote_host:remote_port' - - result = plugin.lookup('testhost') - - instance.get.assert_called_once_with('testhost') - self.assertIsNotNone(result) - self.assertEqual(result[0], 'remote_host') - self.assertEqual(result[1], 'remote_port') - - @patch('redis.Redis') - def test_text_token_with_spaces(self, mock_redis): - plugin = TokenRedis('127.0.0.1:1234') - - instance = mock_redis.return_value - instance.get.return_value = b' remote_host:remote_port ' - - result = plugin.lookup('testhost') - - instance.get.assert_called_once_with('testhost') - self.assertIsNotNone(result) - self.assertEqual(result[0], 'remote_host') - self.assertEqual(result[1], 'remote_port') - - @patch('redis.Redis') - def test_invalid_token(self, mock_redis): - plugin = TokenRedis('127.0.0.1:1234') - - instance = mock_redis.return_value - instance.get.return_value = b'{"host": "remote_host:remote_port" ' - - result = plugin.lookup('testhost') - - instance.get.assert_called_once_with('testhost') - self.assertIsNone(result) - - def test_src_only_host(self): - plugin = TokenRedis('127.0.0.1') - - self.assertEqual(plugin._server, '127.0.0.1') - self.assertEqual(plugin._port, 6379) - self.assertEqual(plugin._db, 0) - self.assertEqual(plugin._password, None) - - def test_src_with_host_port(self): - plugin = TokenRedis('127.0.0.1:1234') - - self.assertEqual(plugin._server, '127.0.0.1') - self.assertEqual(plugin._port, 1234) - self.assertEqual(plugin._db, 0) - self.assertEqual(plugin._password, None) - - def test_src_with_host_port_db(self): - plugin = TokenRedis('127.0.0.1:1234:2') - - self.assertEqual(plugin._server, '127.0.0.1') - self.assertEqual(plugin._port, 1234) - self.assertEqual(plugin._db, 2) - self.assertEqual(plugin._password, None) - - def test_src_with_host_port_db_pass(self): - plugin = TokenRedis('127.0.0.1:1234:2:verysecret') - - self.assertEqual(plugin._server, '127.0.0.1') - self.assertEqual(plugin._port, 1234) - self.assertEqual(plugin._db, 2) - self.assertEqual(plugin._password, 'verysecret') - - def test_src_with_host_empty_port_empty_db_pass(self): - plugin = TokenRedis('127.0.0.1:::verysecret') - - self.assertEqual(plugin._server, '127.0.0.1') - self.assertEqual(plugin._port, 6379) - self.assertEqual(plugin._db, 0) - self.assertEqual(plugin._password, 'verysecret') - - def test_src_with_host_empty_port_empty_db_empty_pass(self): - plugin = TokenRedis('127.0.0.1:::') - - self.assertEqual(plugin._server, '127.0.0.1') - self.assertEqual(plugin._port, 6379) - self.assertEqual(plugin._db, 0) - self.assertEqual(plugin._password, None) - - def test_src_with_host_empty_port_empty_db_no_pass(self): - plugin = TokenRedis('127.0.0.1::') - - self.assertEqual(plugin._server, '127.0.0.1') - self.assertEqual(plugin._port, 6379) - self.assertEqual(plugin._db, 0) - self.assertEqual(plugin._password, None) - - def test_src_with_host_empty_port_no_db_no_pass(self): - plugin = TokenRedis('127.0.0.1:') - - self.assertEqual(plugin._server, '127.0.0.1') - self.assertEqual(plugin._port, 6379) - self.assertEqual(plugin._db, 0) - self.assertEqual(plugin._password, None) - - def test_src_with_host_empty_port_db_no_pass(self): - plugin = TokenRedis('127.0.0.1::2') - - self.assertEqual(plugin._server, '127.0.0.1') - self.assertEqual(plugin._port, 6379) - self.assertEqual(plugin._db, 2) - self.assertEqual(plugin._password, None) - - def test_src_with_host_port_empty_db_pass(self): - plugin = TokenRedis('127.0.0.1:1234::verysecret') - - self.assertEqual(plugin._server, '127.0.0.1') - self.assertEqual(plugin._port, 1234) - self.assertEqual(plugin._db, 0) - self.assertEqual(plugin._password, 'verysecret') - - def test_src_with_host_empty_port_db_pass(self): - plugin = TokenRedis('127.0.0.1::2:verysecret') - - self.assertEqual(plugin._server, '127.0.0.1') - self.assertEqual(plugin._port, 6379) - self.assertEqual(plugin._db, 2) - self.assertEqual(plugin._password, 'verysecret') - - def test_src_with_host_empty_port_db_empty_pass(self): - plugin = TokenRedis('127.0.0.1::2:') - - self.assertEqual(plugin._server, '127.0.0.1') - self.assertEqual(plugin._port, 6379) - self.assertEqual(plugin._db, 2) - self.assertEqual(plugin._password, None) diff --git a/base/app/novnc/utils/websockify/tests/test_websocket.py b/base/app/novnc/utils/websockify/tests/test_websocket.py deleted file mode 100644 index 8ee44f9..0000000 --- a/base/app/novnc/utils/websockify/tests/test_websocket.py +++ /dev/null @@ -1,212 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright(c)2013 NTT corp. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" Unit tests for websocket """ -import unittest -from websockify import websocket - -class FakeSocket: - def __init__(self): - self.data = b'' - - def send(self, buf): - self.data += buf - return len(buf) - -class AcceptTestCase(unittest.TestCase): - def test_success(self): - ws = websocket.WebSocket() - sock = FakeSocket() - ws.accept(sock, {'upgrade': 'websocket', - 'Sec-WebSocket-Version': '13', - 'Sec-WebSocket-Key': 'DKURYVK9cRFul1vOZVA56Q=='}) - self.assertEqual(sock.data[:13], b'HTTP/1.1 101 ') - self.assertTrue(b'\r\nUpgrade: websocket\r\n' in sock.data) - self.assertTrue(b'\r\nConnection: Upgrade\r\n' in sock.data) - self.assertTrue(b'\r\nSec-WebSocket-Accept: pczpYSQsvE1vBpTQYjFQPcuoj6M=\r\n' in sock.data) - - def test_bad_version(self): - ws = websocket.WebSocket() - sock = FakeSocket() - self.assertRaises(Exception, ws.accept, - sock, {'upgrade': 'websocket', - 'Sec-WebSocket-Key': 'DKURYVK9cRFul1vOZVA56Q=='}) - self.assertRaises(Exception, ws.accept, - sock, {'upgrade': 'websocket', - 'Sec-WebSocket-Version': '5', - 'Sec-WebSocket-Key': 'DKURYVK9cRFul1vOZVA56Q=='}) - self.assertRaises(Exception, ws.accept, - sock, {'upgrade': 'websocket', - 'Sec-WebSocket-Version': '20', - 'Sec-WebSocket-Key': 'DKURYVK9cRFul1vOZVA56Q=='}) - - def test_bad_upgrade(self): - ws = websocket.WebSocket() - sock = FakeSocket() - self.assertRaises(Exception, ws.accept, - sock, {'Sec-WebSocket-Version': '13', - 'Sec-WebSocket-Key': 'DKURYVK9cRFul1vOZVA56Q=='}) - self.assertRaises(Exception, ws.accept, - sock, {'upgrade': 'websocket2', - 'Sec-WebSocket-Version': '13', - 'Sec-WebSocket-Key': 'DKURYVK9cRFul1vOZVA56Q=='}) - - def test_missing_key(self): - ws = websocket.WebSocket() - sock = FakeSocket() - self.assertRaises(Exception, ws.accept, - sock, {'upgrade': 'websocket', - 'Sec-WebSocket-Version': '13'}) - - def test_protocol(self): - class ProtoSocket(websocket.WebSocket): - def select_subprotocol(self, protocol): - return 'gazonk' - - ws = ProtoSocket() - sock = FakeSocket() - ws.accept(sock, {'upgrade': 'websocket', - 'Sec-WebSocket-Version': '13', - 'Sec-WebSocket-Key': 'DKURYVK9cRFul1vOZVA56Q==', - 'Sec-WebSocket-Protocol': 'foobar gazonk'}) - self.assertEqual(sock.data[:13], b'HTTP/1.1 101 ') - self.assertTrue(b'\r\nSec-WebSocket-Protocol: gazonk\r\n' in sock.data) - - def test_no_protocol(self): - ws = websocket.WebSocket() - sock = FakeSocket() - ws.accept(sock, {'upgrade': 'websocket', - 'Sec-WebSocket-Version': '13', - 'Sec-WebSocket-Key': 'DKURYVK9cRFul1vOZVA56Q=='}) - self.assertEqual(sock.data[:13], b'HTTP/1.1 101 ') - self.assertFalse(b'\r\nSec-WebSocket-Protocol:' in sock.data) - - def test_missing_protocol(self): - ws = websocket.WebSocket() - sock = FakeSocket() - self.assertRaises(Exception, ws.accept, - sock, {'upgrade': 'websocket', - 'Sec-WebSocket-Version': '13', - 'Sec-WebSocket-Key': 'DKURYVK9cRFul1vOZVA56Q==', - 'Sec-WebSocket-Protocol': 'foobar gazonk'}) - - def test_protocol(self): - class ProtoSocket(websocket.WebSocket): - def select_subprotocol(self, protocol): - return 'oddball' - - ws = ProtoSocket() - sock = FakeSocket() - self.assertRaises(Exception, ws.accept, - sock, {'upgrade': 'websocket', - 'Sec-WebSocket-Version': '13', - 'Sec-WebSocket-Key': 'DKURYVK9cRFul1vOZVA56Q==', - 'Sec-WebSocket-Protocol': 'foobar gazonk'}) - -class PingPongTest(unittest.TestCase): - def setUp(self): - self.ws = websocket.WebSocket() - self.sock = FakeSocket() - self.ws.accept(self.sock, {'upgrade': 'websocket', - 'Sec-WebSocket-Version': '13', - 'Sec-WebSocket-Key': 'DKURYVK9cRFul1vOZVA56Q=='}) - self.assertEqual(self.sock.data[:13], b'HTTP/1.1 101 ') - self.sock.data = b'' - - def test_ping(self): - self.ws.ping() - self.assertEqual(self.sock.data, b'\x89\x00') - - def test_pong(self): - self.ws.pong() - self.assertEqual(self.sock.data, b'\x8a\x00') - - def test_ping_data(self): - self.ws.ping(b'foo') - self.assertEqual(self.sock.data, b'\x89\x03foo') - - def test_pong_data(self): - self.ws.pong(b'foo') - self.assertEqual(self.sock.data, b'\x8a\x03foo') - -class HyBiEncodeDecodeTestCase(unittest.TestCase): - def test_decode_hybi_text(self): - buf = b'\x81\x85\x37\xfa\x21\x3d\x7f\x9f\x4d\x51\x58' - ws = websocket.WebSocket() - res = ws._decode_hybi(buf) - - self.assertEqual(res['fin'], 1) - self.assertEqual(res['opcode'], 0x1) - self.assertEqual(res['masked'], True) - self.assertEqual(res['length'], len(buf)) - self.assertEqual(res['payload'], b'Hello') - - def test_decode_hybi_binary(self): - buf = b'\x82\x04\x01\x02\x03\x04' - ws = websocket.WebSocket() - res = ws._decode_hybi(buf) - - self.assertEqual(res['fin'], 1) - self.assertEqual(res['opcode'], 0x2) - self.assertEqual(res['length'], len(buf)) - self.assertEqual(res['payload'], b'\x01\x02\x03\x04') - - def test_decode_hybi_extended_16bit_binary(self): - data = (b'\x01\x02\x03\x04' * 65) # len > 126 -- len == 260 - buf = b'\x82\x7e\x01\x04' + data - ws = websocket.WebSocket() - res = ws._decode_hybi(buf) - - self.assertEqual(res['fin'], 1) - self.assertEqual(res['opcode'], 0x2) - self.assertEqual(res['length'], len(buf)) - self.assertEqual(res['payload'], data) - - def test_decode_hybi_extended_64bit_binary(self): - data = (b'\x01\x02\x03\x04' * 65) # len > 126 -- len == 260 - buf = b'\x82\x7f\x00\x00\x00\x00\x00\x00\x01\x04' + data - ws = websocket.WebSocket() - res = ws._decode_hybi(buf) - - self.assertEqual(res['fin'], 1) - self.assertEqual(res['opcode'], 0x2) - self.assertEqual(res['length'], len(buf)) - self.assertEqual(res['payload'], data) - - def test_decode_hybi_multi(self): - buf1 = b'\x01\x03\x48\x65\x6c' - buf2 = b'\x80\x02\x6c\x6f' - - ws = websocket.WebSocket() - - res1 = ws._decode_hybi(buf1) - self.assertEqual(res1['fin'], 0) - self.assertEqual(res1['opcode'], 0x1) - self.assertEqual(res1['length'], len(buf1)) - self.assertEqual(res1['payload'], b'Hel') - - res2 = ws._decode_hybi(buf2) - self.assertEqual(res2['fin'], 1) - self.assertEqual(res2['opcode'], 0x0) - self.assertEqual(res2['length'], len(buf2)) - self.assertEqual(res2['payload'], b'lo') - - def test_encode_hybi_basic(self): - ws = websocket.WebSocket() - res = ws._encode_hybi(0x1, b'Hello') - expected = b'\x81\x05\x48\x65\x6c\x6c\x6f' - - self.assertEqual(res, expected) diff --git a/base/app/novnc/utils/websockify/tests/test_websocketproxy.py b/base/app/novnc/utils/websockify/tests/test_websocketproxy.py deleted file mode 100644 index a05e3b1..0000000 --- a/base/app/novnc/utils/websockify/tests/test_websocketproxy.py +++ /dev/null @@ -1,131 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright(c) 2015 Red Hat, Inc All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" Unit tests for websocketproxy """ - -import sys -import unittest -import unittest -import socket -from io import StringIO -from io import BytesIO -from unittest.mock import patch, MagicMock - -from websockify import websocketproxy -from websockify import token_plugins -from websockify import auth_plugins - - -class FakeSocket(object): - def __init__(self, data=b''): - self._data = data - - def recv(self, amt, flags=None): - res = self._data[0:amt] - if not (flags & socket.MSG_PEEK): - self._data = self._data[amt:] - - return res - - def makefile(self, mode='r', buffsize=None): - if 'b' in mode: - return BytesIO(self._data) - else: - return StringIO(self._data.decode('latin_1')) - - -class FakeServer(object): - class EClose(Exception): - pass - - def __init__(self): - self.token_plugin = None - self.auth_plugin = None - self.wrap_cmd = None - self.ssl_target = None - self.unix_target = None - -class ProxyRequestHandlerTestCase(unittest.TestCase): - def setUp(self): - super(ProxyRequestHandlerTestCase, self).setUp() - self.handler = websocketproxy.ProxyRequestHandler( - FakeSocket(), "127.0.0.1", FakeServer()) - self.handler.path = "https://localhost:6080/websockify?token=blah" - self.handler.headers = None - patch('websockify.websockifyserver.WebSockifyServer.socket').start() - - def tearDown(self): - patch.stopall() - super(ProxyRequestHandlerTestCase, self).tearDown() - - def test_get_target(self): - class TestPlugin(token_plugins.BasePlugin): - def lookup(self, token): - return ("some host", "some port") - - host, port = self.handler.get_target( - TestPlugin(None)) - - self.assertEqual(host, "some host") - self.assertEqual(port, "some port") - - def test_get_target_unix_socket(self): - class TestPlugin(token_plugins.BasePlugin): - def lookup(self, token): - return ("unix_socket", "/tmp/socket") - - _, socket = self.handler.get_target( - TestPlugin(None)) - - self.assertEqual(socket, "/tmp/socket") - - def test_get_target_raises_error_on_unknown_token(self): - class TestPlugin(token_plugins.BasePlugin): - def lookup(self, token): - return None - - with self.assertRaises(FakeServer.EClose): - self.handler.get_target(TestPlugin(None)) - - @patch('websockify.websocketproxy.ProxyRequestHandler.send_auth_error', MagicMock()) - def test_token_plugin(self): - class TestPlugin(token_plugins.BasePlugin): - def lookup(self, token): - return (self.source + token).split(',') - - self.handler.server.token_plugin = TestPlugin("somehost,") - self.handler.validate_connection() - - self.assertEqual(self.handler.server.target_host, "somehost") - self.assertEqual(self.handler.server.target_port, "blah") - - @patch('websockify.websocketproxy.ProxyRequestHandler.send_auth_error', MagicMock()) - def test_auth_plugin(self): - class TestPlugin(auth_plugins.BasePlugin): - def authenticate(self, headers, target_host, target_port): - if target_host == self.source: - raise auth_plugins.AuthenticationError(response_msg="some_error") - - self.handler.server.auth_plugin = TestPlugin("somehost") - self.handler.server.target_host = "somehost" - self.handler.server.target_port = "someport" - - with self.assertRaises(auth_plugins.AuthenticationError): - self.handler.auth_connection() - - self.handler.server.target_host = "someotherhost" - self.handler.auth_connection() - diff --git a/base/app/novnc/utils/websockify/tests/test_websocketserver.py b/base/app/novnc/utils/websockify/tests/test_websocketserver.py deleted file mode 100644 index 0e37e3d..0000000 --- a/base/app/novnc/utils/websockify/tests/test_websocketserver.py +++ /dev/null @@ -1,69 +0,0 @@ - -""" Unit tests for websocketserver """ -import unittest -from unittest.mock import patch, MagicMock - -from websockify.websocketserver import HttpWebSocket - - -class HttpWebSocketTest(unittest.TestCase): - @patch("websockify.websocketserver.WebSocket.__init__", autospec=True) - def test_constructor(self, websock): - # Given - req_obj = MagicMock() - - # When - sock = HttpWebSocket(req_obj) - - # Then - websock.assert_called_once_with(sock) - self.assertEqual(sock.request_handler, req_obj) - - @patch("websockify.websocketserver.WebSocket.__init__", MagicMock(autospec=True)) - def test_send_response(self): - # Given - req_obj = MagicMock() - sock = HttpWebSocket(req_obj) - - # When - sock.send_response(200, "message") - - # Then - req_obj.send_response.assert_called_once_with(200, "message") - - @patch("websockify.websocketserver.WebSocket.__init__", MagicMock(autospec=True)) - def test_send_response_default_message(self): - # Given - req_obj = MagicMock() - sock = HttpWebSocket(req_obj) - - # When - sock.send_response(200) - - # Then - req_obj.send_response.assert_called_once_with(200, None) - - @patch("websockify.websocketserver.WebSocket.__init__", MagicMock(autospec=True)) - def test_send_header(self): - # Given - req_obj = MagicMock() - sock = HttpWebSocket(req_obj) - - # When - sock.send_header("keyword", "value") - - # Then - req_obj.send_header.assert_called_once_with("keyword", "value") - - @patch("websockify.websocketserver.WebSocket.__init__", MagicMock(autospec=True)) - def test_end_headers(self): - # Given - req_obj = MagicMock() - sock = HttpWebSocket(req_obj) - - # When - sock.end_headers() - - # Then - req_obj.end_headers.assert_called_once_with() - diff --git a/base/app/novnc/utils/websockify/tests/test_websockifyserver.py b/base/app/novnc/utils/websockify/tests/test_websockifyserver.py deleted file mode 100644 index 79d1b25..0000000 --- a/base/app/novnc/utils/websockify/tests/test_websockifyserver.py +++ /dev/null @@ -1,400 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright(c)2013 NTT corp. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" Unit tests for websockifyserver """ -import errno -import os -import logging -import select -import shutil -import socket -import ssl -from unittest.mock import patch, MagicMock, ANY -import sys -import tempfile -import unittest -import socket -import signal -from http.server import BaseHTTPRequestHandler -from io import StringIO -from io import BytesIO - -from websockify import websockifyserver - - -def raise_oserror(*args, **kwargs): - raise OSError('fake error') - - -class FakeSocket(object): - def __init__(self, data=b''): - self._data = data - - def recv(self, amt, flags=None): - res = self._data[0:amt] - if not (flags & socket.MSG_PEEK): - self._data = self._data[amt:] - - return res - - def makefile(self, mode='r', buffsize=None): - if 'b' in mode: - return BytesIO(self._data) - else: - return StringIO(self._data.decode('latin_1')) - - -class WebSockifyRequestHandlerTestCase(unittest.TestCase): - def setUp(self): - super(WebSockifyRequestHandlerTestCase, self).setUp() - self.tmpdir = tempfile.mkdtemp('-websockify-tests') - # Mock this out cause it screws tests up - patch('os.chdir').start() - - def tearDown(self): - """Called automatically after each test.""" - patch.stopall() - os.rmdir(self.tmpdir) - super(WebSockifyRequestHandlerTestCase, self).tearDown() - - def _get_server(self, handler_class=websockifyserver.WebSockifyRequestHandler, - **kwargs): - web = kwargs.pop('web', self.tmpdir) - return websockifyserver.WebSockifyServer( - handler_class, listen_host='localhost', - listen_port=80, key=self.tmpdir, web=web, - record=self.tmpdir, daemon=False, ssl_only=0, idle_timeout=1, - **kwargs) - - @patch('websockify.websockifyserver.WebSockifyRequestHandler.send_error') - def test_normal_get_with_only_upgrade_returns_error(self, send_error): - server = self._get_server(web=None) - handler = websockifyserver.WebSockifyRequestHandler( - FakeSocket(b'GET /tmp.txt HTTP/1.1'), '127.0.0.1', server) - - handler.do_GET() - send_error.assert_called_with(405) - - @patch('websockify.websockifyserver.WebSockifyRequestHandler.send_error') - def test_list_dir_with_file_only_returns_error(self, send_error): - server = self._get_server(file_only=True) - handler = websockifyserver.WebSockifyRequestHandler( - FakeSocket(b'GET / HTTP/1.1'), '127.0.0.1', server) - - handler.path = '/' - handler.do_GET() - send_error.assert_called_with(404) - - -class WebSockifyServerTestCase(unittest.TestCase): - def setUp(self): - super(WebSockifyServerTestCase, self).setUp() - self.tmpdir = tempfile.mkdtemp('-websockify-tests') - # Mock this out cause it screws tests up - patch('os.chdir').start() - - def tearDown(self): - """Called automatically after each test.""" - patch.stopall() - os.rmdir(self.tmpdir) - super(WebSockifyServerTestCase, self).tearDown() - - def _get_server(self, handler_class=websockifyserver.WebSockifyRequestHandler, - **kwargs): - return websockifyserver.WebSockifyServer( - handler_class, listen_host='localhost', - listen_port=80, key=self.tmpdir, web=self.tmpdir, - record=self.tmpdir, **kwargs) - - def test_daemonize_raises_error_while_closing_fds(self): - server = self._get_server(daemon=True, ssl_only=1, idle_timeout=1) - patch('os.fork').start().return_value = 0 - patch('signal.signal').start() - patch('os.setsid').start() - patch('os.close').start().side_effect = raise_oserror - self.assertRaises(OSError, server.daemonize, keepfd=None, chdir='./') - - def test_daemonize_ignores_ebadf_error_while_closing_fds(self): - def raise_oserror_ebadf(fd): - raise OSError(errno.EBADF, 'fake error') - - server = self._get_server(daemon=True, ssl_only=1, idle_timeout=1) - patch('os.fork').start().return_value = 0 - patch('signal.signal').start() - patch('os.setsid').start() - patch('os.close').start().side_effect = raise_oserror_ebadf - patch('os.open').start().side_effect = raise_oserror - self.assertRaises(OSError, server.daemonize, keepfd=None, chdir='./') - - def test_handshake_fails_on_not_ready(self): - server = self._get_server(daemon=True, ssl_only=0, idle_timeout=1) - - def fake_select(rlist, wlist, xlist, timeout=None): - return ([], [], []) - - patch('select.select').start().side_effect = fake_select - self.assertRaises( - websockifyserver.WebSockifyServer.EClose, server.do_handshake, - FakeSocket(), '127.0.0.1') - - def test_empty_handshake_fails(self): - server = self._get_server(daemon=True, ssl_only=0, idle_timeout=1) - - sock = FakeSocket('') - - def fake_select(rlist, wlist, xlist, timeout=None): - return ([sock], [], []) - - patch('select.select').start().side_effect = fake_select - self.assertRaises( - websockifyserver.WebSockifyServer.EClose, server.do_handshake, - sock, '127.0.0.1') - - def test_handshake_policy_request(self): - # TODO(directxman12): implement - pass - - def test_handshake_ssl_only_without_ssl_raises_error(self): - server = self._get_server(daemon=True, ssl_only=1, idle_timeout=1) - - sock = FakeSocket(b'some initial data') - - def fake_select(rlist, wlist, xlist, timeout=None): - return ([sock], [], []) - - patch('select.select').start().side_effect = fake_select - self.assertRaises( - websockifyserver.WebSockifyServer.EClose, server.do_handshake, - sock, '127.0.0.1') - - def test_do_handshake_no_ssl(self): - class FakeHandler(object): - CALLED = False - def __init__(self, *args, **kwargs): - type(self).CALLED = True - - FakeHandler.CALLED = False - - server = self._get_server( - handler_class=FakeHandler, daemon=True, - ssl_only=0, idle_timeout=1) - - sock = FakeSocket(b'some initial data') - - def fake_select(rlist, wlist, xlist, timeout=None): - return ([sock], [], []) - - patch('select.select').start().side_effect = fake_select - self.assertEqual(server.do_handshake(sock, '127.0.0.1'), sock) - self.assertTrue(FakeHandler.CALLED, True) - - def test_do_handshake_ssl(self): - # TODO(directxman12): implement this - pass - - def test_do_handshake_ssl_without_ssl_raises_error(self): - # TODO(directxman12): implement this - pass - - def test_do_handshake_ssl_without_cert_raises_error(self): - server = self._get_server(daemon=True, ssl_only=0, idle_timeout=1, - cert='afdsfasdafdsafdsafdsafdas') - - sock = FakeSocket(b"\x16some ssl data") - - def fake_select(rlist, wlist, xlist, timeout=None): - return ([sock], [], []) - - patch('select.select').start().side_effect = fake_select - self.assertRaises( - websockifyserver.WebSockifyServer.EClose, server.do_handshake, - sock, '127.0.0.1') - - def test_do_handshake_ssl_error_eof_raises_close_error(self): - server = self._get_server(daemon=True, ssl_only=0, idle_timeout=1) - - sock = FakeSocket(b"\x16some ssl data") - - def fake_select(rlist, wlist, xlist, timeout=None): - return ([sock], [], []) - - def fake_wrap_socket(*args, **kwargs): - raise ssl.SSLError(ssl.SSL_ERROR_EOF) - - class fake_create_default_context(): - def __init__(self, purpose): - self.verify_mode = None - self.options = 0 - def load_cert_chain(self, certfile, keyfile, password): - pass - def set_default_verify_paths(self): - pass - def load_verify_locations(self, cafile): - pass - def wrap_socket(self, *args, **kwargs): - raise ssl.SSLError(ssl.SSL_ERROR_EOF) - - patch('select.select').start().side_effect = fake_select - patch('ssl.create_default_context').start().side_effect = fake_create_default_context - self.assertRaises( - websockifyserver.WebSockifyServer.EClose, server.do_handshake, - sock, '127.0.0.1') - - def test_do_handshake_ssl_sets_ciphers(self): - test_ciphers = 'TEST-CIPHERS-1:TEST-CIPHER-2' - - class FakeHandler(object): - def __init__(self, *args, **kwargs): - pass - - server = self._get_server(handler_class=FakeHandler, daemon=True, - idle_timeout=1, ssl_ciphers=test_ciphers) - sock = FakeSocket(b"\x16some ssl data") - - def fake_select(rlist, wlist, xlist, timeout=None): - return ([sock], [], []) - - class fake_create_default_context(): - CIPHERS = '' - def __init__(self, purpose): - self.verify_mode = None - self.options = 0 - def load_cert_chain(self, certfile, keyfile, password): - pass - def set_default_verify_paths(self): - pass - def load_verify_locations(self, cafile): - pass - def wrap_socket(self, *args, **kwargs): - pass - def set_ciphers(self, ciphers_to_set): - fake_create_default_context.CIPHERS = ciphers_to_set - - patch('select.select').start().side_effect = fake_select - patch('ssl.create_default_context').start().side_effect = fake_create_default_context - server.do_handshake(sock, '127.0.0.1') - self.assertEqual(fake_create_default_context.CIPHERS, test_ciphers) - - def test_do_handshake_ssl_sets_opions(self): - test_options = 0xCAFEBEEF - - class FakeHandler(object): - def __init__(self, *args, **kwargs): - pass - - server = self._get_server(handler_class=FakeHandler, daemon=True, - idle_timeout=1, ssl_options=test_options) - sock = FakeSocket(b"\x16some ssl data") - - def fake_select(rlist, wlist, xlist, timeout=None): - return ([sock], [], []) - - class fake_create_default_context(object): - OPTIONS = 0 - def __init__(self, purpose): - self.verify_mode = None - self._options = 0 - def load_cert_chain(self, certfile, keyfile, password): - pass - def set_default_verify_paths(self): - pass - def load_verify_locations(self, cafile): - pass - def wrap_socket(self, *args, **kwargs): - pass - def get_options(self): - return self._options - def set_options(self, val): - fake_create_default_context.OPTIONS = val - options = property(get_options, set_options) - - patch('select.select').start().side_effect = fake_select - patch('ssl.create_default_context').start().side_effect = fake_create_default_context - server.do_handshake(sock, '127.0.0.1') - self.assertEqual(fake_create_default_context.OPTIONS, test_options) - - def test_fallback_sigchld_handler(self): - # TODO(directxman12): implement this - pass - - def test_start_server_error(self): - server = self._get_server(daemon=False, ssl_only=1, idle_timeout=1) - sock = server.socket('localhost') - - def fake_select(rlist, wlist, xlist, timeout=None): - raise Exception("fake error") - - patch('websockify.websockifyserver.WebSockifyServer.socket').start() - patch('websockify.websockifyserver.WebSockifyServer.daemonize').start() - patch('select.select').start().side_effect = fake_select - server.start_server() - - def test_start_server_keyboardinterrupt(self): - server = self._get_server(daemon=False, ssl_only=0, idle_timeout=1) - sock = server.socket('localhost') - - def fake_select(rlist, wlist, xlist, timeout=None): - raise KeyboardInterrupt - - patch('websockify.websockifyserver.WebSockifyServer.socket').start() - patch('websockify.websockifyserver.WebSockifyServer.daemonize').start() - patch('select.select').start().side_effect = fake_select - server.start_server() - - def test_start_server_systemexit(self): - server = self._get_server(daemon=False, ssl_only=0, idle_timeout=1) - sock = server.socket('localhost') - - def fake_select(rlist, wlist, xlist, timeout=None): - sys.exit() - - patch('websockify.websockifyserver.WebSockifyServer.socket').start() - patch('websockify.websockifyserver.WebSockifyServer.daemonize').start() - patch('select.select').start().side_effect = fake_select - server.start_server() - - def test_socket_set_keepalive_options(self): - keepcnt = 12 - keepidle = 34 - keepintvl = 56 - - server = self._get_server(daemon=False, ssl_only=0, idle_timeout=1) - sock = server.socket('localhost', - tcp_keepcnt=keepcnt, - tcp_keepidle=keepidle, - tcp_keepintvl=keepintvl) - - if hasattr(socket, 'TCP_KEEPCNT'): - self.assertEqual(sock.getsockopt(socket.SOL_TCP, - socket.TCP_KEEPCNT), keepcnt) - self.assertEqual(sock.getsockopt(socket.SOL_TCP, - socket.TCP_KEEPIDLE), keepidle) - self.assertEqual(sock.getsockopt(socket.SOL_TCP, - socket.TCP_KEEPINTVL), keepintvl) - - sock = server.socket('localhost', - tcp_keepalive=False, - tcp_keepcnt=keepcnt, - tcp_keepidle=keepidle, - tcp_keepintvl=keepintvl) - - if hasattr(socket, 'TCP_KEEPCNT'): - self.assertNotEqual(sock.getsockopt(socket.SOL_TCP, - socket.TCP_KEEPCNT), keepcnt) - self.assertNotEqual(sock.getsockopt(socket.SOL_TCP, - socket.TCP_KEEPIDLE), keepidle) - self.assertNotEqual(sock.getsockopt(socket.SOL_TCP, - socket.TCP_KEEPINTVL), keepintvl) diff --git a/base/app/novnc/utils/websockify/tox.ini b/base/app/novnc/utils/websockify/tox.ini deleted file mode 100644 index 526eff6..0000000 --- a/base/app/novnc/utils/websockify/tox.ini +++ /dev/null @@ -1,17 +0,0 @@ -# Tox (http://tox.testrun.org/) is a tool for running tests -# in multiple virtualenvs. This configuration file will run the -# test suite on all supported python versions. To use it, "pip install tox" -# and then run "tox" from this directory. - -[tox] -envlist = py34 - -[testenv] -commands = nosetests {posargs} -deps = -r{toxinidir}/test-requirements.txt - -# At some point we should enable this since tox expects it to exist but -# the code will need pep8ising first. -#[testenv:pep8] -#commands = flake8 -#dep = flake8 diff --git a/base/app/novnc/utils/websockify/websockify.py b/base/app/novnc/utils/websockify/websockify.py deleted file mode 120000 index e5224d5..0000000 --- a/base/app/novnc/utils/websockify/websockify.py +++ /dev/null @@ -1 +0,0 @@ -run \ No newline at end of file diff --git a/base/app/novnc/utils/websockify/websockify/__init__.py b/base/app/novnc/utils/websockify/websockify/__init__.py deleted file mode 100644 index 37a6f47..0000000 --- a/base/app/novnc/utils/websockify/websockify/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from websockify.websocket import * -from websockify.websocketproxy import * diff --git a/base/app/novnc/utils/websockify/websockify/__main__.py b/base/app/novnc/utils/websockify/websockify/__main__.py deleted file mode 100644 index 8378d46..0000000 --- a/base/app/novnc/utils/websockify/websockify/__main__.py +++ /dev/null @@ -1,4 +0,0 @@ -import websockify - -if __name__ == '__main__': - websockify.websocketproxy.websockify_init() diff --git a/base/app/novnc/utils/websockify/websockify/auth_plugins.py b/base/app/novnc/utils/websockify/websockify/auth_plugins.py deleted file mode 100644 index 36fac52..0000000 --- a/base/app/novnc/utils/websockify/websockify/auth_plugins.py +++ /dev/null @@ -1,102 +0,0 @@ -class BasePlugin(): - def __init__(self, src=None): - self.source = src - - def authenticate(self, headers, target_host, target_port): - pass - - -class AuthenticationError(Exception): - def __init__(self, log_msg=None, response_code=403, response_headers={}, response_msg=None): - self.code = response_code - self.headers = response_headers - self.msg = response_msg - - if log_msg is None: - log_msg = response_msg - - super().__init__('%s %s' % (self.code, log_msg)) - - -class InvalidOriginError(AuthenticationError): - def __init__(self, expected, actual): - self.expected_origin = expected - self.actual_origin = actual - - super().__init__( - response_msg='Invalid Origin', - log_msg="Invalid Origin Header: Expected one of " - "%s, got '%s'" % (expected, actual)) - - -class BasicHTTPAuth(): - """Verifies Basic Auth headers. Specify src as username:password""" - - def __init__(self, src=None): - self.src = src - - def authenticate(self, headers, target_host, target_port): - import base64 - auth_header = headers.get('Authorization') - if auth_header: - if not auth_header.startswith('Basic '): - self.auth_error() - - try: - user_pass_raw = base64.b64decode(auth_header[6:]) - except TypeError: - self.auth_error() - - try: - # http://stackoverflow.com/questions/7242316/what-encoding-should-i-use-for-http-basic-authentication - user_pass_as_text = user_pass_raw.decode('ISO-8859-1') - except UnicodeDecodeError: - self.auth_error() - - user_pass = user_pass_as_text.split(':', 1) - if len(user_pass) != 2: - self.auth_error() - - if not self.validate_creds(*user_pass): - self.demand_auth() - - else: - self.demand_auth() - - def validate_creds(self, username, password): - if '%s:%s' % (username, password) == self.src: - return True - else: - return False - - def auth_error(self): - raise AuthenticationError(response_code=403) - - def demand_auth(self): - raise AuthenticationError(response_code=401, - response_headers={'WWW-Authenticate': 'Basic realm="Websockify"'}) - -class ExpectOrigin(): - def __init__(self, src=None): - if src is None: - self.source = [] - else: - self.source = src.split() - - def authenticate(self, headers, target_host, target_port): - origin = headers.get('Origin', None) - if origin is None or origin not in self.source: - raise InvalidOriginError(expected=self.source, actual=origin) - -class ClientCertCNAuth(): - """Verifies client by SSL certificate. Specify src as whitespace separated list of common names.""" - - def __init__(self, src=None): - if src is None: - self.source = [] - else: - self.source = src.split() - - def authenticate(self, headers, target_host, target_port): - if headers.get('SSL_CLIENT_S_DN_CN', None) not in self.source: - raise AuthenticationError(response_code=403) diff --git a/base/app/novnc/utils/websockify/websockify/sysloghandler.py b/base/app/novnc/utils/websockify/websockify/sysloghandler.py deleted file mode 100644 index 37ee9dd..0000000 --- a/base/app/novnc/utils/websockify/websockify/sysloghandler.py +++ /dev/null @@ -1,118 +0,0 @@ -import logging.handlers as handlers, socket, os, time - - -class WebsockifySysLogHandler(handlers.SysLogHandler): - """ - A handler class that sends proper Syslog-formatted messages, - as defined by RFC 5424. - """ - - _legacy_head_fmt = '<{pri}>{ident}[{pid}]: ' - _rfc5424_head_fmt = '<{pri}>1 {timestamp} {hostname} {ident} {pid} - - ' - _head_fmt = _rfc5424_head_fmt - _legacy = False - _timestamp_fmt = '%Y-%m-%dT%H:%M:%SZ' - _max_hostname = 255 - _max_ident = 24 #safer for old daemons - _send_length = False - _tail = '\n' - - - ident = None - - - def __init__(self, address=('localhost', handlers.SYSLOG_UDP_PORT), - facility=handlers.SysLogHandler.LOG_USER, - socktype=None, ident=None, legacy=False): - """ - Initialize a handler. - - If address is specified as a string, a UNIX socket is used. To log to a - local syslogd, "WebsockifySysLogHandler(address="/dev/log")" can be - used. If facility is not specified, LOG_USER is used. If socktype is - specified as socket.SOCK_DGRAM or socket.SOCK_STREAM, that specific - socket type will be used. For Unix sockets, you can also specify a - socktype of None, in which case socket.SOCK_DGRAM will be used, falling - back to socket.SOCK_STREAM. If ident is specified, this string will be - used as the application name in all messages sent. Set legacy to True - to use the old version of the protocol. - """ - - self.ident = ident - - if legacy: - self._legacy = True - self._head_fmt = self._legacy_head_fmt - - super().__init__(address, facility, socktype) - - - def emit(self, record): - """ - Emit a record. - - The record is formatted, and then sent to the syslog server. If - exception information is present, it is NOT sent to the server. - """ - - try: - # Gather info. - text = self.format(record).replace(self._tail, ' ') - if not text: # nothing to log - return - - pri = self.encodePriority(self.facility, - self.mapPriority(record.levelname)) - - timestamp = time.strftime(self._timestamp_fmt, time.gmtime()); - - hostname = socket.gethostname()[:self._max_hostname] - - if self.ident: - ident = self.ident[:self._max_ident] - else: - ident = '' - - pid = os.getpid() # shouldn't need truncation - - # Format the header. - head = { - 'pri': pri, - 'timestamp': timestamp, - 'hostname': hostname, - 'ident': ident, - 'pid': pid, - } - msg = self._head_fmt.format(**head).encode('ascii', 'ignore') - - # Encode text as plain ASCII if possible, else use UTF-8 with BOM. - try: - msg += text.encode('ascii') - except UnicodeEncodeError: - msg += text.encode('utf-8-sig') - - # Add length or tail character, if necessary. - if self.socktype != socket.SOCK_DGRAM: - if self._send_length: - msg = ('%d ' % len(msg)).encode('ascii') + msg - else: - msg += self._tail.encode('ascii') - - # Send the message. - if self.unixsocket: - try: - self.socket.send(msg) - except socket.error: - self._connect_unixsocket(self.address) - self.socket.send(msg) - - else: - if self.socktype == socket.SOCK_DGRAM: - self.socket.sendto(msg, self.address) - else: - self.socket.sendall(msg) - - except (KeyboardInterrupt, SystemExit): - raise - except: - self.handleError(record) diff --git a/base/app/novnc/utils/websockify/websockify/token_plugins.py b/base/app/novnc/utils/websockify/websockify/token_plugins.py deleted file mode 100644 index 36a1dbc..0000000 --- a/base/app/novnc/utils/websockify/websockify/token_plugins.py +++ /dev/null @@ -1,333 +0,0 @@ -import logging -import os -import sys -import time -import re -import json - -logger = logging.getLogger(__name__) - - -class BasePlugin(): - def __init__(self, src): - self.source = src - - def lookup(self, token): - return None - - -class ReadOnlyTokenFile(BasePlugin): - # source is a token file with lines like - # token: host:port - # or a directory of such files - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self._targets = None - - def _load_targets(self): - if os.path.isdir(self.source): - cfg_files = [os.path.join(self.source, f) for - f in os.listdir(self.source)] - else: - cfg_files = [self.source] - - self._targets = {} - index = 1 - for f in cfg_files: - for line in [l.strip() for l in open(f).readlines()]: - if line and not line.startswith('#'): - try: - tok, target = re.split(':\s', line) - self._targets[tok] = target.strip().rsplit(':', 1) - except ValueError: - logger.error("Syntax error in %s on line %d" % (self.source, index)) - index += 1 - - def lookup(self, token): - if self._targets is None: - self._load_targets() - - if token in self._targets: - return self._targets[token] - else: - return None - - -# the above one is probably more efficient, but this one is -# more backwards compatible (although in most cases -# ReadOnlyTokenFile should suffice) -class TokenFile(ReadOnlyTokenFile): - # source is a token file with lines like - # token: host:port - # or a directory of such files - def lookup(self, token): - self._load_targets() - - return super().lookup(token) - -class TokenFileName(BasePlugin): - # source is a directory - # token is filename - # contents of file is host:port - def __init__(self, src): - super().__init__(src) - if not os.path.isdir(src): - raise Exception("TokenFileName plugin requires a directory") - - def lookup(self, token): - token = os.path.basename(token) - path = os.path.join(self.source, token) - if os.path.exists(path): - return open(path).read().strip().split(':') - else: - return None - - -class BaseTokenAPI(BasePlugin): - # source is a url with a '%s' in it where the token - # should go - - # we import things on demand so that other plugins - # in this file can be used w/o unnecessary dependencies - - def process_result(self, resp): - host, port = resp.text.split(':') - port = port.encode('ascii','ignore') - return [ host, port ] - - def lookup(self, token): - import requests - - resp = requests.get(self.source % token) - - if resp.ok: - return self.process_result(resp) - else: - return None - - -class JSONTokenApi(BaseTokenAPI): - # source is a url with a '%s' in it where the token - # should go - - def process_result(self, resp): - resp_json = resp.json() - return (resp_json['host'], resp_json['port']) - - -class JWTTokenApi(BasePlugin): - # source is a JWT-token, with hostname and port included - # Both JWS as JWE tokens are accepted. With regards to JWE tokens, the key is re-used for both validation and decryption. - - def lookup(self, token): - try: - from jwcrypto import jwt, jwk - import json - - key = jwk.JWK() - - try: - with open(self.source, 'rb') as key_file: - key_data = key_file.read() - except Exception as e: - logger.error("Error loading key file: %s" % str(e)) - return None - - try: - key.import_from_pem(key_data) - except: - try: - key.import_key(k=key_data.decode('utf-8'),kty='oct') - except: - logger.error('Failed to correctly parse key data!') - return None - - try: - token = jwt.JWT(key=key, jwt=token) - parsed_header = json.loads(token.header) - - if 'enc' in parsed_header: - # Token is encrypted, so we need to decrypt by passing the claims to a new instance - token = jwt.JWT(key=key, jwt=token.claims) - - parsed = json.loads(token.claims) - - if 'nbf' in parsed: - # Not Before is present, so we need to check it - if time.time() < parsed['nbf']: - logger.warning('Token can not be used yet!') - return None - - if 'exp' in parsed: - # Expiration time is present, so we need to check it - if time.time() > parsed['exp']: - logger.warning('Token has expired!') - return None - - return (parsed['host'], parsed['port']) - except Exception as e: - logger.error("Failed to parse token: %s" % str(e)) - return None - except ImportError: - logger.error("package jwcrypto not found, are you sure you've installed it correctly?") - return None - - -class TokenRedis(BasePlugin): - """Token plugin based on the Redis in-memory data store. - - The token source is in the format: - - host[:port[:db[:password]]] - - where port, db and password are optional. If port or db are left empty - they will take its default value, ie. 6379 and 0 respectively. - - If your redis server is using the default port (6379) then you can use: - - my-redis-host - - In case you need to authenticate with the redis server and you are using - the default database and port you can use: - - my-redis-host:::verysecretpass - - In the more general case you will use: - - my-redis-host:6380:1:verysecretpass - - The TokenRedis plugin expects the format of the target in one of these two - formats: - - - JSON - - {"host": "target-host:target-port"} - - - Plain text - - target-host:target-port - - Prepare data with: - - redis-cli set my-token '{"host": "127.0.0.1:5000"}' - - Verify with: - - redis-cli --raw get my-token - - Spawn a test "server" using netcat - - nc -l 5000 -v - - Note: This Token Plugin depends on the 'redis' module, so you have - to install it before using this plugin: - - pip install redis - """ - def __init__(self, src): - try: - import redis - except ImportError: - logger.error("Unable to load redis module") - sys.exit() - # Default values - self._port = 6379 - self._db = 0 - self._password = None - try: - fields = src.split(":") - if len(fields) == 1: - self._server = fields[0] - elif len(fields) == 2: - self._server, self._port = fields - if not self._port: - self._port = 6379 - elif len(fields) == 3: - self._server, self._port, self._db = fields - if not self._port: - self._port = 6379 - if not self._db: - self._db = 0 - elif len(fields) == 4: - self._server, self._port, self._db, self._password = fields - if not self._port: - self._port = 6379 - if not self._db: - self._db = 0 - if not self._password: - self._password = None - else: - raise ValueError - self._port = int(self._port) - self._db = int(self._db) - logger.info("TokenRedis backend initilized (%s:%s)" % - (self._server, self._port)) - except ValueError: - logger.error("The provided --token-source='%s' is not in the " - "expected format [:[:[:]]]" % - src) - sys.exit() - - def lookup(self, token): - try: - import redis - except ImportError: - logger.error("package redis not found, are you sure you've installed them correctly?") - sys.exit() - - logger.info("resolving token '%s'" % token) - client = redis.Redis(host=self._server, port=self._port, - db=self._db, password=self._password) - stuff = client.get(token) - if stuff is None: - return None - else: - responseStr = stuff.decode("utf-8").strip() - logger.debug("response from redis : %s" % responseStr) - if responseStr.startswith("{"): - try: - combo = json.loads(responseStr) - host, port = combo["host"].split(":") - except ValueError: - logger.error("Unable to decode JSON token: %s" % - responseStr) - return None - except KeyError: - logger.error("Unable to find 'host' key in JSON token: %s" % - responseStr) - return None - elif re.match(r'\S+:\S+', responseStr): - host, port = responseStr.split(":") - else: - logger.error("Unable to parse token: %s" % responseStr) - return None - logger.debug("host: %s, port: %s" % (host, port)) - return [host, port] - - -class UnixDomainSocketDirectory(BasePlugin): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self._dir_path = os.path.abspath(self.source) - - def lookup(self, token): - try: - import stat - - if not os.path.isdir(self._dir_path): - return None - - uds_path = os.path.abspath(os.path.join(self._dir_path, token)) - if not uds_path.startswith(self._dir_path): - return None - - if not os.path.exists(uds_path): - return None - - if not stat.S_ISSOCK(os.stat(uds_path).st_mode): - return None - - return [ 'unix_socket', uds_path ] - except Exception as e: - logger.error("Error finding unix domain socket: %s" % str(e)) - return None diff --git a/base/app/novnc/utils/websockify/websockify/websocket.py b/base/app/novnc/utils/websockify/websockify/websocket.py deleted file mode 100644 index af87d3e..0000000 --- a/base/app/novnc/utils/websockify/websockify/websocket.py +++ /dev/null @@ -1,874 +0,0 @@ -#!/usr/bin/env python - -''' -Python WebSocket library -Copyright 2011 Joel Martin -Copyright 2016 Pierre Ossman -Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3) - -Supports following protocol versions: - - http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-07 - - http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10 - - http://tools.ietf.org/html/rfc6455 -''' - -import sys -import array -import email -import errno -import random -import socket -import ssl -import struct -from base64 import b64encode -from hashlib import sha1 -from urllib.parse import urlparse - -try: - import numpy -except ImportError: - import warnings - warnings.warn("no 'numpy' module, HyBi protocol will be slower") - numpy = None - -class WebSocketWantReadError(ssl.SSLWantReadError): - pass -class WebSocketWantWriteError(ssl.SSLWantWriteError): - pass - -class WebSocket(object): - """WebSocket protocol socket like class. - - This provides access to the WebSocket protocol by behaving much - like a real socket would. It shares many similarities with - ssl.SSLSocket. - - The WebSocket protocols requires extra data to be sent and received - compared to the application level data. This means that a socket - that is ready to be read may not hold enough data to decode any - application data, and a socket that is ready to be written to may - not have enough space for an entire WebSocket frame. This is - handled by the exceptions WebSocketWantReadError and - WebSocketWantWriteError. When these are raised the caller must wait - for the socket to become ready again and call the relevant function - again. - - A connection is established by using either connect() or accept(), - depending on if a client or server session is desired. See the - respective functions for details. - - The following methods are passed on to the underlying socket: - - - fileno - - getpeername, getsockname - - getsockopt, setsockopt - - gettimeout, settimeout - - setblocking - """ - - GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" - - def __init__(self): - """Creates an unconnected WebSocket""" - - self._state = "new" - - self._partial_msg = b'' - - self._recv_buffer = b'' - self._recv_queue = [] - self._send_buffer = b'' - - self._previous_sendmsg = None - - self._sent_close = False - self._received_close = False - - self.close_code = None - self.close_reason = None - - self.socket = None - - def __getattr__(self, name): - # These methods are just redirected to the underlying socket - if name in ["fileno", - "getpeername", "getsockname", - "getsockopt", "setsockopt", - "gettimeout", "settimeout", - "setblocking"]: - assert self.socket is not None - return getattr(self.socket, name) - else: - raise AttributeError("%s instance has no attribute '%s'" % - (self.__class__.__name__, name)) - - def connect(self, uri, origin=None, protocols=[]): - """Establishes a new connection to a WebSocket server. - - This method connects to the host specified by uri and - negotiates a WebSocket connection. origin should be specified - in accordance with RFC 6454 if known. A list of valid - sub-protocols can be specified in the protocols argument. - - The data will be sent in the clear if the "ws" scheme is used, - and encrypted if the "wss" scheme is used. - - Both WebSocketWantReadError and WebSocketWantWriteError can be - raised whilst negotiating the connection. Repeated calls to - connect() must retain the same arguments. - """ - - self.client = True; - - uri = urlparse(uri) - - port = uri.port - if uri.scheme in ("ws", "http"): - if not port: - port = 80 - elif uri.scheme in ("wss", "https"): - if not port: - port = 443 - else: - raise Exception("Unknown scheme '%s'" % uri.scheme) - - # This is a state machine in order to handle - # WantRead/WantWrite events - - if self._state == "new": - self.socket = socket.create_connection((uri.hostname, port)) - - if uri.scheme in ("wss", "https"): - self.socket = ssl.wrap_socket(self.socket) - self._state = "ssl_handshake" - else: - self._state = "headers" - - if self._state == "ssl_handshake": - self.socket.do_handshake() - self._state = "headers" - - if self._state == "headers": - self._key = '' - for i in range(16): - self._key += chr(random.randrange(256)) - self._key = b64encode(self._key.encode("latin-1")).decode("ascii") - - path = uri.path - if not path: - path = "/" - - self.send_request("GET", path) - self.send_header("Host", uri.hostname) - self.send_header("Upgrade", "websocket") - self.send_header("Connection", "upgrade") - self.send_header("Sec-WebSocket-Key", self._key) - self.send_header("Sec-WebSocket-Version", 13) - - if origin is not None: - self.send_header("Origin", origin) - if len(protocols) > 0: - self.send_header("Sec-WebSocket-Protocol", ", ".join(protocols)) - - self.end_headers() - - self._state = "send_headers" - - if self._state == "send_headers": - self._flush() - self._state = "response" - - if self._state == "response": - if not self._recv(): - raise Exception("Socket closed unexpectedly") - - if self._recv_buffer.find(b'\r\n\r\n') == -1: - raise WebSocketWantReadError - - (request, self._recv_buffer) = self._recv_buffer.split(b'\r\n', 1) - request = request.decode("latin-1") - - words = request.split() - if (len(words) < 2) or (words[0] != "HTTP/1.1"): - raise Exception("Invalid response") - if words[1] != "101": - raise Exception("WebSocket request denied: %s" % " ".join(words[1:])) - - (headers, self._recv_buffer) = self._recv_buffer.split(b'\r\n\r\n', 1) - headers = headers.decode('latin-1') + '\r\n' - headers = email.message_from_string(headers) - - if headers.get("Upgrade", "").lower() != "websocket": - print(type(headers)) - raise Exception("Missing or incorrect upgrade header") - - accept = headers.get('Sec-WebSocket-Accept') - if accept is None: - raise Exception("Missing Sec-WebSocket-Accept header"); - - expected = sha1((self._key + self.GUID).encode("ascii")).digest() - expected = b64encode(expected).decode("ascii") - - del self._key - - if accept != expected: - raise Exception("Invalid Sec-WebSocket-Accept header"); - - self.protocol = headers.get('Sec-WebSocket-Protocol') - if len(protocols) == 0: - if self.protocol is not None: - raise Exception("Unexpected Sec-WebSocket-Protocol header") - else: - if self.protocol not in protocols: - raise Exception("Invalid protocol chosen by server") - - self._state = "done" - - return - - raise Exception("WebSocket is in an invalid state") - - def accept(self, socket, headers): - """Establishes a new WebSocket session with a client. - - This method negotiates a WebSocket connection with an incoming - client. The caller must provide the client socket and the - headers from the HTTP request. - - A server can identify that a client is requesting a WebSocket - connection by looking at the "Upgrade" header. It will include - the value "websocket" in such cases. - - WebSocketWantWriteError can be raised if the response cannot be - sent right away. accept() must be called again once more space - is available using the same arguments. - """ - - # This is a state machine in order to handle - # WantRead/WantWrite events - - if self._state == "new": - self.client = False - self.socket = socket - - if headers.get("upgrade", "").lower() != "websocket": - raise Exception("Missing or incorrect upgrade header") - - ver = headers.get('Sec-WebSocket-Version') - if ver is None: - raise Exception("Missing Sec-WebSocket-Version header"); - - # HyBi-07 report version 7 - # HyBi-08 - HyBi-12 report version 8 - # HyBi-13 reports version 13 - if ver in ['7', '8', '13']: - self.version = "hybi-%02d" % int(ver) - else: - raise Exception("Unsupported protocol version %s" % ver) - - key = headers.get('Sec-WebSocket-Key') - if key is None: - raise Exception("Missing Sec-WebSocket-Key header"); - - # Generate the hash value for the accept header - accept = sha1((key + self.GUID).encode("ascii")).digest() - accept = b64encode(accept).decode("ascii") - - self.protocol = '' - protocols = headers.get('Sec-WebSocket-Protocol', '').split(',') - if protocols: - self.protocol = self.select_subprotocol(protocols) - # We are required to choose one of the protocols - # presented by the client - if self.protocol not in protocols: - raise Exception('Invalid protocol selected') - - self.send_response(101, "Switching Protocols") - self.send_header("Upgrade", "websocket") - self.send_header("Connection", "Upgrade") - self.send_header("Sec-WebSocket-Accept", accept) - - if self.protocol: - self.send_header("Sec-WebSocket-Protocol", self.protocol) - - self.end_headers() - - self._state = "flush" - - if self._state == "flush": - self._flush() - self._state = "done" - - return - - raise Exception("WebSocket is in an invalid state") - - def select_subprotocol(self, protocols): - """Returns which sub-protocol should be used. - - This method does not select any sub-protocol by default and is - meant to be overridden by an implementation that wishes to make - use of sub-protocols. It will be called during handling of - accept(). - """ - return "" - - def handle_ping(self, data): - """Called when a WebSocket ping message is received. - - This will be called whilst processing recv()/recvmsg(). The - default implementation sends a pong reply back.""" - self.pong(data) - - def handle_pong(self, data): - """Called when a WebSocket pong message is received. - - This will be called whilst processing recv()/recvmsg(). The - default implementation does nothing.""" - pass - - def recv(self): - """Read data from the WebSocket. - - This will return any available data on the socket (which may - be the empty string if the peer sent an empty message or - messages). If the socket is closed then None will be - returned. The reason for the close is found in the - 'close_code' and 'close_reason' properties. - - Unlike recvmsg() this method may return data from more than one - WebSocket message. It is however not guaranteed to return all - buffered data. Callers should continue calling recv() whilst - pending() returns True. - - Both WebSocketWantReadError and WebSocketWantWriteError can be - raised when calling recv(). - """ - return self.recvmsg() - - def recvmsg(self): - """Read a single message from the WebSocket. - - This will return a single WebSocket message from the socket - (which will be the empty string if the peer sent an empty - message). If the socket is closed then None will be - returned. The reason for the close is found in the - 'close_code' and 'close_reason' properties. - - Unlike recv() this method will not return data from more than - one WebSocket message. Callers should continue calling - recvmsg() whilst pending() returns True. - - Both WebSocketWantReadError and WebSocketWantWriteError can be - raised when calling recvmsg(). - """ - # May have been called to flush out a close - if self._received_close: - self._flush() - return None - - # Anything already queued? - if self.pending(): - return self._recvmsg() - # Note: If self._recvmsg() raised WebSocketWantReadError, - # we cannot proceed to self._recv() here as we may - # have already called it once as part of the caller's - # "while websock.pending():" loop - - # Nope, let's try to read a bit - if not self._recv_frames(): - return None - - # Anything queued now? - return self._recvmsg() - - def pending(self): - """Check if any WebSocket data is pending. - - This method will return True as long as there are WebSocket - frames that have yet been processed. A single recv() from the - underlying socket may return multiple WebSocket frames and it - is therefore important that a caller continues calling recv() - or recvmsg() as long as pending() returns True. - - Note that this function merely tells if there are raw WebSocket - frames pending. Those frames may not contain any application - data. - """ - return len(self._recv_queue) > 0 - - def send(self, bytes): - """Write data to the WebSocket - - This will queue the given data and attempt to send it to the - peer. Unlike sendmsg() this method might coalesce the data with - data from other calls, or split it over multiple messages. - - WebSocketWantWriteError can be raised if there is insufficient - space in the underlying socket. send() must be called again - once more space is available using the same arguments. - """ - if len(bytes) == 0: - return 0 - - return self.sendmsg(bytes) - - def sendmsg(self, msg): - """Write a single message to the WebSocket - - This will queue the given message and attempt to send it to the - peer. Unlike send() this method will preserve the data as a - single WebSocket message. - - WebSocketWantWriteError can be raised if there is insufficient - space in the underlying socket. sendmsg() must be called again - once more space is available using the same arguments. - """ - if not isinstance(msg, bytes): - raise TypeError - - if self._sent_close: - return 0 - - if self._previous_sendmsg is not None: - if self._previous_sendmsg != msg: - raise ValueError - - self._flush() - self._previous_sendmsg = None - - return len(msg) - - try: - self._sendmsg(0x2, msg) - except WebSocketWantWriteError: - self._previous_sendmsg = msg - raise - - return len(msg) - - def send_response(self, code, message): - self._queue_str("HTTP/1.1 %d %s\r\n" % (code, message)) - - def send_header(self, keyword, value): - self._queue_str("%s: %s\r\n" % (keyword, value)) - - def end_headers(self): - self._queue_str("\r\n") - - def send_request(self, type, path): - self._queue_str("%s %s HTTP/1.1\r\n" % (type.upper(), path)) - - def ping(self, data=b''): - """Write a ping message to the WebSocket - - WebSocketWantWriteError can be raised if there is insufficient - space in the underlying socket. ping() must be called again once - more space is available using the same arguments. - """ - if not isinstance(data, bytes): - raise TypeError - - if self._previous_sendmsg is not None: - if self._previous_sendmsg != data: - raise ValueError - - self._flush() - self._previous_sendmsg = None - - return - - try: - self._sendmsg(0x9, data) - except WebSocketWantWriteError: - self._previous_sendmsg = data - raise - - def pong(self, data=b''): - """Write a pong message to the WebSocket - - WebSocketWantWriteError can be raised if there is insufficient - space in the underlying socket. pong() must be called again once - more space is available using the same arguments. - """ - if not isinstance(data, bytes): - raise TypeError - - if self._previous_sendmsg is not None: - if self._previous_sendmsg != data: - raise ValueError - - self._flush() - self._previous_sendmsg = None - - return - - try: - self._sendmsg(0xA, data) - except WebSocketWantWriteError: - self._previous_sendmsg = data - raise - - def shutdown(self, how, code=1000, reason=None): - """Gracefully terminate the WebSocket connection. - - This will start the process to terminate the WebSocket - connection. The caller must continue to calling recv() or - recvmsg() after this function in order to wait for the peer to - acknowledge the close. Calls to send() and sendmsg() will be - ignored. - - WebSocketWantWriteError can be raised if there is insufficient - space in the underlying socket for the close message. shutdown() - must be called again once more space is available using the same - arguments. - - The how argument is currently ignored. - """ - - # Already closing? - if self._sent_close: - self._flush() - return - - # Special code to indicate that we closed the connection - if not self._received_close: - self.close_code = 1000 - self.close_reason = "Locally initiated close" - - self._sent_close = True - - msg = b'' - if code is not None: - msg += struct.pack(">H", code) - if reason is not None: - msg += reason.encode("UTF-8") - - self._sendmsg(0x8, msg) - - def close(self, code=1000, reason=None): - """Terminate the WebSocket connection immediately. - - This will close the WebSocket connection directly after sending - a close message to the peer. - - WebSocketWantWriteError can be raised if there is insufficient - space in the underlying socket for the close message. close() - must be called again once more space is available using the same - arguments. - """ - self.shutdown(socket.SHUT_RDWR, code, reason) - self._close() - - def _recv(self): - # Fetches more data from the socket to the buffer - assert self.socket is not None - - while True: - try: - data = self.socket.recv(4096) - except OSError as exc: - if exc.errno == errno.EWOULDBLOCK: - raise WebSocketWantReadError - raise - - if len(data) == 0: - return False - - self._recv_buffer += data - - # Support for SSLSocket like objects - if hasattr(self.socket, "pending"): - if not self.socket.pending(): - break - else: - break - - return True - - def _recv_frames(self): - # Fetches more data and decodes the frames - if not self._recv(): - if self.close_code is None: - self.close_code = 1006 - self.close_reason = "Connection closed abnormally" - self._sent_close = self._received_close = True - self._close() - return False - - while True: - frame = self._decode_hybi(self._recv_buffer) - if frame is None: - break - self._recv_buffer = self._recv_buffer[frame['length']:] - self._recv_queue.append(frame) - - return True - - def _recvmsg(self): - # Process pending frames and returns any application data - while self._recv_queue: - frame = self._recv_queue.pop(0) - - if not self.client and not frame['masked']: - self.shutdown(socket.SHUT_RDWR, 1002, "Procotol error: Frame not masked") - continue - if self.client and frame['masked']: - self.shutdown(socket.SHUT_RDWR, 1002, "Procotol error: Frame masked") - continue - - if frame["opcode"] == 0x0: - if not self._partial_msg: - self.shutdown(socket.SHUT_RDWR, 1002, "Procotol error: Unexpected continuation frame") - continue - - self._partial_msg += frame["payload"] - - if frame["fin"]: - msg = self._partial_msg - self._partial_msg = b'' - return msg - elif frame["opcode"] == 0x1: - self.shutdown(socket.SHUT_RDWR, 1003, "Unsupported: Text frames are not supported") - elif frame["opcode"] == 0x2: - if self._partial_msg: - self.shutdown(socket.SHUT_RDWR, 1002, "Procotol error: Unexpected new frame") - continue - - if frame["fin"]: - return frame["payload"] - else: - self._partial_msg = frame["payload"] - elif frame["opcode"] == 0x8: - if self._received_close: - continue - - self._received_close = True - - if self._sent_close: - self._close() - return None - - if not frame["fin"]: - self.shutdown(socket.SHUT_RDWR, 1003, "Unsupported: Fragmented close") - continue - - code = None - reason = None - if len(frame["payload"]) >= 2: - code = struct.unpack(">H", frame["payload"][:2])[0] - if len(frame["payload"]) > 2: - reason = frame["payload"][2:] - try: - reason = reason.decode("UTF-8") - except UnicodeDecodeError: - self.shutdown(socket.SHUT_RDWR, 1002, "Procotol error: Invalid UTF-8 in close") - continue - - if code is None: - self.close_code = 1005 - self.close_reason = "No close status code specified by peer" - else: - self.close_code = code - if reason is not None: - self.close_reason = reason - - self.shutdown(None, code, reason) - return None - elif frame["opcode"] == 0x9: - if not frame["fin"]: - self.shutdown(socket.SHUT_RDWR, 1003, "Unsupported: Fragmented ping") - continue - - self.handle_ping(frame["payload"]) - elif frame["opcode"] == 0xA: - if not frame["fin"]: - self.shutdown(socket.SHUT_RDWR, 1003, "Unsupported: Fragmented pong") - continue - - self.handle_pong(frame["payload"]) - else: - self.shutdown(socket.SHUT_RDWR, 1003, "Unsupported: Unknown opcode 0x%02x" % frame["opcode"]) - - raise WebSocketWantReadError - - def _flush(self): - # Writes pending data to the socket - if not self._send_buffer: - return - - assert self.socket is not None - - try: - sent = self.socket.send(self._send_buffer) - except OSError as exc: - if exc.errno == errno.EWOULDBLOCK: - raise WebSocketWantWriteError - raise - - self._send_buffer = self._send_buffer[sent:] - - if self._send_buffer: - raise WebSocketWantWriteError - - # We had a pending close and we've flushed the buffer, - # time to end things - if self._received_close and self._sent_close: - self._close() - - def _send(self, data): - # Queues data and attempts to send it - self._send_buffer += data - self._flush() - - def _queue_str(self, string): - # Queue some data to be sent later. - # Only used by the connecting methods. - self._send_buffer += string.encode("latin-1") - - def _sendmsg(self, opcode, msg): - # Sends a standard data message - if self.client: - mask = b'' - for i in range(4): - mask += random.randrange(256).to_bytes() - frame = self._encode_hybi(opcode, msg, mask) - else: - frame = self._encode_hybi(opcode, msg) - - return self._send(frame) - - def _close(self): - # Close the underlying socket - self.socket.close() - self.socket = None - - def _mask(self, buf, mask): - # Mask a frame - return self._unmask(buf, mask) - - def _unmask(self, buf, mask): - # Unmask a frame - if numpy: - plen = len(buf) - pstart = 0 - pend = plen - b = c = b'' - if plen >= 4: - dtype=numpy.dtype('') - mask = numpy.frombuffer(mask, dtype, count=1) - data = numpy.frombuffer(buf, dtype, count=int(plen / 4)) - #b = numpy.bitwise_xor(data, mask).data - b = numpy.bitwise_xor(data, mask).tobytes() - - if plen % 4: - dtype=numpy.dtype('B') - if sys.byteorder == 'big': - dtype = dtype.newbyteorder('>') - mask = numpy.frombuffer(mask, dtype, count=(plen % 4)) - data = numpy.frombuffer(buf, dtype, - offset=plen - (plen % 4), count=(plen % 4)) - c = numpy.bitwise_xor(data, mask).tobytes() - return b + c - else: - # Slower fallback - data = array.array('B') - data.frombytes(buf) - for i in range(len(data)): - data[i] ^= mask[i % 4] - return data.tobytes() - - def _encode_hybi(self, opcode, buf, mask_key=None, fin=True): - """ Encode a HyBi style WebSocket frame. - Optional opcode: - 0x0 - continuation - 0x1 - text frame - 0x2 - binary frame - 0x8 - connection close - 0x9 - ping - 0xA - pong - """ - - b1 = opcode & 0x0f - if fin: - b1 |= 0x80 - - mask_bit = 0 - if mask_key is not None: - mask_bit = 0x80 - buf = self._mask(buf, mask_key) - - payload_len = len(buf) - if payload_len <= 125: - header = struct.pack('>BB', b1, payload_len | mask_bit) - elif payload_len > 125 and payload_len < 65536: - header = struct.pack('>BBH', b1, 126 | mask_bit, payload_len) - elif payload_len >= 65536: - header = struct.pack('>BBQ', b1, 127 | mask_bit, payload_len) - - if mask_key is not None: - return header + mask_key + buf - else: - return header + buf - - def _decode_hybi(self, buf): - """ Decode HyBi style WebSocket packets. - Returns: - {'fin' : boolean, - 'opcode' : number, - 'masked' : boolean, - 'length' : encoded_length, - 'payload' : decoded_buffer} - """ - - f = {'fin' : 0, - 'opcode' : 0, - 'masked' : False, - 'length' : 0, - 'payload' : None} - - blen = len(buf) - hlen = 2 - - if blen < hlen: - return None - - b1, b2 = struct.unpack(">BB", buf[:2]) - f['opcode'] = b1 & 0x0f - f['fin'] = not not (b1 & 0x80) - f['masked'] = not not (b2 & 0x80) - - if f['masked']: - hlen += 4 - if blen < hlen: - return None - - length = b2 & 0x7f - - if length == 126: - hlen += 2 - if blen < hlen: - return None - length, = struct.unpack('>H', buf[2:4]) - elif length == 127: - hlen += 8 - if blen < hlen: - return None - length, = struct.unpack('>Q', buf[2:10]) - - f['length'] = hlen + length - - if blen < f['length']: - return None - - if f['masked']: - # unmask payload - mask_key = buf[hlen-4:hlen] - f['payload'] = self._unmask(buf[hlen:(hlen+length)], mask_key) - else: - f['payload'] = buf[hlen:(hlen+length)] - - return f - diff --git a/base/app/novnc/utils/websockify/websockify/websocketproxy.py b/base/app/novnc/utils/websockify/websockify/websocketproxy.py deleted file mode 100644 index b6f103c..0000000 --- a/base/app/novnc/utils/websockify/websockify/websocketproxy.py +++ /dev/null @@ -1,800 +0,0 @@ -#!/usr/bin/env python - -''' -A WebSocket to TCP socket proxy with support for "wss://" encryption. -Copyright 2011 Joel Martin -Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3) - -You can make a cert/key with openssl using: -openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem -as taken from http://docs.python.org/dev/library/ssl.html#certificates - -''' - -import signal, socket, optparse, time, os, sys, subprocess, logging, errno, ssl, stat -from socketserver import ThreadingMixIn -from http.server import HTTPServer - -import select -from websockify import websockifyserver -from websockify import auth_plugins as auth -from urllib.parse import parse_qs, urlparse - -class ProxyRequestHandler(websockifyserver.WebSockifyRequestHandler): - - buffer_size = 65536 - - traffic_legend = """ -Traffic Legend: - } - Client receive - }. - Client receive partial - { - Target receive - - > - Target send - >. - Target send partial - < - Client send - <. - Client send partial -""" - - def send_auth_error(self, ex): - self.send_response(ex.code, ex.msg) - self.send_header('Content-Type', 'text/html') - for name, val in ex.headers.items(): - self.send_header(name, val) - - self.end_headers() - - def validate_connection(self): - if not self.server.token_plugin: - return - - host, port = self.get_target(self.server.token_plugin) - if host == 'unix_socket': - self.server.unix_target = port - - else: - self.server.target_host = host - self.server.target_port = port - - def auth_connection(self): - if not self.server.auth_plugin: - return - - try: - # get client certificate data - client_cert_data = self.request.getpeercert() - # extract subject information - client_cert_subject = client_cert_data['subject'] - # flatten data structure - client_cert_subject = dict([x[0] for x in client_cert_subject]) - # add common name to headers (apache +StdEnvVars style) - self.headers['SSL_CLIENT_S_DN_CN'] = client_cert_subject['commonName'] - except (TypeError, AttributeError, KeyError): - # not a SSL connection or client presented no certificate with valid data - pass - - try: - self.server.auth_plugin.authenticate( - headers=self.headers, target_host=self.server.target_host, - target_port=self.server.target_port) - except auth.AuthenticationError: - ex = sys.exc_info()[1] - self.send_auth_error(ex) - raise - - def new_websocket_client(self): - """ - Called after a new WebSocket connection has been established. - """ - # Checking for a token is done in validate_connection() - - # Connect to the target - if self.server.wrap_cmd: - msg = "connecting to command: '%s' (port %s)" % (" ".join(self.server.wrap_cmd), self.server.target_port) - elif self.server.unix_target: - msg = "connecting to unix socket: %s" % self.server.unix_target - else: - msg = "connecting to: %s:%s" % ( - self.server.target_host, self.server.target_port) - - if self.server.ssl_target: - msg += " (using SSL)" - self.log_message(msg) - - try: - tsock = websockifyserver.WebSockifyServer.socket(self.server.target_host, - self.server.target_port, - connect=True, - use_ssl=self.server.ssl_target, - unix_socket=self.server.unix_target) - except Exception as e: - self.log_message("Failed to connect to %s:%s: %s", - self.server.target_host, self.server.target_port, e) - raise self.CClose(1011, "Failed to connect to downstream server") - - # Option unavailable when listening to unix socket - if not self.server.unix_listen: - self.request.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1) - if not self.server.wrap_cmd and not self.server.unix_target: - tsock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1) - - self.print_traffic(self.traffic_legend) - - # Start proxying - try: - self.do_proxy(tsock) - finally: - if tsock: - tsock.shutdown(socket.SHUT_RDWR) - tsock.close() - if self.verbose: - self.log_message("%s:%s: Closed target", - self.server.target_host, self.server.target_port) - - def get_target(self, target_plugin): - """ - Gets a token from either the path or the host, - depending on --host-token, and looks up a target - for that token using the token plugin. Used by - validate_connection() to set target_host and target_port. - """ - # The files in targets contain the lines - # in the form of token: host:port - - if self.host_token: - # Use hostname as token - token = self.headers.get('Host') - - # Remove port from hostname, as it'll always be the one where - # websockify listens (unless something between the client and - # websockify is redirecting traffic, but that's beside the point) - if token: - token = token.partition(':')[0] - - else: - # Extract the token parameter from url - args = parse_qs(urlparse(self.path)[4]) # 4 is the query from url - - if 'token' in args and len(args['token']): - token = args['token'][0].rstrip('\n') - else: - token = None - - if token is None: - raise self.server.EClose("Token not present") - - result_pair = target_plugin.lookup(token) - - if result_pair is not None: - return result_pair - else: - raise self.server.EClose("Token '%s' not found" % token) - - def do_proxy(self, target): - """ - Proxy client WebSocket to normal target socket. - """ - cqueue = [] - c_pend = 0 - tqueue = [] - rlist = [self.request, target] - - if self.server.heartbeat: - now = time.time() - self.heartbeat = now + self.server.heartbeat - else: - self.heartbeat = None - - while True: - wlist = [] - - if self.heartbeat is not None: - now = time.time() - if now > self.heartbeat: - self.heartbeat = now + self.server.heartbeat - self.send_ping() - - if tqueue: wlist.append(target) - if cqueue or c_pend: wlist.append(self.request) - try: - ins, outs, excepts = select.select(rlist, wlist, [], 1) - except (select.error, OSError): - exc = sys.exc_info()[1] - if hasattr(exc, 'errno'): - err = exc.errno - else: - err = exc[0] - - if err != errno.EINTR: - raise - else: - continue - - if excepts: raise Exception("Socket exception") - - if self.request in outs: - # Send queued target data to the client - c_pend = self.send_frames(cqueue) - - cqueue = [] - - if self.request in ins: - # Receive client data, decode it, and queue for target - bufs, closed = self.recv_frames() - tqueue.extend(bufs) - - if closed: - - while (len(tqueue) != 0): - # Send queued client data to the target - dat = tqueue.pop(0) - sent = target.send(dat) - if sent == len(dat): - self.print_traffic(">") - else: - # requeue the remaining data - tqueue.insert(0, dat[sent:]) - self.print_traffic(".>") - - # TODO: What about blocking on client socket? - if self.verbose: - self.log_message("%s:%s: Client closed connection", - self.server.target_host, self.server.target_port) - raise self.CClose(closed['code'], closed['reason']) - - - if target in outs: - # Send queued client data to the target - dat = tqueue.pop(0) - sent = target.send(dat) - if sent == len(dat): - self.print_traffic(">") - else: - # requeue the remaining data - tqueue.insert(0, dat[sent:]) - self.print_traffic(".>") - - - if target in ins: - # Receive target data, encode it and queue for client - buf = target.recv(self.buffer_size) - if len(buf) == 0: - - # Target socket closed, flushing queues and closing client-side websocket - # Send queued target data to the client - if len(cqueue) != 0: - c_pend = True - while(c_pend): - c_pend = self.send_frames(cqueue) - - cqueue = [] - - if self.verbose: - self.log_message("%s:%s: Target closed connection", - self.server.target_host, self.server.target_port) - raise self.CClose(1000, "Target closed") - - cqueue.append(buf) - self.print_traffic("{") - -class WebSocketProxy(websockifyserver.WebSockifyServer): - """ - Proxy traffic to and from a WebSockets client to a normal TCP - socket server target. - """ - - buffer_size = 65536 - - def __init__(self, RequestHandlerClass=ProxyRequestHandler, *args, **kwargs): - # Save off proxy specific options - self.target_host = kwargs.pop('target_host', None) - self.target_port = kwargs.pop('target_port', None) - self.wrap_cmd = kwargs.pop('wrap_cmd', None) - self.wrap_mode = kwargs.pop('wrap_mode', None) - self.unix_target = kwargs.pop('unix_target', None) - self.ssl_target = kwargs.pop('ssl_target', None) - self.heartbeat = kwargs.pop('heartbeat', None) - - self.token_plugin = kwargs.pop('token_plugin', None) - self.host_token = kwargs.pop('host_token', None) - self.auth_plugin = kwargs.pop('auth_plugin', None) - - # Last 3 timestamps command was run - self.wrap_times = [0, 0, 0] - - if self.wrap_cmd: - wsdir = os.path.dirname(sys.argv[0]) - rebinder_path = [os.path.join(wsdir, "..", "lib"), - os.path.join(wsdir, "..", "lib", "websockify"), - os.path.join(wsdir, ".."), - wsdir] - self.rebinder = None - - for rdir in rebinder_path: - rpath = os.path.join(rdir, "rebind.so") - if os.path.exists(rpath): - self.rebinder = rpath - break - - if not self.rebinder: - raise Exception("rebind.so not found, perhaps you need to run make") - self.rebinder = os.path.abspath(self.rebinder) - - self.target_host = "127.0.0.1" # Loopback - # Find a free high port - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.bind(('', 0)) - self.target_port = sock.getsockname()[1] - sock.close() - - # Insert rebinder at the head of the (possibly empty) LD_PRELOAD pathlist - ld_preloads = filter(None, [ self.rebinder, os.environ.get("LD_PRELOAD", None) ]) - - os.environ.update({ - "LD_PRELOAD": os.pathsep.join(ld_preloads), - "REBIND_OLD_PORT": str(kwargs['listen_port']), - "REBIND_NEW_PORT": str(self.target_port)}) - - super().__init__(RequestHandlerClass, *args, **kwargs) - - def run_wrap_cmd(self): - self.msg("Starting '%s'", " ".join(self.wrap_cmd)) - self.wrap_times.append(time.time()) - self.wrap_times.pop(0) - self.cmd = subprocess.Popen( - self.wrap_cmd, env=os.environ, preexec_fn=_subprocess_setup) - self.spawn_message = True - - def started(self): - """ - Called after Websockets server startup (i.e. after daemonize) - """ - # Need to call wrapped command after daemonization so we can - # know when the wrapped command exits - if self.wrap_cmd: - dst_string = "'%s' (port %s)" % (" ".join(self.wrap_cmd), self.target_port) - elif self.unix_target: - dst_string = self.unix_target - else: - dst_string = "%s:%s" % (self.target_host, self.target_port) - - if self.listen_fd != None: - src_string = "inetd" - else: - src_string = "%s:%s" % (self.listen_host, self.listen_port) - - if self.token_plugin: - msg = " - proxying from %s to targets generated by %s" % ( - src_string, type(self.token_plugin).__name__) - else: - msg = " - proxying from %s to %s" % ( - src_string, dst_string) - - if self.ssl_target: - msg += " (using SSL)" - - self.msg("%s", msg) - - if self.wrap_cmd: - self.run_wrap_cmd() - - def poll(self): - # If we are wrapping a command, check it's status - - if self.wrap_cmd and self.cmd: - ret = self.cmd.poll() - if ret != None: - self.vmsg("Wrapped command exited (or daemon). Returned %s" % ret) - self.cmd = None - - if self.wrap_cmd and self.cmd == None: - # Response to wrapped command being gone - if self.wrap_mode == "ignore": - pass - elif self.wrap_mode == "exit": - sys.exit(ret) - elif self.wrap_mode == "respawn": - now = time.time() - avg = sum(self.wrap_times)/len(self.wrap_times) - if (now - avg) < 10: - # 3 times in the last 10 seconds - if self.spawn_message: - self.warn("Command respawning too fast") - self.spawn_message = False - else: - self.run_wrap_cmd() - - -def _subprocess_setup(): - # Python installs a SIGPIPE handler by default. This is usually not what - # non-Python successfulbprocesses expect. - signal.signal(signal.SIGPIPE, signal.SIG_DFL) - - -SSL_OPTIONS = { - 'default': ssl.OP_ALL, - 'tlsv1_1': ssl.PROTOCOL_SSLv23 | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | - ssl.OP_NO_TLSv1, - 'tlsv1_2': ssl.PROTOCOL_SSLv23 | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | - ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1, - 'tlsv1_3': ssl.PROTOCOL_SSLv23 | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | - ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 | ssl.OP_NO_TLSv1_2, -} - -def select_ssl_version(version): - """Returns SSL options for the most secure TSL version available on this - Python version""" - if version in SSL_OPTIONS: - return SSL_OPTIONS[version] - else: - # It so happens that version names sorted lexicographically form a list - # from the least to the most secure - keys = list(SSL_OPTIONS.keys()) - keys.sort() - fallback = keys[-1] - logger = logging.getLogger(WebSocketProxy.log_prefix) - logger.warn("TLS version %s unsupported. Falling back to %s", - version, fallback) - - return SSL_OPTIONS[fallback] - -def websockify_init(): - # Setup basic logging to stderr. - stderr_handler = logging.StreamHandler() - stderr_handler.setLevel(logging.DEBUG) - log_formatter = logging.Formatter("%(message)s") - stderr_handler.setFormatter(log_formatter) - root = logging.getLogger() - root.addHandler(stderr_handler) - root.setLevel(logging.INFO) - - # Setup optparse. - usage = "\n %prog [options]" - usage += " [source_addr:]source_port target_addr:target_port" - usage += "\n %prog [options]" - usage += " --token-plugin=CLASS [source_addr:]source_port" - usage += "\n %prog [options]" - usage += " --unix-target=FILE [source_addr:]source_port" - usage += "\n %prog [options]" - usage += " [source_addr:]source_port -- WRAP_COMMAND_LINE" - parser = optparse.OptionParser(usage=usage) - parser.add_option("--verbose", "-v", action="store_true", - help="verbose messages") - parser.add_option("--traffic", action="store_true", - help="per frame traffic") - parser.add_option("--record", - help="record sessions to FILE.[session_number]", metavar="FILE") - parser.add_option("--daemon", "-D", - dest="daemon", action="store_true", - help="become a daemon (background process)") - parser.add_option("--run-once", action="store_true", - help="handle a single WebSocket connection and exit") - parser.add_option("--timeout", type=int, default=0, - help="after TIMEOUT seconds exit when not connected") - parser.add_option("--idle-timeout", type=int, default=0, - help="server exits after TIMEOUT seconds if there are no " - "active connections") - parser.add_option("--cert", default="self.pem", - help="SSL certificate file") - parser.add_option("--key", default=None, - help="SSL key file (if separate from cert)") - parser.add_option("--key-password", default=None, - help="SSL key password") - parser.add_option("--ssl-only", action="store_true", - help="disallow non-encrypted client connections") - parser.add_option("--ssl-target", action="store_true", - help="connect to SSL target as SSL client") - parser.add_option("--verify-client", action="store_true", - help="require encrypted client to present a valid certificate " - "(needs Python 2.7.9 or newer or Python 3.4 or newer)") - parser.add_option("--cafile", metavar="FILE", - help="file of concatenated certificates of authorities trusted " - "for validating clients (only effective with --verify-client). " - "If omitted, system default list of CAs is used.") - parser.add_option("--ssl-version", type="choice", default="default", - choices=["default", "tlsv1_1", "tlsv1_2", "tlsv1_3"], action="store", - help="minimum TLS version to use (default, tlsv1_1, tlsv1_2, tlsv1_3)") - parser.add_option("--ssl-ciphers", action="store", - help="list of ciphers allowed for connection. For a list of " - "supported ciphers run `openssl ciphers`") - parser.add_option("--unix-listen", - help="listen to unix socket", metavar="FILE", default=None) - parser.add_option("--unix-listen-mode", default=None, - help="specify mode for unix socket (defaults to 0600)") - parser.add_option("--unix-target", - help="connect to unix socket target", metavar="FILE") - parser.add_option("--inetd", - help="inetd mode, receive listening socket from stdin", action="store_true") - parser.add_option("--web", default=None, metavar="DIR", - help="run webserver on same port. Serve files from DIR.") - parser.add_option("--web-auth", action="store_true", - help="require authentication to access webserver.") - parser.add_option("--wrap-mode", default="exit", metavar="MODE", - choices=["exit", "ignore", "respawn"], - help="action to take when the wrapped program exits " - "or daemonizes: exit (default), ignore, respawn") - parser.add_option("--prefer-ipv6", "-6", - action="store_true", dest="source_is_ipv6", - help="prefer IPv6 when resolving source_addr") - parser.add_option("--libserver", action="store_true", - help="use Python library SocketServer engine") - parser.add_option("--target-config", metavar="FILE", - dest="target_cfg", - help="Configuration file containing valid targets " - "in the form 'token: host:port' or, alternatively, a " - "directory containing configuration files of this form " - "(DEPRECATED: use `--token-plugin TokenFile --token-source " - " path/to/token/file` instead)") - parser.add_option("--token-plugin", default=None, metavar="CLASS", - help="use a Python class, usually one from websockify.token_plugins, " - "such as TokenFile, to process tokens into host:port pairs") - parser.add_option("--token-source", default=None, metavar="ARG", - help="an argument to be passed to the token plugin " - "on instantiation") - parser.add_option("--host-token", action="store_true", - help="use the host HTTP header as token instead of the " - "token URL query parameter") - parser.add_option("--auth-plugin", default=None, metavar="CLASS", - help="use a Python class, usually one from websockify.auth_plugins, " - "such as BasicHTTPAuth, to determine if a connection is allowed") - parser.add_option("--auth-source", default=None, metavar="ARG", - help="an argument to be passed to the auth plugin " - "on instantiation") - parser.add_option("--heartbeat", type=int, default=0, metavar="INTERVAL", - help="send a ping to the client every INTERVAL seconds") - parser.add_option("--log-file", metavar="FILE", - dest="log_file", - help="File where logs will be saved") - parser.add_option("--syslog", default=None, metavar="SERVER", - help="Log to syslog server. SERVER can be local socket, " - "such as /dev/log, or a UDP host:port pair.") - parser.add_option("--legacy-syslog", action="store_true", - help="Use the old syslog protocol instead of RFC 5424. " - "Use this if the messages produced by websockify seem abnormal.") - parser.add_option("--file-only", action="store_true", - help="use this to disable directory listings in web server.") - - (opts, args) = parser.parse_args() - - - # Validate options. - - if opts.token_source and not opts.token_plugin: - parser.error("You must use --token-plugin to use --token-source") - - if opts.host_token and not opts.token_plugin: - parser.error("You must use --token-plugin to use --host-token") - - if opts.auth_source and not opts.auth_plugin: - parser.error("You must use --auth-plugin to use --auth-source") - - if opts.web_auth and not opts.auth_plugin: - parser.error("You must use --auth-plugin to use --web-auth") - - if opts.web_auth and not opts.web: - parser.error("You must use --web to use --web-auth") - - if opts.legacy_syslog and not opts.syslog: - parser.error("You must use --syslog to use --legacy-syslog") - - - opts.ssl_options = select_ssl_version(opts.ssl_version) - del opts.ssl_version - - - if opts.log_file: - # Setup logging to user-specified file. - opts.log_file = os.path.abspath(opts.log_file) - log_file_handler = logging.FileHandler(opts.log_file) - log_file_handler.setLevel(logging.DEBUG) - log_file_handler.setFormatter(log_formatter) - root = logging.getLogger() - root.addHandler(log_file_handler) - - del opts.log_file - - if opts.syslog: - # Determine how to connect to syslog... - if opts.syslog.count(':'): - # User supplied a host:port pair. - syslog_host, syslog_port = opts.syslog.rsplit(':', 1) - try: - syslog_port = int(syslog_port) - except ValueError: - parser.error("Error parsing syslog port") - syslog_dest = (syslog_host, syslog_port) - else: - # User supplied a local socket file. - syslog_dest = os.path.abspath(opts.syslog) - - from websockify.sysloghandler import WebsockifySysLogHandler - - # Determine syslog facility. - if opts.daemon: - syslog_facility = WebsockifySysLogHandler.LOG_DAEMON - else: - syslog_facility = WebsockifySysLogHandler.LOG_USER - - # Start logging to syslog. - syslog_handler = WebsockifySysLogHandler(address=syslog_dest, - facility=syslog_facility, - ident='websockify', - legacy=opts.legacy_syslog) - syslog_handler.setLevel(logging.DEBUG) - syslog_handler.setFormatter(log_formatter) - root = logging.getLogger() - root.addHandler(syslog_handler) - - del opts.syslog - del opts.legacy_syslog - - if opts.verbose: - root = logging.getLogger() - root.setLevel(logging.DEBUG) - - - # Transform to absolute path as daemon may chdir - if opts.target_cfg: - opts.target_cfg = os.path.abspath(opts.target_cfg) - - if opts.target_cfg: - opts.token_plugin = 'TokenFile' - opts.token_source = opts.target_cfg - - del opts.target_cfg - - if sys.argv.count('--'): - opts.wrap_cmd = args[1:] - else: - opts.wrap_cmd = None - - if not websockifyserver.ssl and opts.ssl_target: - parser.error("SSL target requested and Python SSL module not loaded."); - - if opts.ssl_only and not os.path.exists(opts.cert): - parser.error("SSL only and %s not found" % opts.cert) - - if opts.inetd: - opts.listen_fd = sys.stdin.fileno() - elif opts.unix_listen: - if opts.unix_listen_mode: - try: - # Parse octal notation (like 750) - opts.unix_listen_mode = int(opts.unix_listen_mode, 8) - except ValueError: - parser.error("Error parsing listen unix socket mode") - else: - # Default to 0600 (Owner Read/Write) - opts.unix_listen_mode = stat.S_IREAD | stat.S_IWRITE - else: - if len(args) < 1: - parser.error("Too few arguments") - arg = args.pop(0) - # Parse host:port and convert ports to numbers - if arg.count(':') > 0: - opts.listen_host, opts.listen_port = arg.rsplit(':', 1) - opts.listen_host = opts.listen_host.strip('[]') - else: - opts.listen_host, opts.listen_port = '', arg - - try: - opts.listen_port = int(opts.listen_port) - except ValueError: - parser.error("Error parsing listen port") - - del opts.inetd - - if opts.wrap_cmd or opts.unix_target or opts.token_plugin: - opts.target_host = None - opts.target_port = None - else: - if len(args) < 1: - parser.error("Too few arguments") - arg = args.pop(0) - if arg.count(':') > 0: - opts.target_host, opts.target_port = arg.rsplit(':', 1) - opts.target_host = opts.target_host.strip('[]') - else: - parser.error("Error parsing target") - - try: - opts.target_port = int(opts.target_port) - except ValueError: - parser.error("Error parsing target port") - - if len(args) > 0 and opts.wrap_cmd == None: - parser.error("Too many arguments") - - if opts.token_plugin is not None: - if '.' not in opts.token_plugin: - opts.token_plugin = ( - 'websockify.token_plugins.%s' % opts.token_plugin) - - token_plugin_module, token_plugin_cls = opts.token_plugin.rsplit('.', 1) - - __import__(token_plugin_module) - token_plugin_cls = getattr(sys.modules[token_plugin_module], token_plugin_cls) - - opts.token_plugin = token_plugin_cls(opts.token_source) - - del opts.token_source - - if opts.auth_plugin is not None: - if '.' not in opts.auth_plugin: - opts.auth_plugin = 'websockify.auth_plugins.%s' % opts.auth_plugin - - auth_plugin_module, auth_plugin_cls = opts.auth_plugin.rsplit('.', 1) - - __import__(auth_plugin_module) - auth_plugin_cls = getattr(sys.modules[auth_plugin_module], auth_plugin_cls) - - opts.auth_plugin = auth_plugin_cls(opts.auth_source) - - del opts.auth_source - - # Create and start the WebSockets proxy - libserver = opts.libserver - del opts.libserver - if libserver: - # Use standard Python SocketServer framework - server = LibProxyServer(**opts.__dict__) - server.serve_forever() - else: - # Use internal service framework - server = WebSocketProxy(**opts.__dict__) - server.start_server() - - -class LibProxyServer(ThreadingMixIn, HTTPServer): - """ - Just like WebSocketProxy, but uses standard Python SocketServer - framework. - """ - - def __init__(self, RequestHandlerClass=ProxyRequestHandler, **kwargs): - # Save off proxy specific options - self.target_host = kwargs.pop('target_host', None) - self.target_port = kwargs.pop('target_port', None) - self.wrap_cmd = kwargs.pop('wrap_cmd', None) - self.wrap_mode = kwargs.pop('wrap_mode', None) - self.unix_target = kwargs.pop('unix_target', None) - self.ssl_target = kwargs.pop('ssl_target', None) - self.token_plugin = kwargs.pop('token_plugin', None) - self.auth_plugin = kwargs.pop('auth_plugin', None) - self.heartbeat = kwargs.pop('heartbeat', None) - - self.token_plugin = None - self.auth_plugin = None - self.daemon = False - - # Server configuration - listen_host = kwargs.pop('listen_host', '') - listen_port = kwargs.pop('listen_port', None) - web = kwargs.pop('web', '') - - # Configuration affecting base request handler - self.only_upgrade = not web - self.verbose = kwargs.pop('verbose', False) - record = kwargs.pop('record', '') - if record: - self.record = os.path.abspath(record) - self.run_once = kwargs.pop('run_once', False) - self.handler_id = 0 - - for arg in kwargs.keys(): - print("warning: option %s ignored when using --libserver" % arg) - - if web: - os.chdir(web) - - super().__init__((listen_host, listen_port), RequestHandlerClass) - - - def process_request(self, request, client_address): - """Override process_request to implement a counter""" - self.handler_id += 1 - super().process_request(request, client_address) - - -if __name__ == '__main__': - websockify_init() diff --git a/base/app/novnc/utils/websockify/websockify/websocketserver.py b/base/app/novnc/utils/websockify/websockify/websocketserver.py deleted file mode 100644 index 4e62f2e..0000000 --- a/base/app/novnc/utils/websockify/websockify/websocketserver.py +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/env python - -''' -Python WebSocket server base -Copyright 2011 Joel Martin -Copyright 2016-2018 Pierre Ossman -Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3) -''' - -import sys -from http.server import BaseHTTPRequestHandler, HTTPServer - -from websockify.websocket import WebSocket, WebSocketWantReadError, WebSocketWantWriteError - -class HttpWebSocket(WebSocket): - """Class to glue websocket and http request functionality together""" - def __init__(self, request_handler): - super().__init__() - - self.request_handler = request_handler - - def send_response(self, code, message=None): - self.request_handler.send_response(code, message) - - def send_header(self, keyword, value): - self.request_handler.send_header(keyword, value) - - def end_headers(self): - self.request_handler.end_headers() - - -class WebSocketRequestHandlerMixIn: - """WebSocket request handler mix-in class - - This class modifies and existing request handler to handle - WebSocket requests. The request handler will continue to function - as before, except that WebSocket requests are intercepted and the - methods handle_upgrade() and handle_websocket() are called. The - standard do_GET() will be called for normal requests. - - The class instance SocketClass can be overridden with the class to - use for the WebSocket connection. - """ - - SocketClass = HttpWebSocket - - def handle_one_request(self): - """Extended request handler - - This is where WebSocketRequestHandler redirects requests to the - new methods. Any sub-classes must call this method in order for - the calls to function. - """ - self._real_do_GET = self.do_GET - self.do_GET = self._websocket_do_GET - try: - super().handle_one_request() - finally: - self.do_GET = self._real_do_GET - - def _websocket_do_GET(self): - # Checks if it is a websocket request and redirects - self.do_GET = self._real_do_GET - - if (self.headers.get('upgrade') and - self.headers.get('upgrade').lower() == 'websocket'): - self.handle_upgrade() - else: - self.do_GET() - - def handle_upgrade(self): - """Initial handler for a WebSocket request - - This method is called when a WebSocket is requested. By default - it will create a WebSocket object and perform the negotiation. - The WebSocket object will then replace the request object and - handle_websocket() will be called. - """ - websocket = self.SocketClass(self) - try: - websocket.accept(self.request, self.headers) - except Exception: - exc = sys.exc_info()[1] - self.send_error(400, str(exc)) - return - - self.request = websocket - - # Other requests cannot follow Websocket data - self.close_connection = True - - self.handle_websocket() - - def handle_websocket(self): - """Handle a WebSocket connection. - - This is called when the WebSocket is ready to be used. A - sub-class should perform the necessary communication here and - return once done. - """ - pass - -# Convenient ready made classes - -class WebSocketRequestHandler(WebSocketRequestHandlerMixIn, - BaseHTTPRequestHandler): - pass - -class WebSocketServer(HTTPServer): - pass diff --git a/base/app/novnc/utils/websockify/websockify/websockifyserver.py b/base/app/novnc/utils/websockify/websockify/websockifyserver.py deleted file mode 100644 index 74f9f53..0000000 --- a/base/app/novnc/utils/websockify/websockify/websockifyserver.py +++ /dev/null @@ -1,862 +0,0 @@ -#!/usr/bin/env python - -''' -Python WebSocket server base with support for "wss://" encryption. -Copyright 2011 Joel Martin -Copyright 2016 Pierre Ossman -Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3) - -You can make a cert/key with openssl using: -openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem -as taken from http://docs.python.org/dev/library/ssl.html#certificates - -''' - -import os, sys, time, errno, signal, socket, select, logging -import multiprocessing -from http.server import SimpleHTTPRequestHandler - -# Degraded functionality if these imports are missing -for mod, msg in [('ssl', 'TLS/SSL/wss is disabled'), - ('resource', 'daemonizing is disabled')]: - try: - globals()[mod] = __import__(mod) - except ImportError: - globals()[mod] = None - print("WARNING: no '%s' module, %s" % (mod, msg)) - -if sys.platform == 'win32': - # make sockets pickle-able/inheritable - import multiprocessing.reduction - -from websockify.websocket import WebSocketWantReadError, WebSocketWantWriteError -from websockify.websocketserver import WebSocketRequestHandlerMixIn - -class CompatibleWebSocket(WebSocketRequestHandlerMixIn.SocketClass): - def select_subprotocol(self, protocols): - # Handle old websockify clients that still specify a sub-protocol - if 'binary' in protocols: - return 'binary' - else: - return '' - -# HTTP handler with WebSocket upgrade support -class WebSockifyRequestHandler(WebSocketRequestHandlerMixIn, SimpleHTTPRequestHandler): - """ - WebSocket Request Handler Class, derived from SimpleHTTPRequestHandler. - Must be sub-classed with new_websocket_client method definition. - The request handler can be configured by setting optional - attributes on the server object: - - * only_upgrade: If true, SimpleHTTPRequestHandler will not be enabled, - only websocket is allowed. - * verbose: If true, verbose logging is activated. - * daemon: Running as daemon, do not write to console etc - * record: Record raw frame data as JavaScript array into specified filename - * run_once: Handle a single request - * handler_id: A sequence number for this connection, appended to record filename - """ - server_version = "WebSockify" - - protocol_version = "HTTP/1.1" - - SocketClass = CompatibleWebSocket - - # An exception while the WebSocket client was connected - class CClose(Exception): - pass - - def __init__(self, req, addr, server): - # Retrieve a few configuration variables from the server - self.only_upgrade = getattr(server, "only_upgrade", False) - self.verbose = getattr(server, "verbose", False) - self.daemon = getattr(server, "daemon", False) - self.record = getattr(server, "record", False) - self.run_once = getattr(server, "run_once", False) - self.rec = None - self.handler_id = getattr(server, "handler_id", False) - self.file_only = getattr(server, "file_only", False) - self.traffic = getattr(server, "traffic", False) - self.web_auth = getattr(server, "web_auth", False) - self.host_token = getattr(server, "host_token", False) - - self.logger = getattr(server, "logger", None) - if self.logger is None: - self.logger = WebSockifyServer.get_logger() - - super().__init__(req, addr, server) - - def log_message(self, format, *args): - self.logger.info("%s - - [%s] %s" % (self.client_address[0], self.log_date_time_string(), format % args)) - - # - # WebSocketRequestHandler logging/output functions - # - - def print_traffic(self, token="."): - """ Show traffic flow mode. """ - if self.traffic: - sys.stdout.write(token) - sys.stdout.flush() - - def msg(self, msg, *args, **kwargs): - """ Output message with handler_id prefix. """ - prefix = "% 3d: " % self.handler_id - self.logger.log(logging.INFO, "%s%s" % (prefix, msg), *args, **kwargs) - - def vmsg(self, msg, *args, **kwargs): - """ Same as msg() but as debug. """ - prefix = "% 3d: " % self.handler_id - self.logger.log(logging.DEBUG, "%s%s" % (prefix, msg), *args, **kwargs) - - def warn(self, msg, *args, **kwargs): - """ Same as msg() but as warning. """ - prefix = "% 3d: " % self.handler_id - self.logger.log(logging.WARN, "%s%s" % (prefix, msg), *args, **kwargs) - - # - # Main WebSocketRequestHandler methods - # - def send_frames(self, bufs=None): - """ Encode and send WebSocket frames. Any frames already - queued will be sent first. If buf is not set then only queued - frames will be sent. Returns True if any frames could not be - fully sent, in which case the caller should call again when - the socket is ready. """ - - tdelta = int(time.time()*1000) - self.start_time - - if bufs: - for buf in bufs: - if self.rec: - # Python 3 compatible conversion - bufstr = buf.decode('latin1').encode('unicode_escape').decode('ascii').replace("'", "\\'") - self.rec.write("'{{{0}{{{1}',\n".format(tdelta, bufstr)) - self.send_parts.append(buf) - - while self.send_parts: - # Send pending frames - try: - self.request.sendmsg(self.send_parts[0]) - except WebSocketWantWriteError: - self.print_traffic("<.") - return True - self.send_parts.pop(0) - self.print_traffic("<") - - return False - - def recv_frames(self): - """ Receive and decode WebSocket frames. - - Returns: - (bufs_list, closed_string) - """ - - closed = False - bufs = [] - tdelta = int(time.time()*1000) - self.start_time - - while True: - try: - buf = self.request.recvmsg() - except WebSocketWantReadError: - self.print_traffic("}.") - break - - if buf is None: - closed = {'code': self.request.close_code, - 'reason': self.request.close_reason} - return bufs, closed - - self.print_traffic("}") - - if self.rec: - # Python 3 compatible conversion - bufstr = buf.decode('latin1').encode('unicode_escape').decode('ascii').replace("'", "\\'") - self.rec.write("'}}{0}}}{1}',\n".format(tdelta, bufstr)) - - bufs.append(buf) - - if not self.request.pending(): - break - - return bufs, closed - - def send_close(self, code=1000, reason=''): - """ Send a WebSocket orderly close frame. """ - self.request.shutdown(socket.SHUT_RDWR, code, reason) - - def send_pong(self, data=''.encode('ascii')): - """ Send a WebSocket pong frame. """ - self.request.pong(data) - - def send_ping(self, data=''.encode('ascii')): - """ Send a WebSocket ping frame. """ - self.request.ping(data) - - def handle_upgrade(self): - # ensure connection is authorized, and determine the target - self.validate_connection() - self.auth_connection() - - super().handle_upgrade() - - def handle_websocket(self): - # Indicate to server that a Websocket upgrade was done - self.server.ws_connection = True - # Initialize per client settings - self.send_parts = [] - self.recv_part = None - self.start_time = int(time.time()*1000) - - # client_address is empty with, say, UNIX domain sockets - client_addr = "" - is_ssl = False - try: - client_addr = self.client_address[0] - is_ssl = self.client_address[2] - except IndexError: - pass - - if is_ssl: - self.stype = "SSL/TLS (wss://)" - else: - self.stype = "Plain non-SSL (ws://)" - - self.log_message("%s: %s WebSocket connection", client_addr, - self.stype) - if self.path != '/': - self.log_message("%s: Path: '%s'", client_addr, self.path) - - if self.record: - # Record raw frame data as JavaScript array - fname = "%s.%s" % (self.record, - self.handler_id) - self.log_message("opening record file: %s", fname) - self.rec = open(fname, 'w+') - self.rec.write("var VNC_frame_data = [\n") - - try: - self.new_websocket_client() - except self.CClose: - # Close the client - _, exc, _ = sys.exc_info() - self.send_close(exc.args[0], exc.args[1]) - - def do_GET(self): - if self.web_auth: - # ensure connection is authorized, this seems to apply to list_directory() as well - self.auth_connection() - - if self.only_upgrade: - self.send_error(405) - else: - super().do_GET() - - def list_directory(self, path): - if self.file_only: - self.send_error(404) - else: - return super().list_directory(path) - - def new_websocket_client(self): - """ Do something with a WebSockets client connection. """ - raise Exception("WebSocketRequestHandler.new_websocket_client() must be overloaded") - - def validate_connection(self): - """ Ensure that the connection has a valid token, and set the target. """ - pass - - def auth_connection(self): - """ Ensure that the connection is authorized. """ - pass - - def do_HEAD(self): - if self.web_auth: - self.auth_connection() - - if self.only_upgrade: - self.send_error(405) - else: - super().do_HEAD() - - def finish(self): - if self.rec: - self.rec.write("'EOF'];\n") - self.rec.close() - super().finish() - - def handle(self): - # When using run_once, we have a single process, so - # we cannot loop in BaseHTTPRequestHandler.handle; we - # must return and handle new connections - if self.run_once: - self.handle_one_request() - else: - super().handle() - - def log_request(self, code='-', size='-'): - if self.verbose: - super().log_request(code, size) - - -class WebSockifyServer(): - """ - WebSockets server class. - As an alternative, the standard library SocketServer can be used - """ - - policy_response = """\n""" - log_prefix = "websocket" - - # An exception before the WebSocket connection was established - class EClose(Exception): - pass - - class Terminate(Exception): - pass - - def __init__(self, RequestHandlerClass, listen_fd=None, - listen_host='', listen_port=None, source_is_ipv6=False, - verbose=False, cert='', key='', key_password=None, ssl_only=None, - verify_client=False, cafile=None, - daemon=False, record='', web='', web_auth=False, - file_only=False, - run_once=False, timeout=0, idle_timeout=0, traffic=False, - tcp_keepalive=True, tcp_keepcnt=None, tcp_keepidle=None, - tcp_keepintvl=None, ssl_ciphers=None, ssl_options=0, - unix_listen=None, unix_listen_mode=None): - - # settings - self.RequestHandlerClass = RequestHandlerClass - self.verbose = verbose - self.listen_fd = listen_fd - self.unix_listen = unix_listen - self.unix_listen_mode = unix_listen_mode - self.listen_host = listen_host - self.listen_port = listen_port - self.prefer_ipv6 = source_is_ipv6 - self.ssl_only = ssl_only - self.ssl_ciphers = ssl_ciphers - self.ssl_options = ssl_options - self.verify_client = verify_client - self.daemon = daemon - self.run_once = run_once - self.timeout = timeout - self.idle_timeout = idle_timeout - self.traffic = traffic - self.file_only = file_only - self.web_auth = web_auth - - self.launch_time = time.time() - self.ws_connection = False - self.handler_id = 1 - self.terminating = False - - self.logger = self.get_logger() - self.tcp_keepalive = tcp_keepalive - self.tcp_keepcnt = tcp_keepcnt - self.tcp_keepidle = tcp_keepidle - self.tcp_keepintvl = tcp_keepintvl - - # keyfile path must be None if not specified - self.key = None - self.key_password = key_password - - # Make paths settings absolute - self.cert = os.path.abspath(cert) - self.web = self.record = self.cafile = '' - if key: - self.key = os.path.abspath(key) - if web: - self.web = os.path.abspath(web) - if record: - self.record = os.path.abspath(record) - if cafile: - self.cafile = os.path.abspath(cafile) - - if self.web: - os.chdir(self.web) - self.only_upgrade = not self.web - - # Sanity checks - if not ssl and self.ssl_only: - raise Exception("No 'ssl' module and SSL-only specified") - if self.daemon and not resource: - raise Exception("Module 'resource' required to daemonize") - - # Show configuration - self.msg("WebSocket server settings:") - if self.listen_fd != None: - self.msg(" - Listen for inetd connections") - elif self.unix_listen != None: - self.msg(" - Listen on unix socket %s", self.unix_listen) - else: - self.msg(" - Listen on %s:%s", - self.listen_host, self.listen_port) - if self.web: - if self.file_only: - self.msg(" - Web server (no directory listings). Web root: %s", self.web) - else: - self.msg(" - Web server. Web root: %s", self.web) - if ssl: - if os.path.exists(self.cert): - self.msg(" - SSL/TLS support") - if self.ssl_only: - self.msg(" - Deny non-SSL/TLS connections") - else: - self.msg(" - No SSL/TLS support (no cert file)") - else: - self.msg(" - No SSL/TLS support (no 'ssl' module)") - if self.daemon: - self.msg(" - Backgrounding (daemon)") - if self.record: - self.msg(" - Recording to '%s.*'", self.record) - - # - # WebSockifyServer static methods - # - - @staticmethod - def get_logger(): - return logging.getLogger("%s.%s" % ( - WebSockifyServer.log_prefix, - WebSockifyServer.__class__.__name__)) - - @staticmethod - def socket(host, port=None, connect=False, prefer_ipv6=False, - unix_socket=None, unix_socket_mode=None, unix_socket_listen=False, - use_ssl=False, tcp_keepalive=True, tcp_keepcnt=None, - tcp_keepidle=None, tcp_keepintvl=None): - """ Resolve a host (and optional port) to an IPv4 or IPv6 - address. Create a socket. Bind to it if listen is set, - otherwise connect to it. Return the socket. - """ - flags = 0 - if host == '': - host = None - if connect and not (port or unix_socket): - raise Exception("Connect mode requires a port") - if use_ssl and not ssl: - raise Exception("SSL socket requested but Python SSL module not loaded."); - if not connect and use_ssl: - raise Exception("SSL only supported in connect mode (for now)") - if not connect: - flags = flags | socket.AI_PASSIVE - - if not unix_socket: - addrs = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM, - socket.IPPROTO_TCP, flags) - if not addrs: - raise Exception("Could not resolve host '%s'" % host) - addrs.sort(key=lambda x: x[0]) - if prefer_ipv6: - addrs.reverse() - sock = socket.socket(addrs[0][0], addrs[0][1]) - - if tcp_keepalive: - sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) - if tcp_keepcnt: - sock.setsockopt(socket.SOL_TCP, socket.TCP_KEEPCNT, - tcp_keepcnt) - if tcp_keepidle: - sock.setsockopt(socket.SOL_TCP, socket.TCP_KEEPIDLE, - tcp_keepidle) - if tcp_keepintvl: - sock.setsockopt(socket.SOL_TCP, socket.TCP_KEEPINTVL, - tcp_keepintvl) - - if connect: - sock.connect(addrs[0][4]) - if use_ssl: - sock = ssl.wrap_socket(sock) - else: - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - sock.bind(addrs[0][4]) - sock.listen(100) - else: - if unix_socket_listen: - # Make sure the socket does not already exist - try: - os.unlink(unix_socket) - except FileNotFoundError: - pass - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - oldmask = os.umask(0o777 ^ unix_socket_mode) - try: - sock.bind(unix_socket) - finally: - os.umask(oldmask) - sock.listen(100) - else: - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - sock.connect(unix_socket) - - return sock - - @staticmethod - def daemonize(keepfd=None, chdir='/'): - - if keepfd is None: - keepfd = [] - - os.umask(0) - if chdir: - os.chdir(chdir) - else: - os.chdir('/') - os.setgid(os.getgid()) # relinquish elevations - os.setuid(os.getuid()) # relinquish elevations - - # Double fork to daemonize - if os.fork() > 0: os._exit(0) # Parent exits - os.setsid() # Obtain new process group - if os.fork() > 0: os._exit(0) # Parent exits - - # Signal handling - signal.signal(signal.SIGTERM, signal.SIG_IGN) - signal.signal(signal.SIGINT, signal.SIG_IGN) - - # Close open files - maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1] - if maxfd == resource.RLIM_INFINITY: maxfd = 256 - for fd in reversed(range(maxfd)): - try: - if fd not in keepfd: - os.close(fd) - except OSError: - _, exc, _ = sys.exc_info() - if exc.errno != errno.EBADF: raise - - # Redirect I/O to /dev/null - os.dup2(os.open(os.devnull, os.O_RDWR), sys.stdin.fileno()) - os.dup2(os.open(os.devnull, os.O_RDWR), sys.stdout.fileno()) - os.dup2(os.open(os.devnull, os.O_RDWR), sys.stderr.fileno()) - - def do_handshake(self, sock, address): - """ - do_handshake does the following: - - Peek at the first few bytes from the socket. - - If the connection is an HTTPS/SSL/TLS connection then SSL - wrap the socket. - - Read from the (possibly wrapped) socket. - - If we have received a HTTP GET request and the webserver - functionality is enabled, answer it, close the socket and - return. - - Assume we have a WebSockets connection, parse the client - handshake data. - - Send a WebSockets handshake server response. - - Return the socket for this WebSocket client. - """ - ready = select.select([sock], [], [], 3)[0] - - if not ready: - raise self.EClose("") - # Peek, but do not read the data so that we have a opportunity - # to SSL wrap the socket first - handshake = sock.recv(1024, socket.MSG_PEEK) - #self.msg("Handshake [%s]" % handshake) - - if not handshake: - raise self.EClose("") - - elif handshake[0] in (22, 128): - # SSL wrap the connection - if not ssl: - raise self.EClose("SSL connection but no 'ssl' module") - if not os.path.exists(self.cert): - raise self.EClose("SSL connection but '%s' not found" - % self.cert) - retsock = None - try: - # create new-style SSL wrapping for extended features - context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) - if self.ssl_ciphers is not None: - context.set_ciphers(self.ssl_ciphers) - context.options = self.ssl_options - context.load_cert_chain(certfile=self.cert, keyfile=self.key, password=self.key_password) - if self.verify_client: - context.verify_mode = ssl.CERT_REQUIRED - if self.cafile: - context.load_verify_locations(cafile=self.cafile) - else: - context.set_default_verify_paths() - retsock = context.wrap_socket( - sock, - server_side=True) - except ssl.SSLError: - _, x, _ = sys.exc_info() - if x.args[0] == ssl.SSL_ERROR_EOF: - if len(x.args) > 1: - raise self.EClose(x.args[1]) - else: - raise self.EClose("Got SSL_ERROR_EOF") - else: - raise - - elif self.ssl_only: - raise self.EClose("non-SSL connection received but disallowed") - - else: - retsock = sock - - # If the address is like (host, port), we are extending it - # with a flag indicating SSL. Not many other options - # available... - if len(address) == 2: - address = (address[0], address[1], (retsock != sock)) - - self.RequestHandlerClass(retsock, address, self) - - # Return the WebSockets socket which may be SSL wrapped - return retsock - - # - # WebSockifyServer logging/output functions - # - - def msg(self, *args, **kwargs): - """ Output message as info """ - self.logger.log(logging.INFO, *args, **kwargs) - - def vmsg(self, *args, **kwargs): - """ Same as msg() but as debug. """ - self.logger.log(logging.DEBUG, *args, **kwargs) - - def warn(self, *args, **kwargs): - """ Same as msg() but as warning. """ - self.logger.log(logging.WARN, *args, **kwargs) - - - # - # Events that can/should be overridden in sub-classes - # - def started(self): - """ Called after WebSockets startup """ - self.vmsg("WebSockets server started") - - def poll(self): - """ Run periodically while waiting for connections. """ - #self.vmsg("Running poll()") - pass - - def terminate(self): - if not self.terminating: - self.terminating = True - raise self.Terminate() - - def multiprocessing_SIGCHLD(self, sig, stack): - # TODO: figure out a way to actually log this information without - # calling `log` in the signal handlers - multiprocessing.active_children() - - def fallback_SIGCHLD(self, sig, stack): - # Reap zombies when using os.fork() (python 2.4) - # TODO: figure out a way to actually log this information without - # calling `log` in the signal handlers - try: - result = os.waitpid(-1, os.WNOHANG) - while result[0]: - self.vmsg("Reaped child process %s" % result[0]) - result = os.waitpid(-1, os.WNOHANG) - except (OSError): - pass - - def do_SIGINT(self, sig, stack): - # TODO: figure out a way to actually log this information without - # calling `log` in the signal handlers - self.terminate() - - def do_SIGTERM(self, sig, stack): - # TODO: figure out a way to actually log this information without - # calling `log` in the signal handlers - self.terminate() - - def top_new_client(self, startsock, address): - """ Do something with a WebSockets client connection. """ - # handler process - client = None - try: - try: - client = self.do_handshake(startsock, address) - except self.EClose: - _, exc, _ = sys.exc_info() - # Connection was not a WebSockets connection - if exc.args[0]: - self.msg("%s: %s" % (address[0], exc.args[0])) - except WebSockifyServer.Terminate: - raise - except Exception: - _, exc, _ = sys.exc_info() - self.msg("handler exception: %s" % str(exc)) - self.vmsg("exception", exc_info=True) - finally: - - if client and client != startsock: - # Close the SSL wrapped socket - # Original socket closed by caller - client.close() - - def get_log_fd(self): - """ - Get file descriptors for the loggers. - They should not be closed when the process is forked. - """ - descriptors = [] - for handler in self.logger.parent.handlers: - if isinstance(handler, logging.FileHandler): - descriptors.append(handler.stream.fileno()) - - return descriptors - - def start_server(self): - """ - Daemonize if requested. Listen for for connections. Run - do_handshake() method for each connection. If the connection - is a WebSockets client then call new_websocket_client() method (which must - be overridden) for each new client connection. - """ - - if self.listen_fd != None: - lsock = socket.fromfd(self.listen_fd, socket.AF_INET, socket.SOCK_STREAM) - elif self.unix_listen != None: - lsock = self.socket(host=None, - unix_socket=self.unix_listen, - unix_socket_mode=self.unix_listen_mode, - unix_socket_listen=True) - else: - lsock = self.socket(self.listen_host, self.listen_port, False, - self.prefer_ipv6, - tcp_keepalive=self.tcp_keepalive, - tcp_keepcnt=self.tcp_keepcnt, - tcp_keepidle=self.tcp_keepidle, - tcp_keepintvl=self.tcp_keepintvl) - - if self.daemon: - keepfd = self.get_log_fd() - keepfd.append(lsock.fileno()) - self.daemonize(keepfd=keepfd, chdir=self.web) - - self.started() # Some things need to happen after daemonizing - - # Allow override of signals - original_signals = { - signal.SIGINT: signal.getsignal(signal.SIGINT), - signal.SIGTERM: signal.getsignal(signal.SIGTERM), - } - if getattr(signal, 'SIGCHLD', None) is not None: - original_signals[signal.SIGCHLD] = signal.getsignal(signal.SIGCHLD) - signal.signal(signal.SIGINT, self.do_SIGINT) - signal.signal(signal.SIGTERM, self.do_SIGTERM) - # make sure that _cleanup is called when children die - # by calling active_children on SIGCHLD - if getattr(signal, 'SIGCHLD', None) is not None: - signal.signal(signal.SIGCHLD, self.multiprocessing_SIGCHLD) - - last_active_time = self.launch_time - try: - while True: - try: - try: - startsock = None - pid = err = 0 - child_count = 0 - - # Collect zombie child processes - child_count = len(multiprocessing.active_children()) - - time_elapsed = time.time() - self.launch_time - if self.timeout and time_elapsed > self.timeout: - self.msg('listener exit due to --timeout %s' - % self.timeout) - break - - if self.idle_timeout: - idle_time = 0 - if child_count == 0: - idle_time = time.time() - last_active_time - else: - idle_time = 0 - last_active_time = time.time() - - if idle_time > self.idle_timeout and child_count == 0: - self.msg('listener exit due to --idle-timeout %s' - % self.idle_timeout) - break - - try: - self.poll() - - ready = select.select([lsock], [], [], 1)[0] - if lsock in ready: - startsock, address = lsock.accept() - # Unix Socket will not report address (empty string), but address[0] is logged a bunch - if self.unix_listen != None: - address = [ self.unix_listen ] - else: - continue - except self.Terminate: - raise - except Exception: - _, exc, _ = sys.exc_info() - if hasattr(exc, 'errno'): - err = exc.errno - elif hasattr(exc, 'args'): - err = exc.args[0] - else: - err = exc[0] - if err == errno.EINTR: - self.vmsg("Ignoring interrupted syscall") - continue - else: - raise - - if self.run_once: - # Run in same process if run_once - self.top_new_client(startsock, address) - if self.ws_connection : - self.msg('%s: exiting due to --run-once' - % address[0]) - break - else: - self.vmsg('%s: new handler Process' % address[0]) - p = multiprocessing.Process( - target=self.top_new_client, - args=(startsock, address)) - p.start() - # child will not return - - # parent process - self.handler_id += 1 - - except (self.Terminate, SystemExit, KeyboardInterrupt): - self.msg("In exit") - # terminate all child processes - if not self.run_once: - children = multiprocessing.active_children() - - for child in children: - self.msg("Terminating child %s" % child.pid) - child.terminate() - - break - except Exception: - exc = sys.exc_info()[1] - self.msg("handler exception: %s", str(exc)) - self.vmsg("exception", exc_info=True) - - finally: - if startsock: - startsock.close() - finally: - # Close listen port - self.vmsg("Closing socket listening at %s:%s", - self.listen_host, self.listen_port) - lsock.close() - - # Restore signals - for sig, func in original_signals.items(): - signal.signal(sig, func) - - diff --git a/base/app/novnc/vendor/pako/LICENSE b/base/app/novnc/vendor/pako/LICENSE deleted file mode 100644 index d082ae3..0000000 --- a/base/app/novnc/vendor/pako/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -(The MIT License) - -Copyright (C) 2014-2016 by Vitaly Puzrin - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/base/app/novnc/vendor/pako/README.md b/base/app/novnc/vendor/pako/README.md deleted file mode 100644 index 755df64..0000000 --- a/base/app/novnc/vendor/pako/README.md +++ /dev/null @@ -1,6 +0,0 @@ -This is an ES6-modules-compatible version of -https://github.com/nodeca/pako, based on pako version 1.0.3. - -It's more-or-less a direct translation of the original, with unused parts -removed, and the dynamic support for non-typed arrays removed (since ES6 -modules don't work well with dynamic exports). diff --git a/base/app/novnc/vendor/pako/lib/utils/common.js b/base/app/novnc/vendor/pako/lib/utils/common.js deleted file mode 100644 index 576fd59..0000000 --- a/base/app/novnc/vendor/pako/lib/utils/common.js +++ /dev/null @@ -1,45 +0,0 @@ -// reduce buffer size, avoiding mem copy -export function shrinkBuf (buf, size) { - if (buf.length === size) { return buf; } - if (buf.subarray) { return buf.subarray(0, size); } - buf.length = size; - return buf; -}; - - -export function arraySet (dest, src, src_offs, len, dest_offs) { - if (src.subarray && dest.subarray) { - dest.set(src.subarray(src_offs, src_offs + len), dest_offs); - return; - } - // Fallback to ordinary array - for (var i = 0; i < len; i++) { - dest[dest_offs + i] = src[src_offs + i]; - } -} - -// Join array of chunks to single array. -export function flattenChunks (chunks) { - var i, l, len, pos, chunk, result; - - // calculate data length - len = 0; - for (i = 0, l = chunks.length; i < l; i++) { - len += chunks[i].length; - } - - // join chunks - result = new Uint8Array(len); - pos = 0; - for (i = 0, l = chunks.length; i < l; i++) { - chunk = chunks[i]; - result.set(chunk, pos); - pos += chunk.length; - } - - return result; -} - -export var Buf8 = Uint8Array; -export var Buf16 = Uint16Array; -export var Buf32 = Int32Array; diff --git a/base/app/novnc/vendor/pako/lib/zlib/adler32.js b/base/app/novnc/vendor/pako/lib/zlib/adler32.js deleted file mode 100644 index 058a534..0000000 --- a/base/app/novnc/vendor/pako/lib/zlib/adler32.js +++ /dev/null @@ -1,27 +0,0 @@ -// Note: adler32 takes 12% for level 0 and 2% for level 6. -// It doesn't worth to make additional optimizationa as in original. -// Small size is preferable. - -export default function adler32(adler, buf, len, pos) { - var s1 = (adler & 0xffff) |0, - s2 = ((adler >>> 16) & 0xffff) |0, - n = 0; - - while (len !== 0) { - // Set limit ~ twice less than 5552, to keep - // s2 in 31-bits, because we force signed ints. - // in other case %= will fail. - n = len > 2000 ? 2000 : len; - len -= n; - - do { - s1 = (s1 + buf[pos++]) |0; - s2 = (s2 + s1) |0; - } while (--n); - - s1 %= 65521; - s2 %= 65521; - } - - return (s1 | (s2 << 16)) |0; -} diff --git a/base/app/novnc/vendor/pako/lib/zlib/constants.js b/base/app/novnc/vendor/pako/lib/zlib/constants.js deleted file mode 100644 index 7d80502..0000000 --- a/base/app/novnc/vendor/pako/lib/zlib/constants.js +++ /dev/null @@ -1,47 +0,0 @@ -export default { - - /* Allowed flush values; see deflate() and inflate() below for details */ - Z_NO_FLUSH: 0, - Z_PARTIAL_FLUSH: 1, - Z_SYNC_FLUSH: 2, - Z_FULL_FLUSH: 3, - Z_FINISH: 4, - Z_BLOCK: 5, - Z_TREES: 6, - - /* Return codes for the compression/decompression functions. Negative values - * are errors, positive values are used for special but normal events. - */ - Z_OK: 0, - Z_STREAM_END: 1, - Z_NEED_DICT: 2, - Z_ERRNO: -1, - Z_STREAM_ERROR: -2, - Z_DATA_ERROR: -3, - //Z_MEM_ERROR: -4, - Z_BUF_ERROR: -5, - //Z_VERSION_ERROR: -6, - - /* compression levels */ - Z_NO_COMPRESSION: 0, - Z_BEST_SPEED: 1, - Z_BEST_COMPRESSION: 9, - Z_DEFAULT_COMPRESSION: -1, - - - Z_FILTERED: 1, - Z_HUFFMAN_ONLY: 2, - Z_RLE: 3, - Z_FIXED: 4, - Z_DEFAULT_STRATEGY: 0, - - /* Possible values of the data_type field (though see inflate()) */ - Z_BINARY: 0, - Z_TEXT: 1, - //Z_ASCII: 1, // = Z_TEXT (deprecated) - Z_UNKNOWN: 2, - - /* The deflate compression method */ - Z_DEFLATED: 8 - //Z_NULL: null // Use -1 or null inline, depending on var type -}; diff --git a/base/app/novnc/vendor/pako/lib/zlib/crc32.js b/base/app/novnc/vendor/pako/lib/zlib/crc32.js deleted file mode 100644 index 611ffb2..0000000 --- a/base/app/novnc/vendor/pako/lib/zlib/crc32.js +++ /dev/null @@ -1,36 +0,0 @@ -// Note: we can't get significant speed boost here. -// So write code to minimize size - no pregenerated tables -// and array tools dependencies. - - -// Use ordinary array, since untyped makes no boost here -export default function makeTable() { - var c, table = []; - - for (var n = 0; n < 256; n++) { - c = n; - for (var k = 0; k < 8; k++) { - c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1)); - } - table[n] = c; - } - - return table; -} - -// Create table on load. Just 255 signed longs. Not a problem. -var crcTable = makeTable(); - - -function crc32(crc, buf, len, pos) { - var t = crcTable, - end = pos + len; - - crc ^= -1; - - for (var i = pos; i < end; i++) { - crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF]; - } - - return (crc ^ (-1)); // >>> 0; -} diff --git a/base/app/novnc/vendor/pako/lib/zlib/deflate.js b/base/app/novnc/vendor/pako/lib/zlib/deflate.js deleted file mode 100644 index c3a5ba4..0000000 --- a/base/app/novnc/vendor/pako/lib/zlib/deflate.js +++ /dev/null @@ -1,1846 +0,0 @@ -import * as utils from "../utils/common.js"; -import * as trees from "./trees.js"; -import adler32 from "./adler32.js"; -import crc32 from "./crc32.js"; -import msg from "./messages.js"; - -/* Public constants ==========================================================*/ -/* ===========================================================================*/ - - -/* Allowed flush values; see deflate() and inflate() below for details */ -export const Z_NO_FLUSH = 0; -export const Z_PARTIAL_FLUSH = 1; -//export const Z_SYNC_FLUSH = 2; -export const Z_FULL_FLUSH = 3; -export const Z_FINISH = 4; -export const Z_BLOCK = 5; -//export const Z_TREES = 6; - - -/* Return codes for the compression/decompression functions. Negative values - * are errors, positive values are used for special but normal events. - */ -export const Z_OK = 0; -export const Z_STREAM_END = 1; -//export const Z_NEED_DICT = 2; -//export const Z_ERRNO = -1; -export const Z_STREAM_ERROR = -2; -export const Z_DATA_ERROR = -3; -//export const Z_MEM_ERROR = -4; -export const Z_BUF_ERROR = -5; -//export const Z_VERSION_ERROR = -6; - - -/* compression levels */ -//export const Z_NO_COMPRESSION = 0; -//export const Z_BEST_SPEED = 1; -//export const Z_BEST_COMPRESSION = 9; -export const Z_DEFAULT_COMPRESSION = -1; - - -export const Z_FILTERED = 1; -export const Z_HUFFMAN_ONLY = 2; -export const Z_RLE = 3; -export const Z_FIXED = 4; -export const Z_DEFAULT_STRATEGY = 0; - -/* Possible values of the data_type field (though see inflate()) */ -//export const Z_BINARY = 0; -//export const Z_TEXT = 1; -//export const Z_ASCII = 1; // = Z_TEXT -export const Z_UNKNOWN = 2; - - -/* The deflate compression method */ -export const Z_DEFLATED = 8; - -/*============================================================================*/ - - -var MAX_MEM_LEVEL = 9; -/* Maximum value for memLevel in deflateInit2 */ -var MAX_WBITS = 15; -/* 32K LZ77 window */ -var DEF_MEM_LEVEL = 8; - - -var LENGTH_CODES = 29; -/* number of length codes, not counting the special END_BLOCK code */ -var LITERALS = 256; -/* number of literal bytes 0..255 */ -var L_CODES = LITERALS + 1 + LENGTH_CODES; -/* number of Literal or Length codes, including the END_BLOCK code */ -var D_CODES = 30; -/* number of distance codes */ -var BL_CODES = 19; -/* number of codes used to transfer the bit lengths */ -var HEAP_SIZE = 2 * L_CODES + 1; -/* maximum heap size */ -var MAX_BITS = 15; -/* All codes must not exceed MAX_BITS bits */ - -var MIN_MATCH = 3; -var MAX_MATCH = 258; -var MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); - -var PRESET_DICT = 0x20; - -var INIT_STATE = 42; -var EXTRA_STATE = 69; -var NAME_STATE = 73; -var COMMENT_STATE = 91; -var HCRC_STATE = 103; -var BUSY_STATE = 113; -var FINISH_STATE = 666; - -var BS_NEED_MORE = 1; /* block not completed, need more input or more output */ -var BS_BLOCK_DONE = 2; /* block flush performed */ -var BS_FINISH_STARTED = 3; /* finish started, need only more output at next deflate */ -var BS_FINISH_DONE = 4; /* finish done, accept no more input or output */ - -var OS_CODE = 0x03; // Unix :) . Don't detect, use this default. - -function err(strm, errorCode) { - strm.msg = msg[errorCode]; - return errorCode; -} - -function rank(f) { - return ((f) << 1) - ((f) > 4 ? 9 : 0); -} - -function zero(buf) { var len = buf.length; while (--len >= 0) { buf[len] = 0; } } - - -/* ========================================================================= - * Flush as much pending output as possible. All deflate() output goes - * through this function so some applications may wish to modify it - * to avoid allocating a large strm->output buffer and copying into it. - * (See also read_buf()). - */ -function flush_pending(strm) { - var s = strm.state; - - //_tr_flush_bits(s); - var len = s.pending; - if (len > strm.avail_out) { - len = strm.avail_out; - } - if (len === 0) { return; } - - utils.arraySet(strm.output, s.pending_buf, s.pending_out, len, strm.next_out); - strm.next_out += len; - s.pending_out += len; - strm.total_out += len; - strm.avail_out -= len; - s.pending -= len; - if (s.pending === 0) { - s.pending_out = 0; - } -} - - -function flush_block_only(s, last) { - trees._tr_flush_block(s, (s.block_start >= 0 ? s.block_start : -1), s.strstart - s.block_start, last); - s.block_start = s.strstart; - flush_pending(s.strm); -} - - -function put_byte(s, b) { - s.pending_buf[s.pending++] = b; -} - - -/* ========================================================================= - * Put a short in the pending buffer. The 16-bit value is put in MSB order. - * IN assertion: the stream state is correct and there is enough room in - * pending_buf. - */ -function putShortMSB(s, b) { -// put_byte(s, (Byte)(b >> 8)); -// put_byte(s, (Byte)(b & 0xff)); - s.pending_buf[s.pending++] = (b >>> 8) & 0xff; - s.pending_buf[s.pending++] = b & 0xff; -} - - -/* =========================================================================== - * Read a new buffer from the current input stream, update the adler32 - * and total number of bytes read. All deflate() input goes through - * this function so some applications may wish to modify it to avoid - * allocating a large strm->input buffer and copying from it. - * (See also flush_pending()). - */ -function read_buf(strm, buf, start, size) { - var len = strm.avail_in; - - if (len > size) { len = size; } - if (len === 0) { return 0; } - - strm.avail_in -= len; - - // zmemcpy(buf, strm->next_in, len); - utils.arraySet(buf, strm.input, strm.next_in, len, start); - if (strm.state.wrap === 1) { - strm.adler = adler32(strm.adler, buf, len, start); - } - - else if (strm.state.wrap === 2) { - strm.adler = crc32(strm.adler, buf, len, start); - } - - strm.next_in += len; - strm.total_in += len; - - return len; -} - - -/* =========================================================================== - * Set match_start to the longest match starting at the given string and - * return its length. Matches shorter or equal to prev_length are discarded, - * in which case the result is equal to prev_length and match_start is - * garbage. - * IN assertions: cur_match is the head of the hash chain for the current - * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 - * OUT assertion: the match length is not greater than s->lookahead. - */ -function longest_match(s, cur_match) { - var chain_length = s.max_chain_length; /* max hash chain length */ - var scan = s.strstart; /* current string */ - var match; /* matched string */ - var len; /* length of current match */ - var best_len = s.prev_length; /* best match length so far */ - var nice_match = s.nice_match; /* stop if match long enough */ - var limit = (s.strstart > (s.w_size - MIN_LOOKAHEAD)) ? - s.strstart - (s.w_size - MIN_LOOKAHEAD) : 0/*NIL*/; - - var _win = s.window; // shortcut - - var wmask = s.w_mask; - var prev = s.prev; - - /* Stop when cur_match becomes <= limit. To simplify the code, - * we prevent matches with the string of window index 0. - */ - - var strend = s.strstart + MAX_MATCH; - var scan_end1 = _win[scan + best_len - 1]; - var scan_end = _win[scan + best_len]; - - /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. - * It is easy to get rid of this optimization if necessary. - */ - // Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); - - /* Do not waste too much time if we already have a good match: */ - if (s.prev_length >= s.good_match) { - chain_length >>= 2; - } - /* Do not look for matches beyond the end of the input. This is necessary - * to make deflate deterministic. - */ - if (nice_match > s.lookahead) { nice_match = s.lookahead; } - - // Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); - - do { - // Assert(cur_match < s->strstart, "no future"); - match = cur_match; - - /* Skip to next match if the match length cannot increase - * or if the match length is less than 2. Note that the checks below - * for insufficient lookahead only occur occasionally for performance - * reasons. Therefore uninitialized memory will be accessed, and - * conditional jumps will be made that depend on those values. - * However the length of the match is limited to the lookahead, so - * the output of deflate is not affected by the uninitialized values. - */ - - if (_win[match + best_len] !== scan_end || - _win[match + best_len - 1] !== scan_end1 || - _win[match] !== _win[scan] || - _win[++match] !== _win[scan + 1]) { - continue; - } - - /* The check at best_len-1 can be removed because it will be made - * again later. (This heuristic is not always a win.) - * It is not necessary to compare scan[2] and match[2] since they - * are always equal when the other bytes match, given that - * the hash keys are equal and that HASH_BITS >= 8. - */ - scan += 2; - match++; - // Assert(*scan == *match, "match[2]?"); - - /* We check for insufficient lookahead only every 8th comparison; - * the 256th check will be made at strstart+258. - */ - do { - // Do nothing - } while (_win[++scan] === _win[++match] && _win[++scan] === _win[++match] && - _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && - _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && - _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && - scan < strend); - - // Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); - - len = MAX_MATCH - (strend - scan); - scan = strend - MAX_MATCH; - - if (len > best_len) { - s.match_start = cur_match; - best_len = len; - if (len >= nice_match) { - break; - } - scan_end1 = _win[scan + best_len - 1]; - scan_end = _win[scan + best_len]; - } - } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length !== 0); - - if (best_len <= s.lookahead) { - return best_len; - } - return s.lookahead; -} - - -/* =========================================================================== - * Fill the window when the lookahead becomes insufficient. - * Updates strstart and lookahead. - * - * IN assertion: lookahead < MIN_LOOKAHEAD - * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD - * At least one byte has been read, or avail_in == 0; reads are - * performed for at least two bytes (required for the zip translate_eol - * option -- not supported here). - */ -function fill_window(s) { - var _w_size = s.w_size; - var p, n, m, more, str; - - //Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); - - do { - more = s.window_size - s.lookahead - s.strstart; - - // JS ints have 32 bit, block below not needed - /* Deal with !@#$% 64K limit: */ - //if (sizeof(int) <= 2) { - // if (more == 0 && s->strstart == 0 && s->lookahead == 0) { - // more = wsize; - // - // } else if (more == (unsigned)(-1)) { - // /* Very unlikely, but possible on 16 bit machine if - // * strstart == 0 && lookahead == 1 (input done a byte at time) - // */ - // more--; - // } - //} - - - /* If the window is almost full and there is insufficient lookahead, - * move the upper half to the lower one to make room in the upper half. - */ - if (s.strstart >= _w_size + (_w_size - MIN_LOOKAHEAD)) { - - utils.arraySet(s.window, s.window, _w_size, _w_size, 0); - s.match_start -= _w_size; - s.strstart -= _w_size; - /* we now have strstart >= MAX_DIST */ - s.block_start -= _w_size; - - /* Slide the hash table (could be avoided with 32 bit values - at the expense of memory usage). We slide even when level == 0 - to keep the hash table consistent if we switch back to level > 0 - later. (Using level 0 permanently is not an optimal usage of - zlib, so we don't care about this pathological case.) - */ - - n = s.hash_size; - p = n; - do { - m = s.head[--p]; - s.head[p] = (m >= _w_size ? m - _w_size : 0); - } while (--n); - - n = _w_size; - p = n; - do { - m = s.prev[--p]; - s.prev[p] = (m >= _w_size ? m - _w_size : 0); - /* If n is not on any hash chain, prev[n] is garbage but - * its value will never be used. - */ - } while (--n); - - more += _w_size; - } - if (s.strm.avail_in === 0) { - break; - } - - /* If there was no sliding: - * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && - * more == window_size - lookahead - strstart - * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) - * => more >= window_size - 2*WSIZE + 2 - * In the BIG_MEM or MMAP case (not yet supported), - * window_size == input_size + MIN_LOOKAHEAD && - * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. - * Otherwise, window_size == 2*WSIZE so more >= 2. - * If there was sliding, more >= WSIZE. So in all cases, more >= 2. - */ - //Assert(more >= 2, "more < 2"); - n = read_buf(s.strm, s.window, s.strstart + s.lookahead, more); - s.lookahead += n; - - /* Initialize the hash value now that we have some input: */ - if (s.lookahead + s.insert >= MIN_MATCH) { - str = s.strstart - s.insert; - s.ins_h = s.window[str]; - - /* UPDATE_HASH(s, s->ins_h, s->window[str + 1]); */ - s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + 1]) & s.hash_mask; -//#if MIN_MATCH != 3 -// Call update_hash() MIN_MATCH-3 more times -//#endif - while (s.insert) { - /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */ - s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + MIN_MATCH - 1]) & s.hash_mask; - - s.prev[str & s.w_mask] = s.head[s.ins_h]; - s.head[s.ins_h] = str; - str++; - s.insert--; - if (s.lookahead + s.insert < MIN_MATCH) { - break; - } - } - } - /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, - * but this is not important since only literal bytes will be emitted. - */ - - } while (s.lookahead < MIN_LOOKAHEAD && s.strm.avail_in !== 0); - - /* If the WIN_INIT bytes after the end of the current data have never been - * written, then zero those bytes in order to avoid memory check reports of - * the use of uninitialized (or uninitialised as Julian writes) bytes by - * the longest match routines. Update the high water mark for the next - * time through here. WIN_INIT is set to MAX_MATCH since the longest match - * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. - */ -// if (s.high_water < s.window_size) { -// var curr = s.strstart + s.lookahead; -// var init = 0; -// -// if (s.high_water < curr) { -// /* Previous high water mark below current data -- zero WIN_INIT -// * bytes or up to end of window, whichever is less. -// */ -// init = s.window_size - curr; -// if (init > WIN_INIT) -// init = WIN_INIT; -// zmemzero(s->window + curr, (unsigned)init); -// s->high_water = curr + init; -// } -// else if (s->high_water < (ulg)curr + WIN_INIT) { -// /* High water mark at or above current data, but below current data -// * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up -// * to end of window, whichever is less. -// */ -// init = (ulg)curr + WIN_INIT - s->high_water; -// if (init > s->window_size - s->high_water) -// init = s->window_size - s->high_water; -// zmemzero(s->window + s->high_water, (unsigned)init); -// s->high_water += init; -// } -// } -// -// Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, -// "not enough room for search"); -} - -/* =========================================================================== - * Copy without compression as much as possible from the input stream, return - * the current block state. - * This function does not insert new strings in the dictionary since - * uncompressible data is probably not useful. This function is used - * only for the level=0 compression option. - * NOTE: this function should be optimized to avoid extra copying from - * window to pending_buf. - */ -function deflate_stored(s, flush) { - /* Stored blocks are limited to 0xffff bytes, pending_buf is limited - * to pending_buf_size, and each stored block has a 5 byte header: - */ - var max_block_size = 0xffff; - - if (max_block_size > s.pending_buf_size - 5) { - max_block_size = s.pending_buf_size - 5; - } - - /* Copy as much as possible from input to output: */ - for (;;) { - /* Fill the window as much as possible: */ - if (s.lookahead <= 1) { - - //Assert(s->strstart < s->w_size+MAX_DIST(s) || - // s->block_start >= (long)s->w_size, "slide too late"); -// if (!(s.strstart < s.w_size + (s.w_size - MIN_LOOKAHEAD) || -// s.block_start >= s.w_size)) { -// throw new Error("slide too late"); -// } - - fill_window(s); - if (s.lookahead === 0 && flush === Z_NO_FLUSH) { - return BS_NEED_MORE; - } - - if (s.lookahead === 0) { - break; - } - /* flush the current block */ - } - //Assert(s->block_start >= 0L, "block gone"); -// if (s.block_start < 0) throw new Error("block gone"); - - s.strstart += s.lookahead; - s.lookahead = 0; - - /* Emit a stored block if pending_buf will be full: */ - var max_start = s.block_start + max_block_size; - - if (s.strstart === 0 || s.strstart >= max_start) { - /* strstart == 0 is possible when wraparound on 16-bit machine */ - s.lookahead = s.strstart - max_start; - s.strstart = max_start; - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - - - } - /* Flush if we may have to slide, otherwise block_start may become - * negative and the data will be gone: - */ - if (s.strstart - s.block_start >= (s.w_size - MIN_LOOKAHEAD)) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - } - - s.insert = 0; - - if (flush === Z_FINISH) { - /*** FLUSH_BLOCK(s, 1); ***/ - flush_block_only(s, true); - if (s.strm.avail_out === 0) { - return BS_FINISH_STARTED; - } - /***/ - return BS_FINISH_DONE; - } - - if (s.strstart > s.block_start) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - - return BS_NEED_MORE; -} - -/* =========================================================================== - * Compress as much as possible from the input stream, return the current - * block state. - * This function does not perform lazy evaluation of matches and inserts - * new strings in the dictionary only for unmatched strings or for short - * matches. It is used only for the fast compression options. - */ -function deflate_fast(s, flush) { - var hash_head; /* head of the hash chain */ - var bflush; /* set if current block must be flushed */ - - for (;;) { - /* Make sure that we always have enough lookahead, except - * at the end of the input file. We need MAX_MATCH bytes - * for the next match, plus MIN_MATCH bytes to insert the - * string following the next match. - */ - if (s.lookahead < MIN_LOOKAHEAD) { - fill_window(s); - if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) { - return BS_NEED_MORE; - } - if (s.lookahead === 0) { - break; /* flush the current block */ - } - } - - /* Insert the string window[strstart .. strstart+2] in the - * dictionary, and set hash_head to the head of the hash chain: - */ - hash_head = 0/*NIL*/; - if (s.lookahead >= MIN_MATCH) { - /*** INSERT_STRING(s, s.strstart, hash_head); ***/ - s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask; - hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; - s.head[s.ins_h] = s.strstart; - /***/ - } - - /* Find the longest match, discarding those <= prev_length. - * At this point we have always match_length < MIN_MATCH - */ - if (hash_head !== 0/*NIL*/ && ((s.strstart - hash_head) <= (s.w_size - MIN_LOOKAHEAD))) { - /* To simplify the code, we prevent matches with the string - * of window index 0 (in particular we have to avoid a match - * of the string with itself at the start of the input file). - */ - s.match_length = longest_match(s, hash_head); - /* longest_match() sets match_start */ - } - if (s.match_length >= MIN_MATCH) { - // check_match(s, s.strstart, s.match_start, s.match_length); // for debug only - - /*** _tr_tally_dist(s, s.strstart - s.match_start, - s.match_length - MIN_MATCH, bflush); ***/ - bflush = trees._tr_tally(s, s.strstart - s.match_start, s.match_length - MIN_MATCH); - - s.lookahead -= s.match_length; - - /* Insert new strings in the hash table only if the match length - * is not too large. This saves time but degrades compression. - */ - if (s.match_length <= s.max_lazy_match/*max_insert_length*/ && s.lookahead >= MIN_MATCH) { - s.match_length--; /* string at strstart already in table */ - do { - s.strstart++; - /*** INSERT_STRING(s, s.strstart, hash_head); ***/ - s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask; - hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; - s.head[s.ins_h] = s.strstart; - /***/ - /* strstart never exceeds WSIZE-MAX_MATCH, so there are - * always MIN_MATCH bytes ahead. - */ - } while (--s.match_length !== 0); - s.strstart++; - } else - { - s.strstart += s.match_length; - s.match_length = 0; - s.ins_h = s.window[s.strstart]; - /* UPDATE_HASH(s, s.ins_h, s.window[s.strstart+1]); */ - s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + 1]) & s.hash_mask; - -//#if MIN_MATCH != 3 -// Call UPDATE_HASH() MIN_MATCH-3 more times -//#endif - /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not - * matter since it will be recomputed at next deflate call. - */ - } - } else { - /* No match, output a literal byte */ - //Tracevv((stderr,"%c", s.window[s.strstart])); - /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ - bflush = trees._tr_tally(s, 0, s.window[s.strstart]); - - s.lookahead--; - s.strstart++; - } - if (bflush) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - } - s.insert = ((s.strstart < (MIN_MATCH - 1)) ? s.strstart : MIN_MATCH - 1); - if (flush === Z_FINISH) { - /*** FLUSH_BLOCK(s, 1); ***/ - flush_block_only(s, true); - if (s.strm.avail_out === 0) { - return BS_FINISH_STARTED; - } - /***/ - return BS_FINISH_DONE; - } - if (s.last_lit) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - return BS_BLOCK_DONE; -} - -/* =========================================================================== - * Same as above, but achieves better compression. We use a lazy - * evaluation for matches: a match is finally adopted only if there is - * no better match at the next window position. - */ -function deflate_slow(s, flush) { - var hash_head; /* head of hash chain */ - var bflush; /* set if current block must be flushed */ - - var max_insert; - - /* Process the input block. */ - for (;;) { - /* Make sure that we always have enough lookahead, except - * at the end of the input file. We need MAX_MATCH bytes - * for the next match, plus MIN_MATCH bytes to insert the - * string following the next match. - */ - if (s.lookahead < MIN_LOOKAHEAD) { - fill_window(s); - if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) { - return BS_NEED_MORE; - } - if (s.lookahead === 0) { break; } /* flush the current block */ - } - - /* Insert the string window[strstart .. strstart+2] in the - * dictionary, and set hash_head to the head of the hash chain: - */ - hash_head = 0/*NIL*/; - if (s.lookahead >= MIN_MATCH) { - /*** INSERT_STRING(s, s.strstart, hash_head); ***/ - s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask; - hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; - s.head[s.ins_h] = s.strstart; - /***/ - } - - /* Find the longest match, discarding those <= prev_length. - */ - s.prev_length = s.match_length; - s.prev_match = s.match_start; - s.match_length = MIN_MATCH - 1; - - if (hash_head !== 0/*NIL*/ && s.prev_length < s.max_lazy_match && - s.strstart - hash_head <= (s.w_size - MIN_LOOKAHEAD)/*MAX_DIST(s)*/) { - /* To simplify the code, we prevent matches with the string - * of window index 0 (in particular we have to avoid a match - * of the string with itself at the start of the input file). - */ - s.match_length = longest_match(s, hash_head); - /* longest_match() sets match_start */ - - if (s.match_length <= 5 && - (s.strategy === Z_FILTERED || (s.match_length === MIN_MATCH && s.strstart - s.match_start > 4096/*TOO_FAR*/))) { - - /* If prev_match is also MIN_MATCH, match_start is garbage - * but we will ignore the current match anyway. - */ - s.match_length = MIN_MATCH - 1; - } - } - /* If there was a match at the previous step and the current - * match is not better, output the previous match: - */ - if (s.prev_length >= MIN_MATCH && s.match_length <= s.prev_length) { - max_insert = s.strstart + s.lookahead - MIN_MATCH; - /* Do not insert strings in hash table beyond this. */ - - //check_match(s, s.strstart-1, s.prev_match, s.prev_length); - - /***_tr_tally_dist(s, s.strstart - 1 - s.prev_match, - s.prev_length - MIN_MATCH, bflush);***/ - bflush = trees._tr_tally(s, s.strstart - 1 - s.prev_match, s.prev_length - MIN_MATCH); - /* Insert in hash table all strings up to the end of the match. - * strstart-1 and strstart are already inserted. If there is not - * enough lookahead, the last two strings are not inserted in - * the hash table. - */ - s.lookahead -= s.prev_length - 1; - s.prev_length -= 2; - do { - if (++s.strstart <= max_insert) { - /*** INSERT_STRING(s, s.strstart, hash_head); ***/ - s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask; - hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; - s.head[s.ins_h] = s.strstart; - /***/ - } - } while (--s.prev_length !== 0); - s.match_available = 0; - s.match_length = MIN_MATCH - 1; - s.strstart++; - - if (bflush) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - - } else if (s.match_available) { - /* If there was no match at the previous position, output a - * single literal. If there was a match but the current match - * is longer, truncate the previous match to a single literal. - */ - //Tracevv((stderr,"%c", s->window[s->strstart-1])); - /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/ - bflush = trees._tr_tally(s, 0, s.window[s.strstart - 1]); - - if (bflush) { - /*** FLUSH_BLOCK_ONLY(s, 0) ***/ - flush_block_only(s, false); - /***/ - } - s.strstart++; - s.lookahead--; - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - } else { - /* There is no previous match to compare with, wait for - * the next step to decide. - */ - s.match_available = 1; - s.strstart++; - s.lookahead--; - } - } - //Assert (flush != Z_NO_FLUSH, "no flush?"); - if (s.match_available) { - //Tracevv((stderr,"%c", s->window[s->strstart-1])); - /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/ - bflush = trees._tr_tally(s, 0, s.window[s.strstart - 1]); - - s.match_available = 0; - } - s.insert = s.strstart < MIN_MATCH - 1 ? s.strstart : MIN_MATCH - 1; - if (flush === Z_FINISH) { - /*** FLUSH_BLOCK(s, 1); ***/ - flush_block_only(s, true); - if (s.strm.avail_out === 0) { - return BS_FINISH_STARTED; - } - /***/ - return BS_FINISH_DONE; - } - if (s.last_lit) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - - return BS_BLOCK_DONE; -} - - -/* =========================================================================== - * For Z_RLE, simply look for runs of bytes, generate matches only of distance - * one. Do not maintain a hash table. (It will be regenerated if this run of - * deflate switches away from Z_RLE.) - */ -function deflate_rle(s, flush) { - var bflush; /* set if current block must be flushed */ - var prev; /* byte at distance one to match */ - var scan, strend; /* scan goes up to strend for length of run */ - - var _win = s.window; - - for (;;) { - /* Make sure that we always have enough lookahead, except - * at the end of the input file. We need MAX_MATCH bytes - * for the longest run, plus one for the unrolled loop. - */ - if (s.lookahead <= MAX_MATCH) { - fill_window(s); - if (s.lookahead <= MAX_MATCH && flush === Z_NO_FLUSH) { - return BS_NEED_MORE; - } - if (s.lookahead === 0) { break; } /* flush the current block */ - } - - /* See how many times the previous byte repeats */ - s.match_length = 0; - if (s.lookahead >= MIN_MATCH && s.strstart > 0) { - scan = s.strstart - 1; - prev = _win[scan]; - if (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan]) { - strend = s.strstart + MAX_MATCH; - do { - // Do nothing - } while (prev === _win[++scan] && prev === _win[++scan] && - prev === _win[++scan] && prev === _win[++scan] && - prev === _win[++scan] && prev === _win[++scan] && - prev === _win[++scan] && prev === _win[++scan] && - scan < strend); - s.match_length = MAX_MATCH - (strend - scan); - if (s.match_length > s.lookahead) { - s.match_length = s.lookahead; - } - } - //Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan"); - } - - /* Emit match if have run of MIN_MATCH or longer, else emit literal */ - if (s.match_length >= MIN_MATCH) { - //check_match(s, s.strstart, s.strstart - 1, s.match_length); - - /*** _tr_tally_dist(s, 1, s.match_length - MIN_MATCH, bflush); ***/ - bflush = trees._tr_tally(s, 1, s.match_length - MIN_MATCH); - - s.lookahead -= s.match_length; - s.strstart += s.match_length; - s.match_length = 0; - } else { - /* No match, output a literal byte */ - //Tracevv((stderr,"%c", s->window[s->strstart])); - /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ - bflush = trees._tr_tally(s, 0, s.window[s.strstart]); - - s.lookahead--; - s.strstart++; - } - if (bflush) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - } - s.insert = 0; - if (flush === Z_FINISH) { - /*** FLUSH_BLOCK(s, 1); ***/ - flush_block_only(s, true); - if (s.strm.avail_out === 0) { - return BS_FINISH_STARTED; - } - /***/ - return BS_FINISH_DONE; - } - if (s.last_lit) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - return BS_BLOCK_DONE; -} - -/* =========================================================================== - * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. - * (It will be regenerated if this run of deflate switches away from Huffman.) - */ -function deflate_huff(s, flush) { - var bflush; /* set if current block must be flushed */ - - for (;;) { - /* Make sure that we have a literal to write. */ - if (s.lookahead === 0) { - fill_window(s); - if (s.lookahead === 0) { - if (flush === Z_NO_FLUSH) { - return BS_NEED_MORE; - } - break; /* flush the current block */ - } - } - - /* Output a literal byte */ - s.match_length = 0; - //Tracevv((stderr,"%c", s->window[s->strstart])); - /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ - bflush = trees._tr_tally(s, 0, s.window[s.strstart]); - s.lookahead--; - s.strstart++; - if (bflush) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - } - s.insert = 0; - if (flush === Z_FINISH) { - /*** FLUSH_BLOCK(s, 1); ***/ - flush_block_only(s, true); - if (s.strm.avail_out === 0) { - return BS_FINISH_STARTED; - } - /***/ - return BS_FINISH_DONE; - } - if (s.last_lit) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - return BS_BLOCK_DONE; -} - -/* Values for max_lazy_match, good_match and max_chain_length, depending on - * the desired pack level (0..9). The values given below have been tuned to - * exclude worst case performance for pathological files. Better values may be - * found for specific files. - */ -function Config(good_length, max_lazy, nice_length, max_chain, func) { - this.good_length = good_length; - this.max_lazy = max_lazy; - this.nice_length = nice_length; - this.max_chain = max_chain; - this.func = func; -} - -var configuration_table; - -configuration_table = [ - /* good lazy nice chain */ - new Config(0, 0, 0, 0, deflate_stored), /* 0 store only */ - new Config(4, 4, 8, 4, deflate_fast), /* 1 max speed, no lazy matches */ - new Config(4, 5, 16, 8, deflate_fast), /* 2 */ - new Config(4, 6, 32, 32, deflate_fast), /* 3 */ - - new Config(4, 4, 16, 16, deflate_slow), /* 4 lazy matches */ - new Config(8, 16, 32, 32, deflate_slow), /* 5 */ - new Config(8, 16, 128, 128, deflate_slow), /* 6 */ - new Config(8, 32, 128, 256, deflate_slow), /* 7 */ - new Config(32, 128, 258, 1024, deflate_slow), /* 8 */ - new Config(32, 258, 258, 4096, deflate_slow) /* 9 max compression */ -]; - - -/* =========================================================================== - * Initialize the "longest match" routines for a new zlib stream - */ -function lm_init(s) { - s.window_size = 2 * s.w_size; - - /*** CLEAR_HASH(s); ***/ - zero(s.head); // Fill with NIL (= 0); - - /* Set the default configuration parameters: - */ - s.max_lazy_match = configuration_table[s.level].max_lazy; - s.good_match = configuration_table[s.level].good_length; - s.nice_match = configuration_table[s.level].nice_length; - s.max_chain_length = configuration_table[s.level].max_chain; - - s.strstart = 0; - s.block_start = 0; - s.lookahead = 0; - s.insert = 0; - s.match_length = s.prev_length = MIN_MATCH - 1; - s.match_available = 0; - s.ins_h = 0; -} - - -function DeflateState() { - this.strm = null; /* pointer back to this zlib stream */ - this.status = 0; /* as the name implies */ - this.pending_buf = null; /* output still pending */ - this.pending_buf_size = 0; /* size of pending_buf */ - this.pending_out = 0; /* next pending byte to output to the stream */ - this.pending = 0; /* nb of bytes in the pending buffer */ - this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */ - this.gzhead = null; /* gzip header information to write */ - this.gzindex = 0; /* where in extra, name, or comment */ - this.method = Z_DEFLATED; /* can only be DEFLATED */ - this.last_flush = -1; /* value of flush param for previous deflate call */ - - this.w_size = 0; /* LZ77 window size (32K by default) */ - this.w_bits = 0; /* log2(w_size) (8..16) */ - this.w_mask = 0; /* w_size - 1 */ - - this.window = null; - /* Sliding window. Input bytes are read into the second half of the window, - * and move to the first half later to keep a dictionary of at least wSize - * bytes. With this organization, matches are limited to a distance of - * wSize-MAX_MATCH bytes, but this ensures that IO is always - * performed with a length multiple of the block size. - */ - - this.window_size = 0; - /* Actual size of window: 2*wSize, except when the user input buffer - * is directly used as sliding window. - */ - - this.prev = null; - /* Link to older string with same hash index. To limit the size of this - * array to 64K, this link is maintained only for the last 32K strings. - * An index in this array is thus a window index modulo 32K. - */ - - this.head = null; /* Heads of the hash chains or NIL. */ - - this.ins_h = 0; /* hash index of string to be inserted */ - this.hash_size = 0; /* number of elements in hash table */ - this.hash_bits = 0; /* log2(hash_size) */ - this.hash_mask = 0; /* hash_size-1 */ - - this.hash_shift = 0; - /* Number of bits by which ins_h must be shifted at each input - * step. It must be such that after MIN_MATCH steps, the oldest - * byte no longer takes part in the hash key, that is: - * hash_shift * MIN_MATCH >= hash_bits - */ - - this.block_start = 0; - /* Window position at the beginning of the current output block. Gets - * negative when the window is moved backwards. - */ - - this.match_length = 0; /* length of best match */ - this.prev_match = 0; /* previous match */ - this.match_available = 0; /* set if previous match exists */ - this.strstart = 0; /* start of string to insert */ - this.match_start = 0; /* start of matching string */ - this.lookahead = 0; /* number of valid bytes ahead in window */ - - this.prev_length = 0; - /* Length of the best match at previous step. Matches not greater than this - * are discarded. This is used in the lazy match evaluation. - */ - - this.max_chain_length = 0; - /* To speed up deflation, hash chains are never searched beyond this - * length. A higher limit improves compression ratio but degrades the - * speed. - */ - - this.max_lazy_match = 0; - /* Attempt to find a better match only when the current match is strictly - * smaller than this value. This mechanism is used only for compression - * levels >= 4. - */ - // That's alias to max_lazy_match, don't use directly - //this.max_insert_length = 0; - /* Insert new strings in the hash table only if the match length is not - * greater than this length. This saves time but degrades compression. - * max_insert_length is used only for compression levels <= 3. - */ - - this.level = 0; /* compression level (1..9) */ - this.strategy = 0; /* favor or force Huffman coding*/ - - this.good_match = 0; - /* Use a faster search when the previous match is longer than this */ - - this.nice_match = 0; /* Stop searching when current match exceeds this */ - - /* used by trees.c: */ - - /* Didn't use ct_data typedef below to suppress compiler warning */ - - // struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ - // struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ - // struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ - - // Use flat array of DOUBLE size, with interleaved fata, - // because JS does not support effective - this.dyn_ltree = new utils.Buf16(HEAP_SIZE * 2); - this.dyn_dtree = new utils.Buf16((2 * D_CODES + 1) * 2); - this.bl_tree = new utils.Buf16((2 * BL_CODES + 1) * 2); - zero(this.dyn_ltree); - zero(this.dyn_dtree); - zero(this.bl_tree); - - this.l_desc = null; /* desc. for literal tree */ - this.d_desc = null; /* desc. for distance tree */ - this.bl_desc = null; /* desc. for bit length tree */ - - //ush bl_count[MAX_BITS+1]; - this.bl_count = new utils.Buf16(MAX_BITS + 1); - /* number of codes at each bit length for an optimal tree */ - - //int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ - this.heap = new utils.Buf16(2 * L_CODES + 1); /* heap used to build the Huffman trees */ - zero(this.heap); - - this.heap_len = 0; /* number of elements in the heap */ - this.heap_max = 0; /* element of largest frequency */ - /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. - * The same heap array is used to build all trees. - */ - - this.depth = new utils.Buf16(2 * L_CODES + 1); //uch depth[2*L_CODES+1]; - zero(this.depth); - /* Depth of each subtree used as tie breaker for trees of equal frequency - */ - - this.l_buf = 0; /* buffer index for literals or lengths */ - - this.lit_bufsize = 0; - /* Size of match buffer for literals/lengths. There are 4 reasons for - * limiting lit_bufsize to 64K: - * - frequencies can be kept in 16 bit counters - * - if compression is not successful for the first block, all input - * data is still in the window so we can still emit a stored block even - * when input comes from standard input. (This can also be done for - * all blocks if lit_bufsize is not greater than 32K.) - * - if compression is not successful for a file smaller than 64K, we can - * even emit a stored file instead of a stored block (saving 5 bytes). - * This is applicable only for zip (not gzip or zlib). - * - creating new Huffman trees less frequently may not provide fast - * adaptation to changes in the input data statistics. (Take for - * example a binary file with poorly compressible code followed by - * a highly compressible string table.) Smaller buffer sizes give - * fast adaptation but have of course the overhead of transmitting - * trees more frequently. - * - I can't count above 4 - */ - - this.last_lit = 0; /* running index in l_buf */ - - this.d_buf = 0; - /* Buffer index for distances. To simplify the code, d_buf and l_buf have - * the same number of elements. To use different lengths, an extra flag - * array would be necessary. - */ - - this.opt_len = 0; /* bit length of current block with optimal trees */ - this.static_len = 0; /* bit length of current block with static trees */ - this.matches = 0; /* number of string matches in current block */ - this.insert = 0; /* bytes at end of window left to insert */ - - - this.bi_buf = 0; - /* Output buffer. bits are inserted starting at the bottom (least - * significant bits). - */ - this.bi_valid = 0; - /* Number of valid bits in bi_buf. All bits above the last valid bit - * are always zero. - */ - - // Used for window memory init. We safely ignore it for JS. That makes - // sense only for pointers and memory check tools. - //this.high_water = 0; - /* High water mark offset in window for initialized bytes -- bytes above - * this are set to zero in order to avoid memory check warnings when - * longest match routines access bytes past the input. This is then - * updated to the new high water mark. - */ -} - - -function deflateResetKeep(strm) { - var s; - - if (!strm || !strm.state) { - return err(strm, Z_STREAM_ERROR); - } - - strm.total_in = strm.total_out = 0; - strm.data_type = Z_UNKNOWN; - - s = strm.state; - s.pending = 0; - s.pending_out = 0; - - if (s.wrap < 0) { - s.wrap = -s.wrap; - /* was made negative by deflate(..., Z_FINISH); */ - } - s.status = (s.wrap ? INIT_STATE : BUSY_STATE); - strm.adler = (s.wrap === 2) ? - 0 // crc32(0, Z_NULL, 0) - : - 1; // adler32(0, Z_NULL, 0) - s.last_flush = Z_NO_FLUSH; - trees._tr_init(s); - return Z_OK; -} - - -function deflateReset(strm) { - var ret = deflateResetKeep(strm); - if (ret === Z_OK) { - lm_init(strm.state); - } - return ret; -} - - -function deflateSetHeader(strm, head) { - if (!strm || !strm.state) { return Z_STREAM_ERROR; } - if (strm.state.wrap !== 2) { return Z_STREAM_ERROR; } - strm.state.gzhead = head; - return Z_OK; -} - - -function deflateInit2(strm, level, method, windowBits, memLevel, strategy) { - if (!strm) { // === Z_NULL - return Z_STREAM_ERROR; - } - var wrap = 1; - - if (level === Z_DEFAULT_COMPRESSION) { - level = 6; - } - - if (windowBits < 0) { /* suppress zlib wrapper */ - wrap = 0; - windowBits = -windowBits; - } - - else if (windowBits > 15) { - wrap = 2; /* write gzip wrapper instead */ - windowBits -= 16; - } - - - if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method !== Z_DEFLATED || - windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || - strategy < 0 || strategy > Z_FIXED) { - return err(strm, Z_STREAM_ERROR); - } - - - if (windowBits === 8) { - windowBits = 9; - } - /* until 256-byte window bug fixed */ - - var s = new DeflateState(); - - strm.state = s; - s.strm = strm; - - s.wrap = wrap; - s.gzhead = null; - s.w_bits = windowBits; - s.w_size = 1 << s.w_bits; - s.w_mask = s.w_size - 1; - - s.hash_bits = memLevel + 7; - s.hash_size = 1 << s.hash_bits; - s.hash_mask = s.hash_size - 1; - s.hash_shift = ~~((s.hash_bits + MIN_MATCH - 1) / MIN_MATCH); - - s.window = new utils.Buf8(s.w_size * 2); - s.head = new utils.Buf16(s.hash_size); - s.prev = new utils.Buf16(s.w_size); - - // Don't need mem init magic for JS. - //s.high_water = 0; /* nothing written to s->window yet */ - - s.lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ - - s.pending_buf_size = s.lit_bufsize * 4; - - //overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); - //s->pending_buf = (uchf *) overlay; - s.pending_buf = new utils.Buf8(s.pending_buf_size); - - // It is offset from `s.pending_buf` (size is `s.lit_bufsize * 2`) - //s->d_buf = overlay + s->lit_bufsize/sizeof(ush); - s.d_buf = 1 * s.lit_bufsize; - - //s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; - s.l_buf = (1 + 2) * s.lit_bufsize; - - s.level = level; - s.strategy = strategy; - s.method = method; - - return deflateReset(strm); -} - -function deflateInit(strm, level) { - return deflateInit2(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); -} - - -function deflate(strm, flush) { - var old_flush, s; - var beg, val; // for gzip header write only - - if (!strm || !strm.state || - flush > Z_BLOCK || flush < 0) { - return strm ? err(strm, Z_STREAM_ERROR) : Z_STREAM_ERROR; - } - - s = strm.state; - - if (!strm.output || - (!strm.input && strm.avail_in !== 0) || - (s.status === FINISH_STATE && flush !== Z_FINISH)) { - return err(strm, (strm.avail_out === 0) ? Z_BUF_ERROR : Z_STREAM_ERROR); - } - - s.strm = strm; /* just in case */ - old_flush = s.last_flush; - s.last_flush = flush; - - /* Write the header */ - if (s.status === INIT_STATE) { - - if (s.wrap === 2) { // GZIP header - strm.adler = 0; //crc32(0L, Z_NULL, 0); - put_byte(s, 31); - put_byte(s, 139); - put_byte(s, 8); - if (!s.gzhead) { // s->gzhead == Z_NULL - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, s.level === 9 ? 2 : - (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? - 4 : 0)); - put_byte(s, OS_CODE); - s.status = BUSY_STATE; - } - else { - put_byte(s, (s.gzhead.text ? 1 : 0) + - (s.gzhead.hcrc ? 2 : 0) + - (!s.gzhead.extra ? 0 : 4) + - (!s.gzhead.name ? 0 : 8) + - (!s.gzhead.comment ? 0 : 16) - ); - put_byte(s, s.gzhead.time & 0xff); - put_byte(s, (s.gzhead.time >> 8) & 0xff); - put_byte(s, (s.gzhead.time >> 16) & 0xff); - put_byte(s, (s.gzhead.time >> 24) & 0xff); - put_byte(s, s.level === 9 ? 2 : - (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? - 4 : 0)); - put_byte(s, s.gzhead.os & 0xff); - if (s.gzhead.extra && s.gzhead.extra.length) { - put_byte(s, s.gzhead.extra.length & 0xff); - put_byte(s, (s.gzhead.extra.length >> 8) & 0xff); - } - if (s.gzhead.hcrc) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending, 0); - } - s.gzindex = 0; - s.status = EXTRA_STATE; - } - } - else // DEFLATE header - { - var header = (Z_DEFLATED + ((s.w_bits - 8) << 4)) << 8; - var level_flags = -1; - - if (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2) { - level_flags = 0; - } else if (s.level < 6) { - level_flags = 1; - } else if (s.level === 6) { - level_flags = 2; - } else { - level_flags = 3; - } - header |= (level_flags << 6); - if (s.strstart !== 0) { header |= PRESET_DICT; } - header += 31 - (header % 31); - - s.status = BUSY_STATE; - putShortMSB(s, header); - - /* Save the adler32 of the preset dictionary: */ - if (s.strstart !== 0) { - putShortMSB(s, strm.adler >>> 16); - putShortMSB(s, strm.adler & 0xffff); - } - strm.adler = 1; // adler32(0L, Z_NULL, 0); - } - } - -//#ifdef GZIP - if (s.status === EXTRA_STATE) { - if (s.gzhead.extra/* != Z_NULL*/) { - beg = s.pending; /* start of bytes to update crc */ - - while (s.gzindex < (s.gzhead.extra.length & 0xffff)) { - if (s.pending === s.pending_buf_size) { - if (s.gzhead.hcrc && s.pending > beg) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); - } - flush_pending(strm); - beg = s.pending; - if (s.pending === s.pending_buf_size) { - break; - } - } - put_byte(s, s.gzhead.extra[s.gzindex] & 0xff); - s.gzindex++; - } - if (s.gzhead.hcrc && s.pending > beg) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); - } - if (s.gzindex === s.gzhead.extra.length) { - s.gzindex = 0; - s.status = NAME_STATE; - } - } - else { - s.status = NAME_STATE; - } - } - if (s.status === NAME_STATE) { - if (s.gzhead.name/* != Z_NULL*/) { - beg = s.pending; /* start of bytes to update crc */ - //int val; - - do { - if (s.pending === s.pending_buf_size) { - if (s.gzhead.hcrc && s.pending > beg) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); - } - flush_pending(strm); - beg = s.pending; - if (s.pending === s.pending_buf_size) { - val = 1; - break; - } - } - // JS specific: little magic to add zero terminator to end of string - if (s.gzindex < s.gzhead.name.length) { - val = s.gzhead.name.charCodeAt(s.gzindex++) & 0xff; - } else { - val = 0; - } - put_byte(s, val); - } while (val !== 0); - - if (s.gzhead.hcrc && s.pending > beg) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); - } - if (val === 0) { - s.gzindex = 0; - s.status = COMMENT_STATE; - } - } - else { - s.status = COMMENT_STATE; - } - } - if (s.status === COMMENT_STATE) { - if (s.gzhead.comment/* != Z_NULL*/) { - beg = s.pending; /* start of bytes to update crc */ - //int val; - - do { - if (s.pending === s.pending_buf_size) { - if (s.gzhead.hcrc && s.pending > beg) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); - } - flush_pending(strm); - beg = s.pending; - if (s.pending === s.pending_buf_size) { - val = 1; - break; - } - } - // JS specific: little magic to add zero terminator to end of string - if (s.gzindex < s.gzhead.comment.length) { - val = s.gzhead.comment.charCodeAt(s.gzindex++) & 0xff; - } else { - val = 0; - } - put_byte(s, val); - } while (val !== 0); - - if (s.gzhead.hcrc && s.pending > beg) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); - } - if (val === 0) { - s.status = HCRC_STATE; - } - } - else { - s.status = HCRC_STATE; - } - } - if (s.status === HCRC_STATE) { - if (s.gzhead.hcrc) { - if (s.pending + 2 > s.pending_buf_size) { - flush_pending(strm); - } - if (s.pending + 2 <= s.pending_buf_size) { - put_byte(s, strm.adler & 0xff); - put_byte(s, (strm.adler >> 8) & 0xff); - strm.adler = 0; //crc32(0L, Z_NULL, 0); - s.status = BUSY_STATE; - } - } - else { - s.status = BUSY_STATE; - } - } -//#endif - - /* Flush as much pending output as possible */ - if (s.pending !== 0) { - flush_pending(strm); - if (strm.avail_out === 0) { - /* Since avail_out is 0, deflate will be called again with - * more output space, but possibly with both pending and - * avail_in equal to zero. There won't be anything to do, - * but this is not an error situation so make sure we - * return OK instead of BUF_ERROR at next call of deflate: - */ - s.last_flush = -1; - return Z_OK; - } - - /* Make sure there is something to do and avoid duplicate consecutive - * flushes. For repeated and useless calls with Z_FINISH, we keep - * returning Z_STREAM_END instead of Z_BUF_ERROR. - */ - } else if (strm.avail_in === 0 && rank(flush) <= rank(old_flush) && - flush !== Z_FINISH) { - return err(strm, Z_BUF_ERROR); - } - - /* User must not provide more input after the first FINISH: */ - if (s.status === FINISH_STATE && strm.avail_in !== 0) { - return err(strm, Z_BUF_ERROR); - } - - /* Start a new block or continue the current one. - */ - if (strm.avail_in !== 0 || s.lookahead !== 0 || - (flush !== Z_NO_FLUSH && s.status !== FINISH_STATE)) { - var bstate = (s.strategy === Z_HUFFMAN_ONLY) ? deflate_huff(s, flush) : - (s.strategy === Z_RLE ? deflate_rle(s, flush) : - configuration_table[s.level].func(s, flush)); - - if (bstate === BS_FINISH_STARTED || bstate === BS_FINISH_DONE) { - s.status = FINISH_STATE; - } - if (bstate === BS_NEED_MORE || bstate === BS_FINISH_STARTED) { - if (strm.avail_out === 0) { - s.last_flush = -1; - /* avoid BUF_ERROR next call, see above */ - } - return Z_OK; - /* If flush != Z_NO_FLUSH && avail_out == 0, the next call - * of deflate should use the same flush parameter to make sure - * that the flush is complete. So we don't have to output an - * empty block here, this will be done at next call. This also - * ensures that for a very small output buffer, we emit at most - * one empty block. - */ - } - if (bstate === BS_BLOCK_DONE) { - if (flush === Z_PARTIAL_FLUSH) { - trees._tr_align(s); - } - else if (flush !== Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ - - trees._tr_stored_block(s, 0, 0, false); - /* For a full flush, this empty block will be recognized - * as a special marker by inflate_sync(). - */ - if (flush === Z_FULL_FLUSH) { - /*** CLEAR_HASH(s); ***/ /* forget history */ - zero(s.head); // Fill with NIL (= 0); - - if (s.lookahead === 0) { - s.strstart = 0; - s.block_start = 0; - s.insert = 0; - } - } - } - flush_pending(strm); - if (strm.avail_out === 0) { - s.last_flush = -1; /* avoid BUF_ERROR at next call, see above */ - return Z_OK; - } - } - } - //Assert(strm->avail_out > 0, "bug2"); - //if (strm.avail_out <= 0) { throw new Error("bug2");} - - if (flush !== Z_FINISH) { return Z_OK; } - if (s.wrap <= 0) { return Z_STREAM_END; } - - /* Write the trailer */ - if (s.wrap === 2) { - put_byte(s, strm.adler & 0xff); - put_byte(s, (strm.adler >> 8) & 0xff); - put_byte(s, (strm.adler >> 16) & 0xff); - put_byte(s, (strm.adler >> 24) & 0xff); - put_byte(s, strm.total_in & 0xff); - put_byte(s, (strm.total_in >> 8) & 0xff); - put_byte(s, (strm.total_in >> 16) & 0xff); - put_byte(s, (strm.total_in >> 24) & 0xff); - } - else - { - putShortMSB(s, strm.adler >>> 16); - putShortMSB(s, strm.adler & 0xffff); - } - - flush_pending(strm); - /* If avail_out is zero, the application will call deflate again - * to flush the rest. - */ - if (s.wrap > 0) { s.wrap = -s.wrap; } - /* write the trailer only once! */ - return s.pending !== 0 ? Z_OK : Z_STREAM_END; -} - -function deflateEnd(strm) { - var status; - - if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) { - return Z_STREAM_ERROR; - } - - status = strm.state.status; - if (status !== INIT_STATE && - status !== EXTRA_STATE && - status !== NAME_STATE && - status !== COMMENT_STATE && - status !== HCRC_STATE && - status !== BUSY_STATE && - status !== FINISH_STATE - ) { - return err(strm, Z_STREAM_ERROR); - } - - strm.state = null; - - return status === BUSY_STATE ? err(strm, Z_DATA_ERROR) : Z_OK; -} - - -/* ========================================================================= - * Initializes the compression dictionary from the given byte - * sequence without producing any compressed output. - */ -function deflateSetDictionary(strm, dictionary) { - var dictLength = dictionary.length; - - var s; - var str, n; - var wrap; - var avail; - var next; - var input; - var tmpDict; - - if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) { - return Z_STREAM_ERROR; - } - - s = strm.state; - wrap = s.wrap; - - if (wrap === 2 || (wrap === 1 && s.status !== INIT_STATE) || s.lookahead) { - return Z_STREAM_ERROR; - } - - /* when using zlib wrappers, compute Adler-32 for provided dictionary */ - if (wrap === 1) { - /* adler32(strm->adler, dictionary, dictLength); */ - strm.adler = adler32(strm.adler, dictionary, dictLength, 0); - } - - s.wrap = 0; /* avoid computing Adler-32 in read_buf */ - - /* if dictionary would fill window, just replace the history */ - if (dictLength >= s.w_size) { - if (wrap === 0) { /* already empty otherwise */ - /*** CLEAR_HASH(s); ***/ - zero(s.head); // Fill with NIL (= 0); - s.strstart = 0; - s.block_start = 0; - s.insert = 0; - } - /* use the tail */ - // dictionary = dictionary.slice(dictLength - s.w_size); - tmpDict = new utils.Buf8(s.w_size); - utils.arraySet(tmpDict, dictionary, dictLength - s.w_size, s.w_size, 0); - dictionary = tmpDict; - dictLength = s.w_size; - } - /* insert dictionary into window and hash */ - avail = strm.avail_in; - next = strm.next_in; - input = strm.input; - strm.avail_in = dictLength; - strm.next_in = 0; - strm.input = dictionary; - fill_window(s); - while (s.lookahead >= MIN_MATCH) { - str = s.strstart; - n = s.lookahead - (MIN_MATCH - 1); - do { - /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */ - s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + MIN_MATCH - 1]) & s.hash_mask; - - s.prev[str & s.w_mask] = s.head[s.ins_h]; - - s.head[s.ins_h] = str; - str++; - } while (--n); - s.strstart = str; - s.lookahead = MIN_MATCH - 1; - fill_window(s); - } - s.strstart += s.lookahead; - s.block_start = s.strstart; - s.insert = s.lookahead; - s.lookahead = 0; - s.match_length = s.prev_length = MIN_MATCH - 1; - s.match_available = 0; - strm.next_in = next; - strm.input = input; - strm.avail_in = avail; - s.wrap = wrap; - return Z_OK; -} - - -export { deflateInit, deflateInit2, deflateReset, deflateResetKeep, deflateSetHeader, deflate, deflateEnd, deflateSetDictionary }; -export var deflateInfo = 'pako deflate (from Nodeca project)'; - -/* Not implemented -exports.deflateBound = deflateBound; -exports.deflateCopy = deflateCopy; -exports.deflateParams = deflateParams; -exports.deflatePending = deflatePending; -exports.deflatePrime = deflatePrime; -exports.deflateTune = deflateTune; -*/ diff --git a/base/app/novnc/vendor/pako/lib/zlib/gzheader.js b/base/app/novnc/vendor/pako/lib/zlib/gzheader.js deleted file mode 100644 index 2ec586d..0000000 --- a/base/app/novnc/vendor/pako/lib/zlib/gzheader.js +++ /dev/null @@ -1,35 +0,0 @@ -export default function GZheader() { - /* true if compressed data believed to be text */ - this.text = 0; - /* modification time */ - this.time = 0; - /* extra flags (not used when writing a gzip file) */ - this.xflags = 0; - /* operating system */ - this.os = 0; - /* pointer to extra field or Z_NULL if none */ - this.extra = null; - /* extra field length (valid if extra != Z_NULL) */ - this.extra_len = 0; // Actually, we don't need it in JS, - // but leave for few code modifications - - // - // Setup limits is not necessary because in js we should not preallocate memory - // for inflate use constant limit in 65536 bytes - // - - /* space at extra (only when reading header) */ - // this.extra_max = 0; - /* pointer to zero-terminated file name or Z_NULL */ - this.name = ''; - /* space at name (only when reading header) */ - // this.name_max = 0; - /* pointer to zero-terminated comment or Z_NULL */ - this.comment = ''; - /* space at comment (only when reading header) */ - // this.comm_max = 0; - /* true if there was or will be a header crc */ - this.hcrc = 0; - /* true when done reading gzip header (not used when writing a gzip file) */ - this.done = false; -} diff --git a/base/app/novnc/vendor/pako/lib/zlib/inffast.js b/base/app/novnc/vendor/pako/lib/zlib/inffast.js deleted file mode 100644 index 889dcc7..0000000 --- a/base/app/novnc/vendor/pako/lib/zlib/inffast.js +++ /dev/null @@ -1,324 +0,0 @@ -// See state defs from inflate.js -var BAD = 30; /* got a data error -- remain here until reset */ -var TYPE = 12; /* i: waiting for type bits, including last-flag bit */ - -/* - Decode literal, length, and distance codes and write out the resulting - literal and match bytes until either not enough input or output is - available, an end-of-block is encountered, or a data error is encountered. - When large enough input and output buffers are supplied to inflate(), for - example, a 16K input buffer and a 64K output buffer, more than 95% of the - inflate execution time is spent in this routine. - - Entry assumptions: - - state.mode === LEN - strm.avail_in >= 6 - strm.avail_out >= 258 - start >= strm.avail_out - state.bits < 8 - - On return, state.mode is one of: - - LEN -- ran out of enough output space or enough available input - TYPE -- reached end of block code, inflate() to interpret next block - BAD -- error in block data - - Notes: - - - The maximum input bits used by a length/distance pair is 15 bits for the - length code, 5 bits for the length extra, 15 bits for the distance code, - and 13 bits for the distance extra. This totals 48 bits, or six bytes. - Therefore if strm.avail_in >= 6, then there is enough input to avoid - checking for available input while decoding. - - - The maximum bytes that a single length/distance pair can output is 258 - bytes, which is the maximum length that can be coded. inflate_fast() - requires strm.avail_out >= 258 for each loop to avoid checking for - output space. - */ -export default function inflate_fast(strm, start) { - var state; - var _in; /* local strm.input */ - var last; /* have enough input while in < last */ - var _out; /* local strm.output */ - var beg; /* inflate()'s initial strm.output */ - var end; /* while out < end, enough space available */ -//#ifdef INFLATE_STRICT - var dmax; /* maximum distance from zlib header */ -//#endif - var wsize; /* window size or zero if not using window */ - var whave; /* valid bytes in the window */ - var wnext; /* window write index */ - // Use `s_window` instead `window`, avoid conflict with instrumentation tools - var s_window; /* allocated sliding window, if wsize != 0 */ - var hold; /* local strm.hold */ - var bits; /* local strm.bits */ - var lcode; /* local strm.lencode */ - var dcode; /* local strm.distcode */ - var lmask; /* mask for first level of length codes */ - var dmask; /* mask for first level of distance codes */ - var here; /* retrieved table entry */ - var op; /* code bits, operation, extra bits, or */ - /* window position, window bytes to copy */ - var len; /* match length, unused bytes */ - var dist; /* match distance */ - var from; /* where to copy match from */ - var from_source; - - - var input, output; // JS specific, because we have no pointers - - /* copy state to local variables */ - state = strm.state; - //here = state.here; - _in = strm.next_in; - input = strm.input; - last = _in + (strm.avail_in - 5); - _out = strm.next_out; - output = strm.output; - beg = _out - (start - strm.avail_out); - end = _out + (strm.avail_out - 257); -//#ifdef INFLATE_STRICT - dmax = state.dmax; -//#endif - wsize = state.wsize; - whave = state.whave; - wnext = state.wnext; - s_window = state.window; - hold = state.hold; - bits = state.bits; - lcode = state.lencode; - dcode = state.distcode; - lmask = (1 << state.lenbits) - 1; - dmask = (1 << state.distbits) - 1; - - - /* decode literals and length/distances until end-of-block or not enough - input data or output space */ - - top: - do { - if (bits < 15) { - hold += input[_in++] << bits; - bits += 8; - hold += input[_in++] << bits; - bits += 8; - } - - here = lcode[hold & lmask]; - - dolen: - for (;;) { // Goto emulation - op = here >>> 24/*here.bits*/; - hold >>>= op; - bits -= op; - op = (here >>> 16) & 0xff/*here.op*/; - if (op === 0) { /* literal */ - //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? - // "inflate: literal '%c'\n" : - // "inflate: literal 0x%02x\n", here.val)); - output[_out++] = here & 0xffff/*here.val*/; - } - else if (op & 16) { /* length base */ - len = here & 0xffff/*here.val*/; - op &= 15; /* number of extra bits */ - if (op) { - if (bits < op) { - hold += input[_in++] << bits; - bits += 8; - } - len += hold & ((1 << op) - 1); - hold >>>= op; - bits -= op; - } - //Tracevv((stderr, "inflate: length %u\n", len)); - if (bits < 15) { - hold += input[_in++] << bits; - bits += 8; - hold += input[_in++] << bits; - bits += 8; - } - here = dcode[hold & dmask]; - - dodist: - for (;;) { // goto emulation - op = here >>> 24/*here.bits*/; - hold >>>= op; - bits -= op; - op = (here >>> 16) & 0xff/*here.op*/; - - if (op & 16) { /* distance base */ - dist = here & 0xffff/*here.val*/; - op &= 15; /* number of extra bits */ - if (bits < op) { - hold += input[_in++] << bits; - bits += 8; - if (bits < op) { - hold += input[_in++] << bits; - bits += 8; - } - } - dist += hold & ((1 << op) - 1); -//#ifdef INFLATE_STRICT - if (dist > dmax) { - strm.msg = 'invalid distance too far back'; - state.mode = BAD; - break top; - } -//#endif - hold >>>= op; - bits -= op; - //Tracevv((stderr, "inflate: distance %u\n", dist)); - op = _out - beg; /* max distance in output */ - if (dist > op) { /* see if copy from window */ - op = dist - op; /* distance back in window */ - if (op > whave) { - if (state.sane) { - strm.msg = 'invalid distance too far back'; - state.mode = BAD; - break top; - } - -// (!) This block is disabled in zlib defailts, -// don't enable it for binary compatibility -//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR -// if (len <= op - whave) { -// do { -// output[_out++] = 0; -// } while (--len); -// continue top; -// } -// len -= op - whave; -// do { -// output[_out++] = 0; -// } while (--op > whave); -// if (op === 0) { -// from = _out - dist; -// do { -// output[_out++] = output[from++]; -// } while (--len); -// continue top; -// } -//#endif - } - from = 0; // window index - from_source = s_window; - if (wnext === 0) { /* very common case */ - from += wsize - op; - if (op < len) { /* some from window */ - len -= op; - do { - output[_out++] = s_window[from++]; - } while (--op); - from = _out - dist; /* rest from output */ - from_source = output; - } - } - else if (wnext < op) { /* wrap around window */ - from += wsize + wnext - op; - op -= wnext; - if (op < len) { /* some from end of window */ - len -= op; - do { - output[_out++] = s_window[from++]; - } while (--op); - from = 0; - if (wnext < len) { /* some from start of window */ - op = wnext; - len -= op; - do { - output[_out++] = s_window[from++]; - } while (--op); - from = _out - dist; /* rest from output */ - from_source = output; - } - } - } - else { /* contiguous in window */ - from += wnext - op; - if (op < len) { /* some from window */ - len -= op; - do { - output[_out++] = s_window[from++]; - } while (--op); - from = _out - dist; /* rest from output */ - from_source = output; - } - } - while (len > 2) { - output[_out++] = from_source[from++]; - output[_out++] = from_source[from++]; - output[_out++] = from_source[from++]; - len -= 3; - } - if (len) { - output[_out++] = from_source[from++]; - if (len > 1) { - output[_out++] = from_source[from++]; - } - } - } - else { - from = _out - dist; /* copy direct from output */ - do { /* minimum length is three */ - output[_out++] = output[from++]; - output[_out++] = output[from++]; - output[_out++] = output[from++]; - len -= 3; - } while (len > 2); - if (len) { - output[_out++] = output[from++]; - if (len > 1) { - output[_out++] = output[from++]; - } - } - } - } - else if ((op & 64) === 0) { /* 2nd level distance code */ - here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))]; - continue dodist; - } - else { - strm.msg = 'invalid distance code'; - state.mode = BAD; - break top; - } - - break; // need to emulate goto via "continue" - } - } - else if ((op & 64) === 0) { /* 2nd level length code */ - here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))]; - continue dolen; - } - else if (op & 32) { /* end-of-block */ - //Tracevv((stderr, "inflate: end of block\n")); - state.mode = TYPE; - break top; - } - else { - strm.msg = 'invalid literal/length code'; - state.mode = BAD; - break top; - } - - break; // need to emulate goto via "continue" - } - } while (_in < last && _out < end); - - /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ - len = bits >> 3; - _in -= len; - bits -= len << 3; - hold &= (1 << bits) - 1; - - /* update state and return */ - strm.next_in = _in; - strm.next_out = _out; - strm.avail_in = (_in < last ? 5 + (last - _in) : 5 - (_in - last)); - strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end)); - state.hold = hold; - state.bits = bits; - return; -}; diff --git a/base/app/novnc/vendor/pako/lib/zlib/inflate.js b/base/app/novnc/vendor/pako/lib/zlib/inflate.js deleted file mode 100644 index 1d2063b..0000000 --- a/base/app/novnc/vendor/pako/lib/zlib/inflate.js +++ /dev/null @@ -1,1527 +0,0 @@ -import * as utils from "../utils/common.js"; -import adler32 from "./adler32.js"; -import crc32 from "./crc32.js"; -import inflate_fast from "./inffast.js"; -import inflate_table from "./inftrees.js"; - -var CODES = 0; -var LENS = 1; -var DISTS = 2; - -/* Public constants ==========================================================*/ -/* ===========================================================================*/ - - -/* Allowed flush values; see deflate() and inflate() below for details */ -//export const Z_NO_FLUSH = 0; -//export const Z_PARTIAL_FLUSH = 1; -//export const Z_SYNC_FLUSH = 2; -//export const Z_FULL_FLUSH = 3; -export const Z_FINISH = 4; -export const Z_BLOCK = 5; -export const Z_TREES = 6; - - -/* Return codes for the compression/decompression functions. Negative values - * are errors, positive values are used for special but normal events. - */ -export const Z_OK = 0; -export const Z_STREAM_END = 1; -export const Z_NEED_DICT = 2; -//export const Z_ERRNO = -1; -export const Z_STREAM_ERROR = -2; -export const Z_DATA_ERROR = -3; -export const Z_MEM_ERROR = -4; -export const Z_BUF_ERROR = -5; -//export const Z_VERSION_ERROR = -6; - -/* The deflate compression method */ -export const Z_DEFLATED = 8; - - -/* STATES ====================================================================*/ -/* ===========================================================================*/ - - -var HEAD = 1; /* i: waiting for magic header */ -var FLAGS = 2; /* i: waiting for method and flags (gzip) */ -var TIME = 3; /* i: waiting for modification time (gzip) */ -var OS = 4; /* i: waiting for extra flags and operating system (gzip) */ -var EXLEN = 5; /* i: waiting for extra length (gzip) */ -var EXTRA = 6; /* i: waiting for extra bytes (gzip) */ -var NAME = 7; /* i: waiting for end of file name (gzip) */ -var COMMENT = 8; /* i: waiting for end of comment (gzip) */ -var HCRC = 9; /* i: waiting for header crc (gzip) */ -var DICTID = 10; /* i: waiting for dictionary check value */ -var DICT = 11; /* waiting for inflateSetDictionary() call */ -var TYPE = 12; /* i: waiting for type bits, including last-flag bit */ -var TYPEDO = 13; /* i: same, but skip check to exit inflate on new block */ -var STORED = 14; /* i: waiting for stored size (length and complement) */ -var COPY_ = 15; /* i/o: same as COPY below, but only first time in */ -var COPY = 16; /* i/o: waiting for input or output to copy stored block */ -var TABLE = 17; /* i: waiting for dynamic block table lengths */ -var LENLENS = 18; /* i: waiting for code length code lengths */ -var CODELENS = 19; /* i: waiting for length/lit and distance code lengths */ -var LEN_ = 20; /* i: same as LEN below, but only first time in */ -var LEN = 21; /* i: waiting for length/lit/eob code */ -var LENEXT = 22; /* i: waiting for length extra bits */ -var DIST = 23; /* i: waiting for distance code */ -var DISTEXT = 24; /* i: waiting for distance extra bits */ -var MATCH = 25; /* o: waiting for output space to copy string */ -var LIT = 26; /* o: waiting for output space to write literal */ -var CHECK = 27; /* i: waiting for 32-bit check value */ -var LENGTH = 28; /* i: waiting for 32-bit length (gzip) */ -var DONE = 29; /* finished check, done -- remain here until reset */ -var BAD = 30; /* got a data error -- remain here until reset */ -var MEM = 31; /* got an inflate() memory error -- remain here until reset */ -var SYNC = 32; /* looking for synchronization bytes to restart inflate() */ - -/* ===========================================================================*/ - - - -var ENOUGH_LENS = 852; -var ENOUGH_DISTS = 592; -//var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); - -var MAX_WBITS = 15; -/* 32K LZ77 window */ -var DEF_WBITS = MAX_WBITS; - - -function zswap32(q) { - return (((q >>> 24) & 0xff) + - ((q >>> 8) & 0xff00) + - ((q & 0xff00) << 8) + - ((q & 0xff) << 24)); -} - - -function InflateState() { - this.mode = 0; /* current inflate mode */ - this.last = false; /* true if processing last block */ - this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */ - this.havedict = false; /* true if dictionary provided */ - this.flags = 0; /* gzip header method and flags (0 if zlib) */ - this.dmax = 0; /* zlib header max distance (INFLATE_STRICT) */ - this.check = 0; /* protected copy of check value */ - this.total = 0; /* protected copy of output count */ - // TODO: may be {} - this.head = null; /* where to save gzip header information */ - - /* sliding window */ - this.wbits = 0; /* log base 2 of requested window size */ - this.wsize = 0; /* window size or zero if not using window */ - this.whave = 0; /* valid bytes in the window */ - this.wnext = 0; /* window write index */ - this.window = null; /* allocated sliding window, if needed */ - - /* bit accumulator */ - this.hold = 0; /* input bit accumulator */ - this.bits = 0; /* number of bits in "in" */ - - /* for string and stored block copying */ - this.length = 0; /* literal or length of data to copy */ - this.offset = 0; /* distance back to copy string from */ - - /* for table and code decoding */ - this.extra = 0; /* extra bits needed */ - - /* fixed and dynamic code tables */ - this.lencode = null; /* starting table for length/literal codes */ - this.distcode = null; /* starting table for distance codes */ - this.lenbits = 0; /* index bits for lencode */ - this.distbits = 0; /* index bits for distcode */ - - /* dynamic table building */ - this.ncode = 0; /* number of code length code lengths */ - this.nlen = 0; /* number of length code lengths */ - this.ndist = 0; /* number of distance code lengths */ - this.have = 0; /* number of code lengths in lens[] */ - this.next = null; /* next available space in codes[] */ - - this.lens = new utils.Buf16(320); /* temporary storage for code lengths */ - this.work = new utils.Buf16(288); /* work area for code table building */ - - /* - because we don't have pointers in js, we use lencode and distcode directly - as buffers so we don't need codes - */ - //this.codes = new utils.Buf32(ENOUGH); /* space for code tables */ - this.lendyn = null; /* dynamic table for length/literal codes (JS specific) */ - this.distdyn = null; /* dynamic table for distance codes (JS specific) */ - this.sane = 0; /* if false, allow invalid distance too far */ - this.back = 0; /* bits back of last unprocessed length/lit */ - this.was = 0; /* initial length of match */ -} - -function inflateResetKeep(strm) { - var state; - - if (!strm || !strm.state) { return Z_STREAM_ERROR; } - state = strm.state; - strm.total_in = strm.total_out = state.total = 0; - strm.msg = ''; /*Z_NULL*/ - if (state.wrap) { /* to support ill-conceived Java test suite */ - strm.adler = state.wrap & 1; - } - state.mode = HEAD; - state.last = 0; - state.havedict = 0; - state.dmax = 32768; - state.head = null/*Z_NULL*/; - state.hold = 0; - state.bits = 0; - //state.lencode = state.distcode = state.next = state.codes; - state.lencode = state.lendyn = new utils.Buf32(ENOUGH_LENS); - state.distcode = state.distdyn = new utils.Buf32(ENOUGH_DISTS); - - state.sane = 1; - state.back = -1; - //Tracev((stderr, "inflate: reset\n")); - return Z_OK; -} - -function inflateReset(strm) { - var state; - - if (!strm || !strm.state) { return Z_STREAM_ERROR; } - state = strm.state; - state.wsize = 0; - state.whave = 0; - state.wnext = 0; - return inflateResetKeep(strm); - -} - -function inflateReset2(strm, windowBits) { - var wrap; - var state; - - /* get the state */ - if (!strm || !strm.state) { return Z_STREAM_ERROR; } - state = strm.state; - - /* extract wrap request from windowBits parameter */ - if (windowBits < 0) { - wrap = 0; - windowBits = -windowBits; - } - else { - wrap = (windowBits >> 4) + 1; - if (windowBits < 48) { - windowBits &= 15; - } - } - - /* set number of window bits, free window if different */ - if (windowBits && (windowBits < 8 || windowBits > 15)) { - return Z_STREAM_ERROR; - } - if (state.window !== null && state.wbits !== windowBits) { - state.window = null; - } - - /* update state and reset the rest of it */ - state.wrap = wrap; - state.wbits = windowBits; - return inflateReset(strm); -} - -function inflateInit2(strm, windowBits) { - var ret; - var state; - - if (!strm) { return Z_STREAM_ERROR; } - //strm.msg = Z_NULL; /* in case we return an error */ - - state = new InflateState(); - - //if (state === Z_NULL) return Z_MEM_ERROR; - //Tracev((stderr, "inflate: allocated\n")); - strm.state = state; - state.window = null/*Z_NULL*/; - ret = inflateReset2(strm, windowBits); - if (ret !== Z_OK) { - strm.state = null/*Z_NULL*/; - } - return ret; -} - -function inflateInit(strm) { - return inflateInit2(strm, DEF_WBITS); -} - - -/* - Return state with length and distance decoding tables and index sizes set to - fixed code decoding. Normally this returns fixed tables from inffixed.h. - If BUILDFIXED is defined, then instead this routine builds the tables the - first time it's called, and returns those tables the first time and - thereafter. This reduces the size of the code by about 2K bytes, in - exchange for a little execution time. However, BUILDFIXED should not be - used for threaded applications, since the rewriting of the tables and virgin - may not be thread-safe. - */ -var virgin = true; - -var lenfix, distfix; // We have no pointers in JS, so keep tables separate - -function fixedtables(state) { - /* build fixed huffman tables if first call (may not be thread safe) */ - if (virgin) { - var sym; - - lenfix = new utils.Buf32(512); - distfix = new utils.Buf32(32); - - /* literal/length table */ - sym = 0; - while (sym < 144) { state.lens[sym++] = 8; } - while (sym < 256) { state.lens[sym++] = 9; } - while (sym < 280) { state.lens[sym++] = 7; } - while (sym < 288) { state.lens[sym++] = 8; } - - inflate_table(LENS, state.lens, 0, 288, lenfix, 0, state.work, { bits: 9 }); - - /* distance table */ - sym = 0; - while (sym < 32) { state.lens[sym++] = 5; } - - inflate_table(DISTS, state.lens, 0, 32, distfix, 0, state.work, { bits: 5 }); - - /* do this just once */ - virgin = false; - } - - state.lencode = lenfix; - state.lenbits = 9; - state.distcode = distfix; - state.distbits = 5; -} - - -/* - Update the window with the last wsize (normally 32K) bytes written before - returning. If window does not exist yet, create it. This is only called - when a window is already in use, or when output has been written during this - inflate call, but the end of the deflate stream has not been reached yet. - It is also called to create a window for dictionary data when a dictionary - is loaded. - - Providing output buffers larger than 32K to inflate() should provide a speed - advantage, since only the last 32K of output is copied to the sliding window - upon return from inflate(), and since all distances after the first 32K of - output will fall in the output data, making match copies simpler and faster. - The advantage may be dependent on the size of the processor's data caches. - */ -function updatewindow(strm, src, end, copy) { - var dist; - var state = strm.state; - - /* if it hasn't been done already, allocate space for the window */ - if (state.window === null) { - state.wsize = 1 << state.wbits; - state.wnext = 0; - state.whave = 0; - - state.window = new utils.Buf8(state.wsize); - } - - /* copy state->wsize or less output bytes into the circular window */ - if (copy >= state.wsize) { - utils.arraySet(state.window, src, end - state.wsize, state.wsize, 0); - state.wnext = 0; - state.whave = state.wsize; - } - else { - dist = state.wsize - state.wnext; - if (dist > copy) { - dist = copy; - } - //zmemcpy(state->window + state->wnext, end - copy, dist); - utils.arraySet(state.window, src, end - copy, dist, state.wnext); - copy -= dist; - if (copy) { - //zmemcpy(state->window, end - copy, copy); - utils.arraySet(state.window, src, end - copy, copy, 0); - state.wnext = copy; - state.whave = state.wsize; - } - else { - state.wnext += dist; - if (state.wnext === state.wsize) { state.wnext = 0; } - if (state.whave < state.wsize) { state.whave += dist; } - } - } - return 0; -} - -function inflate(strm, flush) { - var state; - var input, output; // input/output buffers - var next; /* next input INDEX */ - var put; /* next output INDEX */ - var have, left; /* available input and output */ - var hold; /* bit buffer */ - var bits; /* bits in bit buffer */ - var _in, _out; /* save starting available input and output */ - var copy; /* number of stored or match bytes to copy */ - var from; /* where to copy match bytes from */ - var from_source; - var here = 0; /* current decoding table entry */ - var here_bits, here_op, here_val; // paked "here" denormalized (JS specific) - //var last; /* parent table entry */ - var last_bits, last_op, last_val; // paked "last" denormalized (JS specific) - var len; /* length to copy for repeats, bits to drop */ - var ret; /* return code */ - var hbuf = new utils.Buf8(4); /* buffer for gzip header crc calculation */ - var opts; - - var n; // temporary var for NEED_BITS - - var order = /* permutation of code lengths */ - [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ]; - - - if (!strm || !strm.state || !strm.output || - (!strm.input && strm.avail_in !== 0)) { - return Z_STREAM_ERROR; - } - - state = strm.state; - if (state.mode === TYPE) { state.mode = TYPEDO; } /* skip check */ - - - //--- LOAD() --- - put = strm.next_out; - output = strm.output; - left = strm.avail_out; - next = strm.next_in; - input = strm.input; - have = strm.avail_in; - hold = state.hold; - bits = state.bits; - //--- - - _in = have; - _out = left; - ret = Z_OK; - - inf_leave: // goto emulation - for (;;) { - switch (state.mode) { - case HEAD: - if (state.wrap === 0) { - state.mode = TYPEDO; - break; - } - //=== NEEDBITS(16); - while (bits < 16) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - if ((state.wrap & 2) && hold === 0x8b1f) { /* gzip header */ - state.check = 0/*crc32(0L, Z_NULL, 0)*/; - //=== CRC2(state.check, hold); - hbuf[0] = hold & 0xff; - hbuf[1] = (hold >>> 8) & 0xff; - state.check = crc32(state.check, hbuf, 2, 0); - //===// - - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - state.mode = FLAGS; - break; - } - state.flags = 0; /* expect zlib header */ - if (state.head) { - state.head.done = false; - } - if (!(state.wrap & 1) || /* check if zlib header allowed */ - (((hold & 0xff)/*BITS(8)*/ << 8) + (hold >> 8)) % 31) { - strm.msg = 'incorrect header check'; - state.mode = BAD; - break; - } - if ((hold & 0x0f)/*BITS(4)*/ !== Z_DEFLATED) { - strm.msg = 'unknown compression method'; - state.mode = BAD; - break; - } - //--- DROPBITS(4) ---// - hold >>>= 4; - bits -= 4; - //---// - len = (hold & 0x0f)/*BITS(4)*/ + 8; - if (state.wbits === 0) { - state.wbits = len; - } - else if (len > state.wbits) { - strm.msg = 'invalid window size'; - state.mode = BAD; - break; - } - state.dmax = 1 << len; - //Tracev((stderr, "inflate: zlib header ok\n")); - strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/; - state.mode = hold & 0x200 ? DICTID : TYPE; - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - break; - case FLAGS: - //=== NEEDBITS(16); */ - while (bits < 16) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - state.flags = hold; - if ((state.flags & 0xff) !== Z_DEFLATED) { - strm.msg = 'unknown compression method'; - state.mode = BAD; - break; - } - if (state.flags & 0xe000) { - strm.msg = 'unknown header flags set'; - state.mode = BAD; - break; - } - if (state.head) { - state.head.text = ((hold >> 8) & 1); - } - if (state.flags & 0x0200) { - //=== CRC2(state.check, hold); - hbuf[0] = hold & 0xff; - hbuf[1] = (hold >>> 8) & 0xff; - state.check = crc32(state.check, hbuf, 2, 0); - //===// - } - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - state.mode = TIME; - /* falls through */ - case TIME: - //=== NEEDBITS(32); */ - while (bits < 32) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - if (state.head) { - state.head.time = hold; - } - if (state.flags & 0x0200) { - //=== CRC4(state.check, hold) - hbuf[0] = hold & 0xff; - hbuf[1] = (hold >>> 8) & 0xff; - hbuf[2] = (hold >>> 16) & 0xff; - hbuf[3] = (hold >>> 24) & 0xff; - state.check = crc32(state.check, hbuf, 4, 0); - //=== - } - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - state.mode = OS; - /* falls through */ - case OS: - //=== NEEDBITS(16); */ - while (bits < 16) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - if (state.head) { - state.head.xflags = (hold & 0xff); - state.head.os = (hold >> 8); - } - if (state.flags & 0x0200) { - //=== CRC2(state.check, hold); - hbuf[0] = hold & 0xff; - hbuf[1] = (hold >>> 8) & 0xff; - state.check = crc32(state.check, hbuf, 2, 0); - //===// - } - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - state.mode = EXLEN; - /* falls through */ - case EXLEN: - if (state.flags & 0x0400) { - //=== NEEDBITS(16); */ - while (bits < 16) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - state.length = hold; - if (state.head) { - state.head.extra_len = hold; - } - if (state.flags & 0x0200) { - //=== CRC2(state.check, hold); - hbuf[0] = hold & 0xff; - hbuf[1] = (hold >>> 8) & 0xff; - state.check = crc32(state.check, hbuf, 2, 0); - //===// - } - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - } - else if (state.head) { - state.head.extra = null/*Z_NULL*/; - } - state.mode = EXTRA; - /* falls through */ - case EXTRA: - if (state.flags & 0x0400) { - copy = state.length; - if (copy > have) { copy = have; } - if (copy) { - if (state.head) { - len = state.head.extra_len - state.length; - if (!state.head.extra) { - // Use untyped array for more conveniend processing later - state.head.extra = new Array(state.head.extra_len); - } - utils.arraySet( - state.head.extra, - input, - next, - // extra field is limited to 65536 bytes - // - no need for additional size check - copy, - /*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/ - len - ); - //zmemcpy(state.head.extra + len, next, - // len + copy > state.head.extra_max ? - // state.head.extra_max - len : copy); - } - if (state.flags & 0x0200) { - state.check = crc32(state.check, input, copy, next); - } - have -= copy; - next += copy; - state.length -= copy; - } - if (state.length) { break inf_leave; } - } - state.length = 0; - state.mode = NAME; - /* falls through */ - case NAME: - if (state.flags & 0x0800) { - if (have === 0) { break inf_leave; } - copy = 0; - do { - // TODO: 2 or 1 bytes? - len = input[next + copy++]; - /* use constant limit because in js we should not preallocate memory */ - if (state.head && len && - (state.length < 65536 /*state.head.name_max*/)) { - state.head.name += String.fromCharCode(len); - } - } while (len && copy < have); - - if (state.flags & 0x0200) { - state.check = crc32(state.check, input, copy, next); - } - have -= copy; - next += copy; - if (len) { break inf_leave; } - } - else if (state.head) { - state.head.name = null; - } - state.length = 0; - state.mode = COMMENT; - /* falls through */ - case COMMENT: - if (state.flags & 0x1000) { - if (have === 0) { break inf_leave; } - copy = 0; - do { - len = input[next + copy++]; - /* use constant limit because in js we should not preallocate memory */ - if (state.head && len && - (state.length < 65536 /*state.head.comm_max*/)) { - state.head.comment += String.fromCharCode(len); - } - } while (len && copy < have); - if (state.flags & 0x0200) { - state.check = crc32(state.check, input, copy, next); - } - have -= copy; - next += copy; - if (len) { break inf_leave; } - } - else if (state.head) { - state.head.comment = null; - } - state.mode = HCRC; - /* falls through */ - case HCRC: - if (state.flags & 0x0200) { - //=== NEEDBITS(16); */ - while (bits < 16) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - if (hold !== (state.check & 0xffff)) { - strm.msg = 'header crc mismatch'; - state.mode = BAD; - break; - } - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - } - if (state.head) { - state.head.hcrc = ((state.flags >> 9) & 1); - state.head.done = true; - } - strm.adler = state.check = 0; - state.mode = TYPE; - break; - case DICTID: - //=== NEEDBITS(32); */ - while (bits < 32) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - strm.adler = state.check = zswap32(hold); - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - state.mode = DICT; - /* falls through */ - case DICT: - if (state.havedict === 0) { - //--- RESTORE() --- - strm.next_out = put; - strm.avail_out = left; - strm.next_in = next; - strm.avail_in = have; - state.hold = hold; - state.bits = bits; - //--- - return Z_NEED_DICT; - } - strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/; - state.mode = TYPE; - /* falls through */ - case TYPE: - if (flush === Z_BLOCK || flush === Z_TREES) { break inf_leave; } - /* falls through */ - case TYPEDO: - if (state.last) { - //--- BYTEBITS() ---// - hold >>>= bits & 7; - bits -= bits & 7; - //---// - state.mode = CHECK; - break; - } - //=== NEEDBITS(3); */ - while (bits < 3) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - state.last = (hold & 0x01)/*BITS(1)*/; - //--- DROPBITS(1) ---// - hold >>>= 1; - bits -= 1; - //---// - - switch ((hold & 0x03)/*BITS(2)*/) { - case 0: /* stored block */ - //Tracev((stderr, "inflate: stored block%s\n", - // state.last ? " (last)" : "")); - state.mode = STORED; - break; - case 1: /* fixed block */ - fixedtables(state); - //Tracev((stderr, "inflate: fixed codes block%s\n", - // state.last ? " (last)" : "")); - state.mode = LEN_; /* decode codes */ - if (flush === Z_TREES) { - //--- DROPBITS(2) ---// - hold >>>= 2; - bits -= 2; - //---// - break inf_leave; - } - break; - case 2: /* dynamic block */ - //Tracev((stderr, "inflate: dynamic codes block%s\n", - // state.last ? " (last)" : "")); - state.mode = TABLE; - break; - case 3: - strm.msg = 'invalid block type'; - state.mode = BAD; - } - //--- DROPBITS(2) ---// - hold >>>= 2; - bits -= 2; - //---// - break; - case STORED: - //--- BYTEBITS() ---// /* go to byte boundary */ - hold >>>= bits & 7; - bits -= bits & 7; - //---// - //=== NEEDBITS(32); */ - while (bits < 32) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - if ((hold & 0xffff) !== ((hold >>> 16) ^ 0xffff)) { - strm.msg = 'invalid stored block lengths'; - state.mode = BAD; - break; - } - state.length = hold & 0xffff; - //Tracev((stderr, "inflate: stored length %u\n", - // state.length)); - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - state.mode = COPY_; - if (flush === Z_TREES) { break inf_leave; } - /* falls through */ - case COPY_: - state.mode = COPY; - /* falls through */ - case COPY: - copy = state.length; - if (copy) { - if (copy > have) { copy = have; } - if (copy > left) { copy = left; } - if (copy === 0) { break inf_leave; } - //--- zmemcpy(put, next, copy); --- - utils.arraySet(output, input, next, copy, put); - //---// - have -= copy; - next += copy; - left -= copy; - put += copy; - state.length -= copy; - break; - } - //Tracev((stderr, "inflate: stored end\n")); - state.mode = TYPE; - break; - case TABLE: - //=== NEEDBITS(14); */ - while (bits < 14) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - state.nlen = (hold & 0x1f)/*BITS(5)*/ + 257; - //--- DROPBITS(5) ---// - hold >>>= 5; - bits -= 5; - //---// - state.ndist = (hold & 0x1f)/*BITS(5)*/ + 1; - //--- DROPBITS(5) ---// - hold >>>= 5; - bits -= 5; - //---// - state.ncode = (hold & 0x0f)/*BITS(4)*/ + 4; - //--- DROPBITS(4) ---// - hold >>>= 4; - bits -= 4; - //---// -//#ifndef PKZIP_BUG_WORKAROUND - if (state.nlen > 286 || state.ndist > 30) { - strm.msg = 'too many length or distance symbols'; - state.mode = BAD; - break; - } -//#endif - //Tracev((stderr, "inflate: table sizes ok\n")); - state.have = 0; - state.mode = LENLENS; - /* falls through */ - case LENLENS: - while (state.have < state.ncode) { - //=== NEEDBITS(3); - while (bits < 3) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - state.lens[order[state.have++]] = (hold & 0x07);//BITS(3); - //--- DROPBITS(3) ---// - hold >>>= 3; - bits -= 3; - //---// - } - while (state.have < 19) { - state.lens[order[state.have++]] = 0; - } - // We have separate tables & no pointers. 2 commented lines below not needed. - //state.next = state.codes; - //state.lencode = state.next; - // Switch to use dynamic table - state.lencode = state.lendyn; - state.lenbits = 7; - - opts = { bits: state.lenbits }; - ret = inflate_table(CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts); - state.lenbits = opts.bits; - - if (ret) { - strm.msg = 'invalid code lengths set'; - state.mode = BAD; - break; - } - //Tracev((stderr, "inflate: code lengths ok\n")); - state.have = 0; - state.mode = CODELENS; - /* falls through */ - case CODELENS: - while (state.have < state.nlen + state.ndist) { - for (;;) { - here = state.lencode[hold & ((1 << state.lenbits) - 1)];/*BITS(state.lenbits)*/ - here_bits = here >>> 24; - here_op = (here >>> 16) & 0xff; - here_val = here & 0xffff; - - if ((here_bits) <= bits) { break; } - //--- PULLBYTE() ---// - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - //---// - } - if (here_val < 16) { - //--- DROPBITS(here.bits) ---// - hold >>>= here_bits; - bits -= here_bits; - //---// - state.lens[state.have++] = here_val; - } - else { - if (here_val === 16) { - //=== NEEDBITS(here.bits + 2); - n = here_bits + 2; - while (bits < n) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - //--- DROPBITS(here.bits) ---// - hold >>>= here_bits; - bits -= here_bits; - //---// - if (state.have === 0) { - strm.msg = 'invalid bit length repeat'; - state.mode = BAD; - break; - } - len = state.lens[state.have - 1]; - copy = 3 + (hold & 0x03);//BITS(2); - //--- DROPBITS(2) ---// - hold >>>= 2; - bits -= 2; - //---// - } - else if (here_val === 17) { - //=== NEEDBITS(here.bits + 3); - n = here_bits + 3; - while (bits < n) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - //--- DROPBITS(here.bits) ---// - hold >>>= here_bits; - bits -= here_bits; - //---// - len = 0; - copy = 3 + (hold & 0x07);//BITS(3); - //--- DROPBITS(3) ---// - hold >>>= 3; - bits -= 3; - //---// - } - else { - //=== NEEDBITS(here.bits + 7); - n = here_bits + 7; - while (bits < n) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - //--- DROPBITS(here.bits) ---// - hold >>>= here_bits; - bits -= here_bits; - //---// - len = 0; - copy = 11 + (hold & 0x7f);//BITS(7); - //--- DROPBITS(7) ---// - hold >>>= 7; - bits -= 7; - //---// - } - if (state.have + copy > state.nlen + state.ndist) { - strm.msg = 'invalid bit length repeat'; - state.mode = BAD; - break; - } - while (copy--) { - state.lens[state.have++] = len; - } - } - } - - /* handle error breaks in while */ - if (state.mode === BAD) { break; } - - /* check for end-of-block code (better have one) */ - if (state.lens[256] === 0) { - strm.msg = 'invalid code -- missing end-of-block'; - state.mode = BAD; - break; - } - - /* build code tables -- note: do not change the lenbits or distbits - values here (9 and 6) without reading the comments in inftrees.h - concerning the ENOUGH constants, which depend on those values */ - state.lenbits = 9; - - opts = { bits: state.lenbits }; - ret = inflate_table(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts); - // We have separate tables & no pointers. 2 commented lines below not needed. - // state.next_index = opts.table_index; - state.lenbits = opts.bits; - // state.lencode = state.next; - - if (ret) { - strm.msg = 'invalid literal/lengths set'; - state.mode = BAD; - break; - } - - state.distbits = 6; - //state.distcode.copy(state.codes); - // Switch to use dynamic table - state.distcode = state.distdyn; - opts = { bits: state.distbits }; - ret = inflate_table(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts); - // We have separate tables & no pointers. 2 commented lines below not needed. - // state.next_index = opts.table_index; - state.distbits = opts.bits; - // state.distcode = state.next; - - if (ret) { - strm.msg = 'invalid distances set'; - state.mode = BAD; - break; - } - //Tracev((stderr, 'inflate: codes ok\n')); - state.mode = LEN_; - if (flush === Z_TREES) { break inf_leave; } - /* falls through */ - case LEN_: - state.mode = LEN; - /* falls through */ - case LEN: - if (have >= 6 && left >= 258) { - //--- RESTORE() --- - strm.next_out = put; - strm.avail_out = left; - strm.next_in = next; - strm.avail_in = have; - state.hold = hold; - state.bits = bits; - //--- - inflate_fast(strm, _out); - //--- LOAD() --- - put = strm.next_out; - output = strm.output; - left = strm.avail_out; - next = strm.next_in; - input = strm.input; - have = strm.avail_in; - hold = state.hold; - bits = state.bits; - //--- - - if (state.mode === TYPE) { - state.back = -1; - } - break; - } - state.back = 0; - for (;;) { - here = state.lencode[hold & ((1 << state.lenbits) - 1)]; /*BITS(state.lenbits)*/ - here_bits = here >>> 24; - here_op = (here >>> 16) & 0xff; - here_val = here & 0xffff; - - if (here_bits <= bits) { break; } - //--- PULLBYTE() ---// - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - //---// - } - if (here_op && (here_op & 0xf0) === 0) { - last_bits = here_bits; - last_op = here_op; - last_val = here_val; - for (;;) { - here = state.lencode[last_val + - ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)]; - here_bits = here >>> 24; - here_op = (here >>> 16) & 0xff; - here_val = here & 0xffff; - - if ((last_bits + here_bits) <= bits) { break; } - //--- PULLBYTE() ---// - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - //---// - } - //--- DROPBITS(last.bits) ---// - hold >>>= last_bits; - bits -= last_bits; - //---// - state.back += last_bits; - } - //--- DROPBITS(here.bits) ---// - hold >>>= here_bits; - bits -= here_bits; - //---// - state.back += here_bits; - state.length = here_val; - if (here_op === 0) { - //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? - // "inflate: literal '%c'\n" : - // "inflate: literal 0x%02x\n", here.val)); - state.mode = LIT; - break; - } - if (here_op & 32) { - //Tracevv((stderr, "inflate: end of block\n")); - state.back = -1; - state.mode = TYPE; - break; - } - if (here_op & 64) { - strm.msg = 'invalid literal/length code'; - state.mode = BAD; - break; - } - state.extra = here_op & 15; - state.mode = LENEXT; - /* falls through */ - case LENEXT: - if (state.extra) { - //=== NEEDBITS(state.extra); - n = state.extra; - while (bits < n) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - state.length += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/; - //--- DROPBITS(state.extra) ---// - hold >>>= state.extra; - bits -= state.extra; - //---// - state.back += state.extra; - } - //Tracevv((stderr, "inflate: length %u\n", state.length)); - state.was = state.length; - state.mode = DIST; - /* falls through */ - case DIST: - for (;;) { - here = state.distcode[hold & ((1 << state.distbits) - 1)];/*BITS(state.distbits)*/ - here_bits = here >>> 24; - here_op = (here >>> 16) & 0xff; - here_val = here & 0xffff; - - if ((here_bits) <= bits) { break; } - //--- PULLBYTE() ---// - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - //---// - } - if ((here_op & 0xf0) === 0) { - last_bits = here_bits; - last_op = here_op; - last_val = here_val; - for (;;) { - here = state.distcode[last_val + - ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)]; - here_bits = here >>> 24; - here_op = (here >>> 16) & 0xff; - here_val = here & 0xffff; - - if ((last_bits + here_bits) <= bits) { break; } - //--- PULLBYTE() ---// - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - //---// - } - //--- DROPBITS(last.bits) ---// - hold >>>= last_bits; - bits -= last_bits; - //---// - state.back += last_bits; - } - //--- DROPBITS(here.bits) ---// - hold >>>= here_bits; - bits -= here_bits; - //---// - state.back += here_bits; - if (here_op & 64) { - strm.msg = 'invalid distance code'; - state.mode = BAD; - break; - } - state.offset = here_val; - state.extra = (here_op) & 15; - state.mode = DISTEXT; - /* falls through */ - case DISTEXT: - if (state.extra) { - //=== NEEDBITS(state.extra); - n = state.extra; - while (bits < n) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - state.offset += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/; - //--- DROPBITS(state.extra) ---// - hold >>>= state.extra; - bits -= state.extra; - //---// - state.back += state.extra; - } -//#ifdef INFLATE_STRICT - if (state.offset > state.dmax) { - strm.msg = 'invalid distance too far back'; - state.mode = BAD; - break; - } -//#endif - //Tracevv((stderr, "inflate: distance %u\n", state.offset)); - state.mode = MATCH; - /* falls through */ - case MATCH: - if (left === 0) { break inf_leave; } - copy = _out - left; - if (state.offset > copy) { /* copy from window */ - copy = state.offset - copy; - if (copy > state.whave) { - if (state.sane) { - strm.msg = 'invalid distance too far back'; - state.mode = BAD; - break; - } -// (!) This block is disabled in zlib defailts, -// don't enable it for binary compatibility -//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR -// Trace((stderr, "inflate.c too far\n")); -// copy -= state.whave; -// if (copy > state.length) { copy = state.length; } -// if (copy > left) { copy = left; } -// left -= copy; -// state.length -= copy; -// do { -// output[put++] = 0; -// } while (--copy); -// if (state.length === 0) { state.mode = LEN; } -// break; -//#endif - } - if (copy > state.wnext) { - copy -= state.wnext; - from = state.wsize - copy; - } - else { - from = state.wnext - copy; - } - if (copy > state.length) { copy = state.length; } - from_source = state.window; - } - else { /* copy from output */ - from_source = output; - from = put - state.offset; - copy = state.length; - } - if (copy > left) { copy = left; } - left -= copy; - state.length -= copy; - do { - output[put++] = from_source[from++]; - } while (--copy); - if (state.length === 0) { state.mode = LEN; } - break; - case LIT: - if (left === 0) { break inf_leave; } - output[put++] = state.length; - left--; - state.mode = LEN; - break; - case CHECK: - if (state.wrap) { - //=== NEEDBITS(32); - while (bits < 32) { - if (have === 0) { break inf_leave; } - have--; - // Use '|' insdead of '+' to make sure that result is signed - hold |= input[next++] << bits; - bits += 8; - } - //===// - _out -= left; - strm.total_out += _out; - state.total += _out; - if (_out) { - strm.adler = state.check = - /*UPDATE(state.check, put - _out, _out);*/ - (state.flags ? crc32(state.check, output, _out, put - _out) : adler32(state.check, output, _out, put - _out)); - - } - _out = left; - // NB: crc32 stored as signed 32-bit int, zswap32 returns signed too - if ((state.flags ? hold : zswap32(hold)) !== state.check) { - strm.msg = 'incorrect data check'; - state.mode = BAD; - break; - } - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - //Tracev((stderr, "inflate: check matches trailer\n")); - } - state.mode = LENGTH; - /* falls through */ - case LENGTH: - if (state.wrap && state.flags) { - //=== NEEDBITS(32); - while (bits < 32) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - if (hold !== (state.total & 0xffffffff)) { - strm.msg = 'incorrect length check'; - state.mode = BAD; - break; - } - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - //Tracev((stderr, "inflate: length matches trailer\n")); - } - state.mode = DONE; - /* falls through */ - case DONE: - ret = Z_STREAM_END; - break inf_leave; - case BAD: - ret = Z_DATA_ERROR; - break inf_leave; - case MEM: - return Z_MEM_ERROR; - case SYNC: - /* falls through */ - default: - return Z_STREAM_ERROR; - } - } - - // inf_leave <- here is real place for "goto inf_leave", emulated via "break inf_leave" - - /* - Return from inflate(), updating the total counts and the check value. - If there was no progress during the inflate() call, return a buffer - error. Call updatewindow() to create and/or update the window state. - Note: a memory error from inflate() is non-recoverable. - */ - - //--- RESTORE() --- - strm.next_out = put; - strm.avail_out = left; - strm.next_in = next; - strm.avail_in = have; - state.hold = hold; - state.bits = bits; - //--- - - if (state.wsize || (_out !== strm.avail_out && state.mode < BAD && - (state.mode < CHECK || flush !== Z_FINISH))) { - if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)) { - state.mode = MEM; - return Z_MEM_ERROR; - } - } - _in -= strm.avail_in; - _out -= strm.avail_out; - strm.total_in += _in; - strm.total_out += _out; - state.total += _out; - if (state.wrap && _out) { - strm.adler = state.check = /*UPDATE(state.check, strm.next_out - _out, _out);*/ - (state.flags ? crc32(state.check, output, _out, strm.next_out - _out) : adler32(state.check, output, _out, strm.next_out - _out)); - } - strm.data_type = state.bits + (state.last ? 64 : 0) + - (state.mode === TYPE ? 128 : 0) + - (state.mode === LEN_ || state.mode === COPY_ ? 256 : 0); - if (((_in === 0 && _out === 0) || flush === Z_FINISH) && ret === Z_OK) { - ret = Z_BUF_ERROR; - } - return ret; -} - -function inflateEnd(strm) { - - if (!strm || !strm.state /*|| strm->zfree == (free_func)0*/) { - return Z_STREAM_ERROR; - } - - var state = strm.state; - if (state.window) { - state.window = null; - } - strm.state = null; - return Z_OK; -} - -function inflateGetHeader(strm, head) { - var state; - - /* check state */ - if (!strm || !strm.state) { return Z_STREAM_ERROR; } - state = strm.state; - if ((state.wrap & 2) === 0) { return Z_STREAM_ERROR; } - - /* save header structure */ - state.head = head; - head.done = false; - return Z_OK; -} - -function inflateSetDictionary(strm, dictionary) { - var dictLength = dictionary.length; - - var state; - var dictid; - var ret; - - /* check state */ - if (!strm /* == Z_NULL */ || !strm.state /* == Z_NULL */) { return Z_STREAM_ERROR; } - state = strm.state; - - if (state.wrap !== 0 && state.mode !== DICT) { - return Z_STREAM_ERROR; - } - - /* check for correct dictionary identifier */ - if (state.mode === DICT) { - dictid = 1; /* adler32(0, null, 0)*/ - /* dictid = adler32(dictid, dictionary, dictLength); */ - dictid = adler32(dictid, dictionary, dictLength, 0); - if (dictid !== state.check) { - return Z_DATA_ERROR; - } - } - /* copy dictionary to window using updatewindow(), which will amend the - existing dictionary if appropriate */ - ret = updatewindow(strm, dictionary, dictLength, dictLength); - if (ret) { - state.mode = MEM; - return Z_MEM_ERROR; - } - state.havedict = 1; - // Tracev((stderr, "inflate: dictionary set\n")); - return Z_OK; -} - -export { inflateReset, inflateReset2, inflateResetKeep, inflateInit, inflateInit2, inflate, inflateEnd, inflateGetHeader, inflateSetDictionary }; -export var inflateInfo = 'pako inflate (from Nodeca project)'; - -/* Not implemented -exports.inflateCopy = inflateCopy; -exports.inflateGetDictionary = inflateGetDictionary; -exports.inflateMark = inflateMark; -exports.inflatePrime = inflatePrime; -exports.inflateSync = inflateSync; -exports.inflateSyncPoint = inflateSyncPoint; -exports.inflateUndermine = inflateUndermine; -*/ diff --git a/base/app/novnc/vendor/pako/lib/zlib/inftrees.js b/base/app/novnc/vendor/pako/lib/zlib/inftrees.js deleted file mode 100644 index 78b7c9e..0000000 --- a/base/app/novnc/vendor/pako/lib/zlib/inftrees.js +++ /dev/null @@ -1,322 +0,0 @@ -import * as utils from "../utils/common.js"; - -var MAXBITS = 15; -var ENOUGH_LENS = 852; -var ENOUGH_DISTS = 592; -//var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); - -var CODES = 0; -var LENS = 1; -var DISTS = 2; - -var lbase = [ /* Length codes 257..285 base */ - 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, - 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 -]; - -var lext = [ /* Length codes 257..285 extra */ - 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, - 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78 -]; - -var dbase = [ /* Distance codes 0..29 base */ - 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, - 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, - 8193, 12289, 16385, 24577, 0, 0 -]; - -var dext = [ /* Distance codes 0..29 extra */ - 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, - 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, - 28, 28, 29, 29, 64, 64 -]; - -export default function inflate_table(type, lens, lens_index, codes, table, table_index, work, opts) -{ - var bits = opts.bits; - //here = opts.here; /* table entry for duplication */ - - var len = 0; /* a code's length in bits */ - var sym = 0; /* index of code symbols */ - var min = 0, max = 0; /* minimum and maximum code lengths */ - var root = 0; /* number of index bits for root table */ - var curr = 0; /* number of index bits for current table */ - var drop = 0; /* code bits to drop for sub-table */ - var left = 0; /* number of prefix codes available */ - var used = 0; /* code entries in table used */ - var huff = 0; /* Huffman code */ - var incr; /* for incrementing code, index */ - var fill; /* index for replicating entries */ - var low; /* low bits for current root entry */ - var mask; /* mask for low root bits */ - var next; /* next available space in table */ - var base = null; /* base value table to use */ - var base_index = 0; -// var shoextra; /* extra bits table to use */ - var end; /* use base and extra for symbol > end */ - var count = new utils.Buf16(MAXBITS + 1); //[MAXBITS+1]; /* number of codes of each length */ - var offs = new utils.Buf16(MAXBITS + 1); //[MAXBITS+1]; /* offsets in table for each length */ - var extra = null; - var extra_index = 0; - - var here_bits, here_op, here_val; - - /* - Process a set of code lengths to create a canonical Huffman code. The - code lengths are lens[0..codes-1]. Each length corresponds to the - symbols 0..codes-1. The Huffman code is generated by first sorting the - symbols by length from short to long, and retaining the symbol order - for codes with equal lengths. Then the code starts with all zero bits - for the first code of the shortest length, and the codes are integer - increments for the same length, and zeros are appended as the length - increases. For the deflate format, these bits are stored backwards - from their more natural integer increment ordering, and so when the - decoding tables are built in the large loop below, the integer codes - are incremented backwards. - - This routine assumes, but does not check, that all of the entries in - lens[] are in the range 0..MAXBITS. The caller must assure this. - 1..MAXBITS is interpreted as that code length. zero means that that - symbol does not occur in this code. - - The codes are sorted by computing a count of codes for each length, - creating from that a table of starting indices for each length in the - sorted table, and then entering the symbols in order in the sorted - table. The sorted table is work[], with that space being provided by - the caller. - - The length counts are used for other purposes as well, i.e. finding - the minimum and maximum length codes, determining if there are any - codes at all, checking for a valid set of lengths, and looking ahead - at length counts to determine sub-table sizes when building the - decoding tables. - */ - - /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ - for (len = 0; len <= MAXBITS; len++) { - count[len] = 0; - } - for (sym = 0; sym < codes; sym++) { - count[lens[lens_index + sym]]++; - } - - /* bound code lengths, force root to be within code lengths */ - root = bits; - for (max = MAXBITS; max >= 1; max--) { - if (count[max] !== 0) { break; } - } - if (root > max) { - root = max; - } - if (max === 0) { /* no symbols to code at all */ - //table.op[opts.table_index] = 64; //here.op = (var char)64; /* invalid code marker */ - //table.bits[opts.table_index] = 1; //here.bits = (var char)1; - //table.val[opts.table_index++] = 0; //here.val = (var short)0; - table[table_index++] = (1 << 24) | (64 << 16) | 0; - - - //table.op[opts.table_index] = 64; - //table.bits[opts.table_index] = 1; - //table.val[opts.table_index++] = 0; - table[table_index++] = (1 << 24) | (64 << 16) | 0; - - opts.bits = 1; - return 0; /* no symbols, but wait for decoding to report error */ - } - for (min = 1; min < max; min++) { - if (count[min] !== 0) { break; } - } - if (root < min) { - root = min; - } - - /* check for an over-subscribed or incomplete set of lengths */ - left = 1; - for (len = 1; len <= MAXBITS; len++) { - left <<= 1; - left -= count[len]; - if (left < 0) { - return -1; - } /* over-subscribed */ - } - if (left > 0 && (type === CODES || max !== 1)) { - return -1; /* incomplete set */ - } - - /* generate offsets into symbol table for each length for sorting */ - offs[1] = 0; - for (len = 1; len < MAXBITS; len++) { - offs[len + 1] = offs[len] + count[len]; - } - - /* sort symbols by length, by symbol order within each length */ - for (sym = 0; sym < codes; sym++) { - if (lens[lens_index + sym] !== 0) { - work[offs[lens[lens_index + sym]]++] = sym; - } - } - - /* - Create and fill in decoding tables. In this loop, the table being - filled is at next and has curr index bits. The code being used is huff - with length len. That code is converted to an index by dropping drop - bits off of the bottom. For codes where len is less than drop + curr, - those top drop + curr - len bits are incremented through all values to - fill the table with replicated entries. - - root is the number of index bits for the root table. When len exceeds - root, sub-tables are created pointed to by the root entry with an index - of the low root bits of huff. This is saved in low to check for when a - new sub-table should be started. drop is zero when the root table is - being filled, and drop is root when sub-tables are being filled. - - When a new sub-table is needed, it is necessary to look ahead in the - code lengths to determine what size sub-table is needed. The length - counts are used for this, and so count[] is decremented as codes are - entered in the tables. - - used keeps track of how many table entries have been allocated from the - provided *table space. It is checked for LENS and DIST tables against - the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in - the initial root table size constants. See the comments in inftrees.h - for more information. - - sym increments through all symbols, and the loop terminates when - all codes of length max, i.e. all codes, have been processed. This - routine permits incomplete codes, so another loop after this one fills - in the rest of the decoding tables with invalid code markers. - */ - - /* set up for code type */ - // poor man optimization - use if-else instead of switch, - // to avoid deopts in old v8 - if (type === CODES) { - base = extra = work; /* dummy value--not used */ - end = 19; - - } else if (type === LENS) { - base = lbase; - base_index -= 257; - extra = lext; - extra_index -= 257; - end = 256; - - } else { /* DISTS */ - base = dbase; - extra = dext; - end = -1; - } - - /* initialize opts for loop */ - huff = 0; /* starting code */ - sym = 0; /* starting code symbol */ - len = min; /* starting code length */ - next = table_index; /* current table to fill in */ - curr = root; /* current table index bits */ - drop = 0; /* current bits to drop from code for index */ - low = -1; /* trigger new sub-table when len > root */ - used = 1 << root; /* use root table entries */ - mask = used - 1; /* mask for comparing low */ - - /* check available table space */ - if ((type === LENS && used > ENOUGH_LENS) || - (type === DISTS && used > ENOUGH_DISTS)) { - return 1; - } - - /* process all codes and make table entries */ - for (;;) { - /* create table entry */ - here_bits = len - drop; - if (work[sym] < end) { - here_op = 0; - here_val = work[sym]; - } - else if (work[sym] > end) { - here_op = extra[extra_index + work[sym]]; - here_val = base[base_index + work[sym]]; - } - else { - here_op = 32 + 64; /* end of block */ - here_val = 0; - } - - /* replicate for those indices with low len bits equal to huff */ - incr = 1 << (len - drop); - fill = 1 << curr; - min = fill; /* save offset to next table */ - do { - fill -= incr; - table[next + (huff >> drop) + fill] = (here_bits << 24) | (here_op << 16) | here_val |0; - } while (fill !== 0); - - /* backwards increment the len-bit code huff */ - incr = 1 << (len - 1); - while (huff & incr) { - incr >>= 1; - } - if (incr !== 0) { - huff &= incr - 1; - huff += incr; - } else { - huff = 0; - } - - /* go to next symbol, update count, len */ - sym++; - if (--count[len] === 0) { - if (len === max) { break; } - len = lens[lens_index + work[sym]]; - } - - /* create new sub-table if needed */ - if (len > root && (huff & mask) !== low) { - /* if first time, transition to sub-tables */ - if (drop === 0) { - drop = root; - } - - /* increment past last table */ - next += min; /* here min is 1 << curr */ - - /* determine length of next table */ - curr = len - drop; - left = 1 << curr; - while (curr + drop < max) { - left -= count[curr + drop]; - if (left <= 0) { break; } - curr++; - left <<= 1; - } - - /* check for enough space */ - used += 1 << curr; - if ((type === LENS && used > ENOUGH_LENS) || - (type === DISTS && used > ENOUGH_DISTS)) { - return 1; - } - - /* point entry in root table to sub-table */ - low = huff & mask; - /*table.op[low] = curr; - table.bits[low] = root; - table.val[low] = next - opts.table_index;*/ - table[low] = (root << 24) | (curr << 16) | (next - table_index) |0; - } - } - - /* fill in remaining table entry if code is incomplete (guaranteed to have - at most one remaining entry, since if the code is incomplete, the - maximum code length that was allowed to get this far is one bit) */ - if (huff !== 0) { - //table.op[next + huff] = 64; /* invalid code marker */ - //table.bits[next + huff] = len - drop; - //table.val[next + huff] = 0; - table[next + huff] = ((len - drop) << 24) | (64 << 16) |0; - } - - /* set return parameters */ - //opts.table_index += used; - opts.bits = root; - return 0; -}; diff --git a/base/app/novnc/vendor/pako/lib/zlib/messages.js b/base/app/novnc/vendor/pako/lib/zlib/messages.js deleted file mode 100644 index f95cb70..0000000 --- a/base/app/novnc/vendor/pako/lib/zlib/messages.js +++ /dev/null @@ -1,11 +0,0 @@ -export default { - 2: 'need dictionary', /* Z_NEED_DICT 2 */ - 1: 'stream end', /* Z_STREAM_END 1 */ - 0: '', /* Z_OK 0 */ - '-1': 'file error', /* Z_ERRNO (-1) */ - '-2': 'stream error', /* Z_STREAM_ERROR (-2) */ - '-3': 'data error', /* Z_DATA_ERROR (-3) */ - '-4': 'insufficient memory', /* Z_MEM_ERROR (-4) */ - '-5': 'buffer error', /* Z_BUF_ERROR (-5) */ - '-6': 'incompatible version' /* Z_VERSION_ERROR (-6) */ -}; diff --git a/base/app/novnc/vendor/pako/lib/zlib/trees.js b/base/app/novnc/vendor/pako/lib/zlib/trees.js deleted file mode 100644 index a69b8a5..0000000 --- a/base/app/novnc/vendor/pako/lib/zlib/trees.js +++ /dev/null @@ -1,1195 +0,0 @@ -import * as utils from "../utils/common.js"; - -/* Public constants ==========================================================*/ -/* ===========================================================================*/ - - -//var Z_FILTERED = 1; -//var Z_HUFFMAN_ONLY = 2; -//var Z_RLE = 3; -var Z_FIXED = 4; -//var Z_DEFAULT_STRATEGY = 0; - -/* Possible values of the data_type field (though see inflate()) */ -var Z_BINARY = 0; -var Z_TEXT = 1; -//var Z_ASCII = 1; // = Z_TEXT -var Z_UNKNOWN = 2; - -/*============================================================================*/ - - -function zero(buf) { var len = buf.length; while (--len >= 0) { buf[len] = 0; } } - -// From zutil.h - -var STORED_BLOCK = 0; -var STATIC_TREES = 1; -var DYN_TREES = 2; -/* The three kinds of block type */ - -var MIN_MATCH = 3; -var MAX_MATCH = 258; -/* The minimum and maximum match lengths */ - -// From deflate.h -/* =========================================================================== - * Internal compression state. - */ - -var LENGTH_CODES = 29; -/* number of length codes, not counting the special END_BLOCK code */ - -var LITERALS = 256; -/* number of literal bytes 0..255 */ - -var L_CODES = LITERALS + 1 + LENGTH_CODES; -/* number of Literal or Length codes, including the END_BLOCK code */ - -var D_CODES = 30; -/* number of distance codes */ - -var BL_CODES = 19; -/* number of codes used to transfer the bit lengths */ - -var HEAP_SIZE = 2 * L_CODES + 1; -/* maximum heap size */ - -var MAX_BITS = 15; -/* All codes must not exceed MAX_BITS bits */ - -var Buf_size = 16; -/* size of bit buffer in bi_buf */ - - -/* =========================================================================== - * Constants - */ - -var MAX_BL_BITS = 7; -/* Bit length codes must not exceed MAX_BL_BITS bits */ - -var END_BLOCK = 256; -/* end of block literal code */ - -var REP_3_6 = 16; -/* repeat previous bit length 3-6 times (2 bits of repeat count) */ - -var REPZ_3_10 = 17; -/* repeat a zero length 3-10 times (3 bits of repeat count) */ - -var REPZ_11_138 = 18; -/* repeat a zero length 11-138 times (7 bits of repeat count) */ - -/* eslint-disable comma-spacing,array-bracket-spacing */ -var extra_lbits = /* extra bits for each length code */ - [0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0]; - -var extra_dbits = /* extra bits for each distance code */ - [0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13]; - -var extra_blbits = /* extra bits for each bit length code */ - [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7]; - -var bl_order = - [16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]; -/* eslint-enable comma-spacing,array-bracket-spacing */ - -/* The lengths of the bit length codes are sent in order of decreasing - * probability, to avoid transmitting the lengths for unused bit length codes. - */ - -/* =========================================================================== - * Local data. These are initialized only once. - */ - -// We pre-fill arrays with 0 to avoid uninitialized gaps - -var DIST_CODE_LEN = 512; /* see definition of array dist_code below */ - -// !!!! Use flat array insdead of structure, Freq = i*2, Len = i*2+1 -var static_ltree = new Array((L_CODES + 2) * 2); -zero(static_ltree); -/* The static literal tree. Since the bit lengths are imposed, there is no - * need for the L_CODES extra codes used during heap construction. However - * The codes 286 and 287 are needed to build a canonical tree (see _tr_init - * below). - */ - -var static_dtree = new Array(D_CODES * 2); -zero(static_dtree); -/* The static distance tree. (Actually a trivial tree since all codes use - * 5 bits.) - */ - -var _dist_code = new Array(DIST_CODE_LEN); -zero(_dist_code); -/* Distance codes. The first 256 values correspond to the distances - * 3 .. 258, the last 256 values correspond to the top 8 bits of - * the 15 bit distances. - */ - -var _length_code = new Array(MAX_MATCH - MIN_MATCH + 1); -zero(_length_code); -/* length code for each normalized match length (0 == MIN_MATCH) */ - -var base_length = new Array(LENGTH_CODES); -zero(base_length); -/* First normalized length for each code (0 = MIN_MATCH) */ - -var base_dist = new Array(D_CODES); -zero(base_dist); -/* First normalized distance for each code (0 = distance of 1) */ - - -function StaticTreeDesc(static_tree, extra_bits, extra_base, elems, max_length) { - - this.static_tree = static_tree; /* static tree or NULL */ - this.extra_bits = extra_bits; /* extra bits for each code or NULL */ - this.extra_base = extra_base; /* base index for extra_bits */ - this.elems = elems; /* max number of elements in the tree */ - this.max_length = max_length; /* max bit length for the codes */ - - // show if `static_tree` has data or dummy - needed for monomorphic objects - this.has_stree = static_tree && static_tree.length; -} - - -var static_l_desc; -var static_d_desc; -var static_bl_desc; - - -function TreeDesc(dyn_tree, stat_desc) { - this.dyn_tree = dyn_tree; /* the dynamic tree */ - this.max_code = 0; /* largest code with non zero frequency */ - this.stat_desc = stat_desc; /* the corresponding static tree */ -} - - - -function d_code(dist) { - return dist < 256 ? _dist_code[dist] : _dist_code[256 + (dist >>> 7)]; -} - - -/* =========================================================================== - * Output a short LSB first on the stream. - * IN assertion: there is enough room in pendingBuf. - */ -function put_short(s, w) { -// put_byte(s, (uch)((w) & 0xff)); -// put_byte(s, (uch)((ush)(w) >> 8)); - s.pending_buf[s.pending++] = (w) & 0xff; - s.pending_buf[s.pending++] = (w >>> 8) & 0xff; -} - - -/* =========================================================================== - * Send a value on a given number of bits. - * IN assertion: length <= 16 and value fits in length bits. - */ -function send_bits(s, value, length) { - if (s.bi_valid > (Buf_size - length)) { - s.bi_buf |= (value << s.bi_valid) & 0xffff; - put_short(s, s.bi_buf); - s.bi_buf = value >> (Buf_size - s.bi_valid); - s.bi_valid += length - Buf_size; - } else { - s.bi_buf |= (value << s.bi_valid) & 0xffff; - s.bi_valid += length; - } -} - - -function send_code(s, c, tree) { - send_bits(s, tree[c * 2]/*.Code*/, tree[c * 2 + 1]/*.Len*/); -} - - -/* =========================================================================== - * Reverse the first len bits of a code, using straightforward code (a faster - * method would use a table) - * IN assertion: 1 <= len <= 15 - */ -function bi_reverse(code, len) { - var res = 0; - do { - res |= code & 1; - code >>>= 1; - res <<= 1; - } while (--len > 0); - return res >>> 1; -} - - -/* =========================================================================== - * Flush the bit buffer, keeping at most 7 bits in it. - */ -function bi_flush(s) { - if (s.bi_valid === 16) { - put_short(s, s.bi_buf); - s.bi_buf = 0; - s.bi_valid = 0; - - } else if (s.bi_valid >= 8) { - s.pending_buf[s.pending++] = s.bi_buf & 0xff; - s.bi_buf >>= 8; - s.bi_valid -= 8; - } -} - - -/* =========================================================================== - * Compute the optimal bit lengths for a tree and update the total bit length - * for the current block. - * IN assertion: the fields freq and dad are set, heap[heap_max] and - * above are the tree nodes sorted by increasing frequency. - * OUT assertions: the field len is set to the optimal bit length, the - * array bl_count contains the frequencies for each bit length. - * The length opt_len is updated; static_len is also updated if stree is - * not null. - */ -function gen_bitlen(s, desc) -// deflate_state *s; -// tree_desc *desc; /* the tree descriptor */ -{ - var tree = desc.dyn_tree; - var max_code = desc.max_code; - var stree = desc.stat_desc.static_tree; - var has_stree = desc.stat_desc.has_stree; - var extra = desc.stat_desc.extra_bits; - var base = desc.stat_desc.extra_base; - var max_length = desc.stat_desc.max_length; - var h; /* heap index */ - var n, m; /* iterate over the tree elements */ - var bits; /* bit length */ - var xbits; /* extra bits */ - var f; /* frequency */ - var overflow = 0; /* number of elements with bit length too large */ - - for (bits = 0; bits <= MAX_BITS; bits++) { - s.bl_count[bits] = 0; - } - - /* In a first pass, compute the optimal bit lengths (which may - * overflow in the case of the bit length tree). - */ - tree[s.heap[s.heap_max] * 2 + 1]/*.Len*/ = 0; /* root of the heap */ - - for (h = s.heap_max + 1; h < HEAP_SIZE; h++) { - n = s.heap[h]; - bits = tree[tree[n * 2 + 1]/*.Dad*/ * 2 + 1]/*.Len*/ + 1; - if (bits > max_length) { - bits = max_length; - overflow++; - } - tree[n * 2 + 1]/*.Len*/ = bits; - /* We overwrite tree[n].Dad which is no longer needed */ - - if (n > max_code) { continue; } /* not a leaf node */ - - s.bl_count[bits]++; - xbits = 0; - if (n >= base) { - xbits = extra[n - base]; - } - f = tree[n * 2]/*.Freq*/; - s.opt_len += f * (bits + xbits); - if (has_stree) { - s.static_len += f * (stree[n * 2 + 1]/*.Len*/ + xbits); - } - } - if (overflow === 0) { return; } - - // Trace((stderr,"\nbit length overflow\n")); - /* This happens for example on obj2 and pic of the Calgary corpus */ - - /* Find the first bit length which could increase: */ - do { - bits = max_length - 1; - while (s.bl_count[bits] === 0) { bits--; } - s.bl_count[bits]--; /* move one leaf down the tree */ - s.bl_count[bits + 1] += 2; /* move one overflow item as its brother */ - s.bl_count[max_length]--; - /* The brother of the overflow item also moves one step up, - * but this does not affect bl_count[max_length] - */ - overflow -= 2; - } while (overflow > 0); - - /* Now recompute all bit lengths, scanning in increasing frequency. - * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all - * lengths instead of fixing only the wrong ones. This idea is taken - * from 'ar' written by Haruhiko Okumura.) - */ - for (bits = max_length; bits !== 0; bits--) { - n = s.bl_count[bits]; - while (n !== 0) { - m = s.heap[--h]; - if (m > max_code) { continue; } - if (tree[m * 2 + 1]/*.Len*/ !== bits) { - // Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); - s.opt_len += (bits - tree[m * 2 + 1]/*.Len*/) * tree[m * 2]/*.Freq*/; - tree[m * 2 + 1]/*.Len*/ = bits; - } - n--; - } - } -} - - -/* =========================================================================== - * Generate the codes for a given tree and bit counts (which need not be - * optimal). - * IN assertion: the array bl_count contains the bit length statistics for - * the given tree and the field len is set for all tree elements. - * OUT assertion: the field code is set for all tree elements of non - * zero code length. - */ -function gen_codes(tree, max_code, bl_count) -// ct_data *tree; /* the tree to decorate */ -// int max_code; /* largest code with non zero frequency */ -// ushf *bl_count; /* number of codes at each bit length */ -{ - var next_code = new Array(MAX_BITS + 1); /* next code value for each bit length */ - var code = 0; /* running code value */ - var bits; /* bit index */ - var n; /* code index */ - - /* The distribution counts are first used to generate the code values - * without bit reversal. - */ - for (bits = 1; bits <= MAX_BITS; bits++) { - next_code[bits] = code = (code + bl_count[bits - 1]) << 1; - } - /* Check that the bit counts in bl_count are consistent. The last code - * must be all ones. - */ - //Assert (code + bl_count[MAX_BITS]-1 == (1< length code (0..28) */ - length = 0; - for (code = 0; code < LENGTH_CODES - 1; code++) { - base_length[code] = length; - for (n = 0; n < (1 << extra_lbits[code]); n++) { - _length_code[length++] = code; - } - } - //Assert (length == 256, "tr_static_init: length != 256"); - /* Note that the length 255 (match length 258) can be represented - * in two different ways: code 284 + 5 bits or code 285, so we - * overwrite length_code[255] to use the best encoding: - */ - _length_code[length - 1] = code; - - /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ - dist = 0; - for (code = 0; code < 16; code++) { - base_dist[code] = dist; - for (n = 0; n < (1 << extra_dbits[code]); n++) { - _dist_code[dist++] = code; - } - } - //Assert (dist == 256, "tr_static_init: dist != 256"); - dist >>= 7; /* from now on, all distances are divided by 128 */ - for (; code < D_CODES; code++) { - base_dist[code] = dist << 7; - for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) { - _dist_code[256 + dist++] = code; - } - } - //Assert (dist == 256, "tr_static_init: 256+dist != 512"); - - /* Construct the codes of the static literal tree */ - for (bits = 0; bits <= MAX_BITS; bits++) { - bl_count[bits] = 0; - } - - n = 0; - while (n <= 143) { - static_ltree[n * 2 + 1]/*.Len*/ = 8; - n++; - bl_count[8]++; - } - while (n <= 255) { - static_ltree[n * 2 + 1]/*.Len*/ = 9; - n++; - bl_count[9]++; - } - while (n <= 279) { - static_ltree[n * 2 + 1]/*.Len*/ = 7; - n++; - bl_count[7]++; - } - while (n <= 287) { - static_ltree[n * 2 + 1]/*.Len*/ = 8; - n++; - bl_count[8]++; - } - /* Codes 286 and 287 do not exist, but we must include them in the - * tree construction to get a canonical Huffman tree (longest code - * all ones) - */ - gen_codes(static_ltree, L_CODES + 1, bl_count); - - /* The static distance tree is trivial: */ - for (n = 0; n < D_CODES; n++) { - static_dtree[n * 2 + 1]/*.Len*/ = 5; - static_dtree[n * 2]/*.Code*/ = bi_reverse(n, 5); - } - - // Now data ready and we can init static trees - static_l_desc = new StaticTreeDesc(static_ltree, extra_lbits, LITERALS + 1, L_CODES, MAX_BITS); - static_d_desc = new StaticTreeDesc(static_dtree, extra_dbits, 0, D_CODES, MAX_BITS); - static_bl_desc = new StaticTreeDesc(new Array(0), extra_blbits, 0, BL_CODES, MAX_BL_BITS); - - //static_init_done = true; -} - - -/* =========================================================================== - * Initialize a new block. - */ -function init_block(s) { - var n; /* iterates over tree elements */ - - /* Initialize the trees. */ - for (n = 0; n < L_CODES; n++) { s.dyn_ltree[n * 2]/*.Freq*/ = 0; } - for (n = 0; n < D_CODES; n++) { s.dyn_dtree[n * 2]/*.Freq*/ = 0; } - for (n = 0; n < BL_CODES; n++) { s.bl_tree[n * 2]/*.Freq*/ = 0; } - - s.dyn_ltree[END_BLOCK * 2]/*.Freq*/ = 1; - s.opt_len = s.static_len = 0; - s.last_lit = s.matches = 0; -} - - -/* =========================================================================== - * Flush the bit buffer and align the output on a byte boundary - */ -function bi_windup(s) -{ - if (s.bi_valid > 8) { - put_short(s, s.bi_buf); - } else if (s.bi_valid > 0) { - //put_byte(s, (Byte)s->bi_buf); - s.pending_buf[s.pending++] = s.bi_buf; - } - s.bi_buf = 0; - s.bi_valid = 0; -} - -/* =========================================================================== - * Copy a stored block, storing first the length and its - * one's complement if requested. - */ -function copy_block(s, buf, len, header) -//DeflateState *s; -//charf *buf; /* the input data */ -//unsigned len; /* its length */ -//int header; /* true if block header must be written */ -{ - bi_windup(s); /* align on byte boundary */ - - if (header) { - put_short(s, len); - put_short(s, ~len); - } -// while (len--) { -// put_byte(s, *buf++); -// } - utils.arraySet(s.pending_buf, s.window, buf, len, s.pending); - s.pending += len; -} - -/* =========================================================================== - * Compares to subtrees, using the tree depth as tie breaker when - * the subtrees have equal frequency. This minimizes the worst case length. - */ -function smaller(tree, n, m, depth) { - var _n2 = n * 2; - var _m2 = m * 2; - return (tree[_n2]/*.Freq*/ < tree[_m2]/*.Freq*/ || - (tree[_n2]/*.Freq*/ === tree[_m2]/*.Freq*/ && depth[n] <= depth[m])); -} - -/* =========================================================================== - * Restore the heap property by moving down the tree starting at node k, - * exchanging a node with the smallest of its two sons if necessary, stopping - * when the heap property is re-established (each father smaller than its - * two sons). - */ -function pqdownheap(s, tree, k) -// deflate_state *s; -// ct_data *tree; /* the tree to restore */ -// int k; /* node to move down */ -{ - var v = s.heap[k]; - var j = k << 1; /* left son of k */ - while (j <= s.heap_len) { - /* Set j to the smallest of the two sons: */ - if (j < s.heap_len && - smaller(tree, s.heap[j + 1], s.heap[j], s.depth)) { - j++; - } - /* Exit if v is smaller than both sons */ - if (smaller(tree, v, s.heap[j], s.depth)) { break; } - - /* Exchange v with the smallest son */ - s.heap[k] = s.heap[j]; - k = j; - - /* And continue down the tree, setting j to the left son of k */ - j <<= 1; - } - s.heap[k] = v; -} - - -// inlined manually -// var SMALLEST = 1; - -/* =========================================================================== - * Send the block data compressed using the given Huffman trees - */ -function compress_block(s, ltree, dtree) -// deflate_state *s; -// const ct_data *ltree; /* literal tree */ -// const ct_data *dtree; /* distance tree */ -{ - var dist; /* distance of matched string */ - var lc; /* match length or unmatched char (if dist == 0) */ - var lx = 0; /* running index in l_buf */ - var code; /* the code to send */ - var extra; /* number of extra bits to send */ - - if (s.last_lit !== 0) { - do { - dist = (s.pending_buf[s.d_buf + lx * 2] << 8) | (s.pending_buf[s.d_buf + lx * 2 + 1]); - lc = s.pending_buf[s.l_buf + lx]; - lx++; - - if (dist === 0) { - send_code(s, lc, ltree); /* send a literal byte */ - //Tracecv(isgraph(lc), (stderr," '%c' ", lc)); - } else { - /* Here, lc is the match length - MIN_MATCH */ - code = _length_code[lc]; - send_code(s, code + LITERALS + 1, ltree); /* send the length code */ - extra = extra_lbits[code]; - if (extra !== 0) { - lc -= base_length[code]; - send_bits(s, lc, extra); /* send the extra length bits */ - } - dist--; /* dist is now the match distance - 1 */ - code = d_code(dist); - //Assert (code < D_CODES, "bad d_code"); - - send_code(s, code, dtree); /* send the distance code */ - extra = extra_dbits[code]; - if (extra !== 0) { - dist -= base_dist[code]; - send_bits(s, dist, extra); /* send the extra distance bits */ - } - } /* literal or match pair ? */ - - /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ - //Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, - // "pendingBuf overflow"); - - } while (lx < s.last_lit); - } - - send_code(s, END_BLOCK, ltree); -} - - -/* =========================================================================== - * Construct one Huffman tree and assigns the code bit strings and lengths. - * Update the total bit length for the current block. - * IN assertion: the field freq is set for all tree elements. - * OUT assertions: the fields len and code are set to the optimal bit length - * and corresponding code. The length opt_len is updated; static_len is - * also updated if stree is not null. The field max_code is set. - */ -function build_tree(s, desc) -// deflate_state *s; -// tree_desc *desc; /* the tree descriptor */ -{ - var tree = desc.dyn_tree; - var stree = desc.stat_desc.static_tree; - var has_stree = desc.stat_desc.has_stree; - var elems = desc.stat_desc.elems; - var n, m; /* iterate over heap elements */ - var max_code = -1; /* largest code with non zero frequency */ - var node; /* new node being created */ - - /* Construct the initial heap, with least frequent element in - * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. - * heap[0] is not used. - */ - s.heap_len = 0; - s.heap_max = HEAP_SIZE; - - for (n = 0; n < elems; n++) { - if (tree[n * 2]/*.Freq*/ !== 0) { - s.heap[++s.heap_len] = max_code = n; - s.depth[n] = 0; - - } else { - tree[n * 2 + 1]/*.Len*/ = 0; - } - } - - /* The pkzip format requires that at least one distance code exists, - * and that at least one bit should be sent even if there is only one - * possible code. So to avoid special checks later on we force at least - * two codes of non zero frequency. - */ - while (s.heap_len < 2) { - node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0); - tree[node * 2]/*.Freq*/ = 1; - s.depth[node] = 0; - s.opt_len--; - - if (has_stree) { - s.static_len -= stree[node * 2 + 1]/*.Len*/; - } - /* node is 0 or 1 so it does not have extra bits */ - } - desc.max_code = max_code; - - /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, - * establish sub-heaps of increasing lengths: - */ - for (n = (s.heap_len >> 1/*int /2*/); n >= 1; n--) { pqdownheap(s, tree, n); } - - /* Construct the Huffman tree by repeatedly combining the least two - * frequent nodes. - */ - node = elems; /* next internal node of the tree */ - do { - //pqremove(s, tree, n); /* n = node of least frequency */ - /*** pqremove ***/ - n = s.heap[1/*SMALLEST*/]; - s.heap[1/*SMALLEST*/] = s.heap[s.heap_len--]; - pqdownheap(s, tree, 1/*SMALLEST*/); - /***/ - - m = s.heap[1/*SMALLEST*/]; /* m = node of next least frequency */ - - s.heap[--s.heap_max] = n; /* keep the nodes sorted by frequency */ - s.heap[--s.heap_max] = m; - - /* Create a new node father of n and m */ - tree[node * 2]/*.Freq*/ = tree[n * 2]/*.Freq*/ + tree[m * 2]/*.Freq*/; - s.depth[node] = (s.depth[n] >= s.depth[m] ? s.depth[n] : s.depth[m]) + 1; - tree[n * 2 + 1]/*.Dad*/ = tree[m * 2 + 1]/*.Dad*/ = node; - - /* and insert the new node in the heap */ - s.heap[1/*SMALLEST*/] = node++; - pqdownheap(s, tree, 1/*SMALLEST*/); - - } while (s.heap_len >= 2); - - s.heap[--s.heap_max] = s.heap[1/*SMALLEST*/]; - - /* At this point, the fields freq and dad are set. We can now - * generate the bit lengths. - */ - gen_bitlen(s, desc); - - /* The field len is now set, we can generate the bit codes */ - gen_codes(tree, max_code, s.bl_count); -} - - -/* =========================================================================== - * Scan a literal or distance tree to determine the frequencies of the codes - * in the bit length tree. - */ -function scan_tree(s, tree, max_code) -// deflate_state *s; -// ct_data *tree; /* the tree to be scanned */ -// int max_code; /* and its largest code of non zero frequency */ -{ - var n; /* iterates over all tree elements */ - var prevlen = -1; /* last emitted length */ - var curlen; /* length of current code */ - - var nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */ - - var count = 0; /* repeat count of the current code */ - var max_count = 7; /* max repeat count */ - var min_count = 4; /* min repeat count */ - - if (nextlen === 0) { - max_count = 138; - min_count = 3; - } - tree[(max_code + 1) * 2 + 1]/*.Len*/ = 0xffff; /* guard */ - - for (n = 0; n <= max_code; n++) { - curlen = nextlen; - nextlen = tree[(n + 1) * 2 + 1]/*.Len*/; - - if (++count < max_count && curlen === nextlen) { - continue; - - } else if (count < min_count) { - s.bl_tree[curlen * 2]/*.Freq*/ += count; - - } else if (curlen !== 0) { - - if (curlen !== prevlen) { s.bl_tree[curlen * 2]/*.Freq*/++; } - s.bl_tree[REP_3_6 * 2]/*.Freq*/++; - - } else if (count <= 10) { - s.bl_tree[REPZ_3_10 * 2]/*.Freq*/++; - - } else { - s.bl_tree[REPZ_11_138 * 2]/*.Freq*/++; - } - - count = 0; - prevlen = curlen; - - if (nextlen === 0) { - max_count = 138; - min_count = 3; - - } else if (curlen === nextlen) { - max_count = 6; - min_count = 3; - - } else { - max_count = 7; - min_count = 4; - } - } -} - - -/* =========================================================================== - * Send a literal or distance tree in compressed form, using the codes in - * bl_tree. - */ -function send_tree(s, tree, max_code) -// deflate_state *s; -// ct_data *tree; /* the tree to be scanned */ -// int max_code; /* and its largest code of non zero frequency */ -{ - var n; /* iterates over all tree elements */ - var prevlen = -1; /* last emitted length */ - var curlen; /* length of current code */ - - var nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */ - - var count = 0; /* repeat count of the current code */ - var max_count = 7; /* max repeat count */ - var min_count = 4; /* min repeat count */ - - /* tree[max_code+1].Len = -1; */ /* guard already set */ - if (nextlen === 0) { - max_count = 138; - min_count = 3; - } - - for (n = 0; n <= max_code; n++) { - curlen = nextlen; - nextlen = tree[(n + 1) * 2 + 1]/*.Len*/; - - if (++count < max_count && curlen === nextlen) { - continue; - - } else if (count < min_count) { - do { send_code(s, curlen, s.bl_tree); } while (--count !== 0); - - } else if (curlen !== 0) { - if (curlen !== prevlen) { - send_code(s, curlen, s.bl_tree); - count--; - } - //Assert(count >= 3 && count <= 6, " 3_6?"); - send_code(s, REP_3_6, s.bl_tree); - send_bits(s, count - 3, 2); - - } else if (count <= 10) { - send_code(s, REPZ_3_10, s.bl_tree); - send_bits(s, count - 3, 3); - - } else { - send_code(s, REPZ_11_138, s.bl_tree); - send_bits(s, count - 11, 7); - } - - count = 0; - prevlen = curlen; - if (nextlen === 0) { - max_count = 138; - min_count = 3; - - } else if (curlen === nextlen) { - max_count = 6; - min_count = 3; - - } else { - max_count = 7; - min_count = 4; - } - } -} - - -/* =========================================================================== - * Construct the Huffman tree for the bit lengths and return the index in - * bl_order of the last bit length code to send. - */ -function build_bl_tree(s) { - var max_blindex; /* index of last bit length code of non zero freq */ - - /* Determine the bit length frequencies for literal and distance trees */ - scan_tree(s, s.dyn_ltree, s.l_desc.max_code); - scan_tree(s, s.dyn_dtree, s.d_desc.max_code); - - /* Build the bit length tree: */ - build_tree(s, s.bl_desc); - /* opt_len now includes the length of the tree representations, except - * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. - */ - - /* Determine the number of bit length codes to send. The pkzip format - * requires that at least 4 bit length codes be sent. (appnote.txt says - * 3 but the actual value used is 4.) - */ - for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) { - if (s.bl_tree[bl_order[max_blindex] * 2 + 1]/*.Len*/ !== 0) { - break; - } - } - /* Update opt_len to include the bit length tree and counts */ - s.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; - //Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", - // s->opt_len, s->static_len)); - - return max_blindex; -} - - -/* =========================================================================== - * Send the header for a block using dynamic Huffman trees: the counts, the - * lengths of the bit length codes, the literal tree and the distance tree. - * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. - */ -function send_all_trees(s, lcodes, dcodes, blcodes) -// deflate_state *s; -// int lcodes, dcodes, blcodes; /* number of codes for each tree */ -{ - var rank; /* index in bl_order */ - - //Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); - //Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, - // "too many codes"); - //Tracev((stderr, "\nbl counts: ")); - send_bits(s, lcodes - 257, 5); /* not +255 as stated in appnote.txt */ - send_bits(s, dcodes - 1, 5); - send_bits(s, blcodes - 4, 4); /* not -3 as stated in appnote.txt */ - for (rank = 0; rank < blcodes; rank++) { - //Tracev((stderr, "\nbl code %2d ", bl_order[rank])); - send_bits(s, s.bl_tree[bl_order[rank] * 2 + 1]/*.Len*/, 3); - } - //Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); - - send_tree(s, s.dyn_ltree, lcodes - 1); /* literal tree */ - //Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); - - send_tree(s, s.dyn_dtree, dcodes - 1); /* distance tree */ - //Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); -} - - -/* =========================================================================== - * Check if the data type is TEXT or BINARY, using the following algorithm: - * - TEXT if the two conditions below are satisfied: - * a) There are no non-portable control characters belonging to the - * "black list" (0..6, 14..25, 28..31). - * b) There is at least one printable character belonging to the - * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). - * - BINARY otherwise. - * - The following partially-portable control characters form a - * "gray list" that is ignored in this detection algorithm: - * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). - * IN assertion: the fields Freq of dyn_ltree are set. - */ -function detect_data_type(s) { - /* black_mask is the bit mask of black-listed bytes - * set bits 0..6, 14..25, and 28..31 - * 0xf3ffc07f = binary 11110011111111111100000001111111 - */ - var black_mask = 0xf3ffc07f; - var n; - - /* Check for non-textual ("black-listed") bytes. */ - for (n = 0; n <= 31; n++, black_mask >>>= 1) { - if ((black_mask & 1) && (s.dyn_ltree[n * 2]/*.Freq*/ !== 0)) { - return Z_BINARY; - } - } - - /* Check for textual ("white-listed") bytes. */ - if (s.dyn_ltree[9 * 2]/*.Freq*/ !== 0 || s.dyn_ltree[10 * 2]/*.Freq*/ !== 0 || - s.dyn_ltree[13 * 2]/*.Freq*/ !== 0) { - return Z_TEXT; - } - for (n = 32; n < LITERALS; n++) { - if (s.dyn_ltree[n * 2]/*.Freq*/ !== 0) { - return Z_TEXT; - } - } - - /* There are no "black-listed" or "white-listed" bytes: - * this stream either is empty or has tolerated ("gray-listed") bytes only. - */ - return Z_BINARY; -} - - -var static_init_done = false; - -/* =========================================================================== - * Initialize the tree data structures for a new zlib stream. - */ -function _tr_init(s) -{ - - if (!static_init_done) { - tr_static_init(); - static_init_done = true; - } - - s.l_desc = new TreeDesc(s.dyn_ltree, static_l_desc); - s.d_desc = new TreeDesc(s.dyn_dtree, static_d_desc); - s.bl_desc = new TreeDesc(s.bl_tree, static_bl_desc); - - s.bi_buf = 0; - s.bi_valid = 0; - - /* Initialize the first block of the first file: */ - init_block(s); -} - - -/* =========================================================================== - * Send a stored block - */ -function _tr_stored_block(s, buf, stored_len, last) -//DeflateState *s; -//charf *buf; /* input block */ -//ulg stored_len; /* length of input block */ -//int last; /* one if this is the last block for a file */ -{ - send_bits(s, (STORED_BLOCK << 1) + (last ? 1 : 0), 3); /* send block type */ - copy_block(s, buf, stored_len, true); /* with header */ -} - - -/* =========================================================================== - * Send one empty static block to give enough lookahead for inflate. - * This takes 10 bits, of which 7 may remain in the bit buffer. - */ -function _tr_align(s) { - send_bits(s, STATIC_TREES << 1, 3); - send_code(s, END_BLOCK, static_ltree); - bi_flush(s); -} - - -/* =========================================================================== - * Determine the best encoding for the current block: dynamic trees, static - * trees or store, and output the encoded block to the zip file. - */ -function _tr_flush_block(s, buf, stored_len, last) -//DeflateState *s; -//charf *buf; /* input block, or NULL if too old */ -//ulg stored_len; /* length of input block */ -//int last; /* one if this is the last block for a file */ -{ - var opt_lenb, static_lenb; /* opt_len and static_len in bytes */ - var max_blindex = 0; /* index of last bit length code of non zero freq */ - - /* Build the Huffman trees unless a stored block is forced */ - if (s.level > 0) { - - /* Check if the file is binary or text */ - if (s.strm.data_type === Z_UNKNOWN) { - s.strm.data_type = detect_data_type(s); - } - - /* Construct the literal and distance trees */ - build_tree(s, s.l_desc); - // Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, - // s->static_len)); - - build_tree(s, s.d_desc); - // Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, - // s->static_len)); - /* At this point, opt_len and static_len are the total bit lengths of - * the compressed block data, excluding the tree representations. - */ - - /* Build the bit length tree for the above two trees, and get the index - * in bl_order of the last bit length code to send. - */ - max_blindex = build_bl_tree(s); - - /* Determine the best encoding. Compute the block lengths in bytes. */ - opt_lenb = (s.opt_len + 3 + 7) >>> 3; - static_lenb = (s.static_len + 3 + 7) >>> 3; - - // Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", - // opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, - // s->last_lit)); - - if (static_lenb <= opt_lenb) { opt_lenb = static_lenb; } - - } else { - // Assert(buf != (char*)0, "lost buf"); - opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ - } - - if ((stored_len + 4 <= opt_lenb) && (buf !== -1)) { - /* 4: two words for the lengths */ - - /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. - * Otherwise we can't have processed more than WSIZE input bytes since - * the last block flush, because compression would have been - * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to - * transform a block into a stored block. - */ - _tr_stored_block(s, buf, stored_len, last); - - } else if (s.strategy === Z_FIXED || static_lenb === opt_lenb) { - - send_bits(s, (STATIC_TREES << 1) + (last ? 1 : 0), 3); - compress_block(s, static_ltree, static_dtree); - - } else { - send_bits(s, (DYN_TREES << 1) + (last ? 1 : 0), 3); - send_all_trees(s, s.l_desc.max_code + 1, s.d_desc.max_code + 1, max_blindex + 1); - compress_block(s, s.dyn_ltree, s.dyn_dtree); - } - // Assert (s->compressed_len == s->bits_sent, "bad compressed size"); - /* The above check is made mod 2^32, for files larger than 512 MB - * and uLong implemented on 32 bits. - */ - init_block(s); - - if (last) { - bi_windup(s); - } - // Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, - // s->compressed_len-7*last)); -} - -/* =========================================================================== - * Save the match info and tally the frequency counts. Return true if - * the current block must be flushed. - */ -function _tr_tally(s, dist, lc) -// deflate_state *s; -// unsigned dist; /* distance of matched string */ -// unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ -{ - //var out_length, in_length, dcode; - - s.pending_buf[s.d_buf + s.last_lit * 2] = (dist >>> 8) & 0xff; - s.pending_buf[s.d_buf + s.last_lit * 2 + 1] = dist & 0xff; - - s.pending_buf[s.l_buf + s.last_lit] = lc & 0xff; - s.last_lit++; - - if (dist === 0) { - /* lc is the unmatched char */ - s.dyn_ltree[lc * 2]/*.Freq*/++; - } else { - s.matches++; - /* Here, lc is the match length - MIN_MATCH */ - dist--; /* dist = match distance - 1 */ - //Assert((ush)dist < (ush)MAX_DIST(s) && - // (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && - // (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); - - s.dyn_ltree[(_length_code[lc] + LITERALS + 1) * 2]/*.Freq*/++; - s.dyn_dtree[d_code(dist) * 2]/*.Freq*/++; - } - -// (!) This block is disabled in zlib defailts, -// don't enable it for binary compatibility - -//#ifdef TRUNCATE_BLOCK -// /* Try to guess if it is profitable to stop the current block here */ -// if ((s.last_lit & 0x1fff) === 0 && s.level > 2) { -// /* Compute an upper bound for the compressed length */ -// out_length = s.last_lit*8; -// in_length = s.strstart - s.block_start; -// -// for (dcode = 0; dcode < D_CODES; dcode++) { -// out_length += s.dyn_dtree[dcode*2]/*.Freq*/ * (5 + extra_dbits[dcode]); -// } -// out_length >>>= 3; -// //Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", -// // s->last_lit, in_length, out_length, -// // 100L - out_length*100L/in_length)); -// if (s.matches < (s.last_lit>>1)/*int /2*/ && out_length < (in_length>>1)/*int /2*/) { -// return true; -// } -// } -//#endif - - return (s.last_lit === s.lit_bufsize - 1); - /* We avoid equality with lit_bufsize because of wraparound at 64K - * on 16 bit machines and because stored blocks are restricted to - * 64K-1 bytes. - */ -} - -export { _tr_init, _tr_stored_block, _tr_flush_block, _tr_tally, _tr_align }; diff --git a/base/app/novnc/vendor/pako/lib/zlib/zstream.js b/base/app/novnc/vendor/pako/lib/zlib/zstream.js deleted file mode 100644 index e7e674e..0000000 --- a/base/app/novnc/vendor/pako/lib/zlib/zstream.js +++ /dev/null @@ -1,24 +0,0 @@ -export default function ZStream() { - /* next input byte */ - this.input = null; // JS specific, because we have no pointers - this.next_in = 0; - /* number of bytes available at input */ - this.avail_in = 0; - /* total number of input bytes read so far */ - this.total_in = 0; - /* next output byte should be put there */ - this.output = null; // JS specific, because we have no pointers - this.next_out = 0; - /* remaining free space at output */ - this.avail_out = 0; - /* total number of bytes output so far */ - this.total_out = 0; - /* last error message, NULL if no error */ - this.msg = ''/*Z_NULL*/; - /* not visible by applications */ - this.state = null; - /* best guess about the data type: binary or text */ - this.data_type = 2/*Z_UNKNOWN*/; - /* adler32 value of the uncompressed data */ - this.adler = 0; -} diff --git a/base/app/novnc/vnc.html b/base/app/novnc/vnc.html deleted file mode 100644 index 24a118d..0000000 --- a/base/app/novnc/vnc.html +++ /dev/null @@ -1,341 +0,0 @@ - - - - - - noVNC - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
noVNC encountered an error:
-
-
-
-
- - -
- -
-
- -
- -

no
VNC

- -
- - - - - -
- -
- - - -
-
- - - - - - -
-
- - - -
-
-
- Power -
- - - -
-
- - - -
-
-
- Clipboard -
-

- Edit clipboard content in the textarea below. -

- -
-
- - - - - - -
-
-
- Settings -
-
    -
  • - -
  • -
  • - -
  • -

  • -
  • - -
  • -
  • - - -
  • -

  • -
  • -
    Advanced
    -
      -
    • - - -
    • -
    • - - -
    • -

    • -
    • - - -
    • -
    • -
      WebSocket
      -
        -
      • - -
      • -
      • - - -
      • -
      • - - -
      • -
      • - - -
      • -
      -
    • -

    • -
    • - -
    • -
    • - - -
    • -

    • -
    • - -
    • -

    • - -
    • - -
    • -
    -
  • -

  • -
  • - Version: - -
  • -
-
-
- - - - -
-
- -
- -
-
-
-
- - -
- - -
-
- -
- -
-
-
- - -
-
-
- Server identity -
-
- The server has provided the following identifying information: -
-
- Fingerprint: - -
-
- Please verify that the information is correct and press - "Approve". Otherwise press "Reject". -
-
- - -
-
-
- - -
-
-
- Credentials -
-
- - -
-
- - -
-
- -
-
-
- - -
-
-
- -
-
-
- - -
- - -
- - - - diff --git a/base/app/novnc/vnc_lite.html b/base/app/novnc/vnc_lite.html deleted file mode 100644 index eaf75f8..0000000 --- a/base/app/novnc/vnc_lite.html +++ /dev/null @@ -1,180 +0,0 @@ - - - - - - noVNC - - - - - - - -
-
Loading
-
Send CtrlAltDel
-
-
- -
- - diff --git a/firefox/20-firefox.sh b/firefox/20-firefox.sh deleted file mode 100755 index 3935a23..0000000 --- a/firefox/20-firefox.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env bash -# Create firefox profile directory -mkdir -p /app/firefox - -# Firefox custom user.js -cat >/app/firefox/user.js <<- 'firefox' -// First run -user_pref("app.normandy.first_run", false); -user_pref("toolkit.telemetry.reportingpolicy.firstRun", false); -user_pref("trailhead.firstrun.didSeeAboutWelcome", true); -user_pref("browser.startup.homepage_override.mstone", "ignore"); - -// Homepage -// user_pref("browser.startup.page", 1); -// user_pref("browser.startup.homepage", "https://myvelabs.app/"); - -// Security/privacy section -user_pref("app.shield.optoutstudies.enabled", false); -user_pref("browser.contentblocking.category", "standard"); -user_pref("datareporting.healthreport.uploadEnabled", false); -user_pref("extensions.pocket.enabled", false); -user_pref("dom.private-attribution.submission.enabled", false); -user_pref("network.trr.mode", 5); - -// Disable sponsored content on Firefox Home (Activity Stream) -user_pref("browser.newtabpage.activity-stream.showSearch", false); -user_pref("browser.newtabpage.activity-stream.showSponsored", false); -user_pref("browser.newtabpage.activity-stream.showSponsoredTopSites", false); -user_pref("browser.newtabpage.activity-stream.default.sites", ""); -user_pref("browser.newtabpage.activity-stream.topSitesRows", 4); - -// Disable about:config warning -user_pref("browser.aboutConfig.showWarning", false); - -// Disable url autocomplete -user_pref("browser.search.suggest.enabled", false); -user_pref("browser.urlbar.suggest.recentsearches", false); -user_pref("browser.urlbar.suggest.searches", false); - -// Closing firefox properties -user_pref("browser.warnOnQuitShortcut", false); -user_pref("browser.tabs.closeWindowWithLastTab", false); - -// Disable autohide toolbar on fullscreen -user_pref("browser.fullscreen.autohide", false); -firefox - -# i3/firefox startup -install /dev/stdin ~/.config/i3/startapp.sh <<- startup -#!/usr/bin/env bash -# Run firefox -while true -do - /usr/bin/firefox --profile /app/firefox ${MYVNC_FIREFOX_OPTS} --new-window ${MYVNC_FIREFOX_URL} -done -startup diff --git a/firefox/Dockerfile b/firefox/Dockerfile index 7812027..5713922 100644 --- a/firefox/Dockerfile +++ b/firefox/Dockerfile @@ -1,18 +1,45 @@ # syntax = docker/dockerfile:1 -FROM myvnc/arch/base:i3 +FROM quay.io/archlinux/archlinux:base-devel USER root +# Build ARG for additional packages to install (eg, openssh) +ARG addpkg + # Build ARG: use "root" for debugging ARG debug + +# Copy mirrorlist +COPY mirrorlist /etc/pacman.d/mirrorlist + +# Copy app folder +COPY entrypoint /entrypoint # Install packages RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman \ - pacman -Syu --ask 4 --needed \ - firefox firefox-decentraleyes firefox-ublock-origin \ - && pacman -Scc --ask 4 - -# Tigervnc -COPY *.sh /app/init.d/ + pacman-key --init \ + && sed -i '/ParallelDownloads/c ParallelDownloads = 10' /etc/pacman.conf \ + && pacman -Sy --ask 4 archlinux-keyring \ + && pacman -Su --ask 4 --needed ${addpkg} \ + sudo bash bash-completion \ + tigervnc \ + i3-wm ttf-dejavu \ + firefox firefox-decentraleyes firefox-ublock-origin \ + && pacman -Scc --ask 4 \ + && useradd --create-home --gid users --shell /usr/bin/bash user \ + && printf '%s\n' 'user ALL=(ALL:ALL) NOPASSWD: /usr/bin/pacman' \ + 'Defaults lecture = never' >/etc/sudoers.d/zz-DOCKER \ + && passwd -l root >/dev/null 2>&1 # Default environment USER ${debug:-user} +WORKDIR /home/user +ENV HOME=/home/user +ENV DISPLAY=:0 +ENV SHELL=/usr/bin/bash +ENV PS1="[\u@\h \W \$?]\$ " + +# X session environment variable +ENV STARTXBIN=i3 + +# Docker entrypoint +ENTRYPOINT ["/entrypoint"] diff --git a/base/build b/firefox/build similarity index 85% rename from base/build rename to firefox/build index 012cfc1..fe44626 100755 --- a/base/build +++ b/firefox/build @@ -11,5 +11,5 @@ DOCKER_BUILDKIT=1 \ docker build . \ --build-arg addpkg=${addpkg:-""} \ --build-arg debug=${debug:-""} \ - --tag ${buildtag:-myvnc/arch/base} -rm mirrorlist \ No newline at end of file + --tag ${buildtag:-myvnc/arch/firefox} +rm mirrorlist diff --git a/firefox/docker-compose.yaml b/firefox/docker-compose.yaml index eaac256..5df252e 100644 --- a/firefox/docker-compose.yaml +++ b/firefox/docker-compose.yaml @@ -3,22 +3,27 @@ services: image: myvnc/arch/firefox container_name: firefox restart: unless-stopped - build: . + # build: . # shm_size: 2gb # security_opt: # - seccomp:unconfined + environment: + MYVNC_VNCPASS: ${MYVNC_FIREFOX_VNCPASS} + networks: + - firefox + firefox-novnc: + image: myvnc/alpine/base + container_name: firefox-novnc + restart: unless-stopped ports: - ${MYVNC_FIREFOX_PORT}:6900 environment: - MYVNC_VNCPASS: ${MYVNC_FIREFOX_VNCPASS} - MYVNC_CUSTOM_TITLE: ${MYVNC_FIREFOX_CUSTOM_TITLE} MYVNC_PROXYPATH: ${MYVNC_FIREFOX_PROXYPATH} - - MYVNC_FIREFOX_URL: ${MYVNC_FIREFOX_URL} - MYVNC_FIREFOX_OPTS: ${MYVNC_FIREFOX_OPTS} - MYVNC_VNCSHARING: ${MYVNC_FIREFOX_VNCSHARING} + + MYVNC_VNCLISTEN_HOST: firefox + MYVNC_VNCLISTEN_PORT: 5900 networks: - firefox diff --git a/firefox/entrypoint b/firefox/entrypoint new file mode 100755 index 0000000..803eecf --- /dev/null +++ b/firefox/entrypoint @@ -0,0 +1,155 @@ +#!/usr/bin/env bash +# Export all variables +set -a + +# Abort if an error is encountered +set -e + +# SSH config +if [ -f ~/.ssh/id_ed25519 ] +then + chmod 0600 ~/.ssh/id_ed25519 +fi + +# Create base directories +for dir in ssh config/tigervnc +do + [ -d ~/.${dir} ] || mkdir -p ~/.${dir}/ +done + +# Check for MYVNC_VNCPASS variable +if [ -z ${MYVNC_VNCPASS} ] +then + echo "MYVNC_VNCPASS env variable is missing" + exit 1 +fi + +# Configure tigervnc auth +if [ ! -f ~/.config/tigervnc/passwd ] +then + echo "${MYVNC_VNCPASS}" | vncpasswd -f >~/.config/tigervnc/passwd + chmod 0600 ~/.config/tigervnc/passwd +fi + +# Remove VNCPASS env +unset MYVNC_VNCPASS + +# VNC xstartup +install /dev/stdin ~/.config/tigervnc/xstartup <<- xstartup +#!/usr/bin/env bash +unset SESSION_MANAGER +unset DBUS_SESSION_BUS_ADDRESS +exec ${STARTXBIN} +xstartup + +# Turn vnc sharing on/off +if [[ ${MYVNC_VNCSHARING} == "true" ]] || [[ ${MYVNC_VNCSHARING} == "1" ]] +then + _MYVNC_VNCSHARING=alwaysshared +else + _MYVNC_VNCSHARING=nevershared +fi + +# VNC config +cat >~/.config/tigervnc/config <<- vncconfig +session=i3 +dpi=192 +geometry=1920x1080 +framerate=60 +depth=32 +${_MYVNC_VNCSHARING} +vncconfig + +# i3 preferences +mkdir -p ~/.config/i3/config.d +cat >~/.config/i3/config <<- 'i3config' +# Font +font pango:DejaVu Sans Mono 8 + +# Include custom config +include ~/.config/i3/config.d/*.conf + +# Binds for killing application +bindsym Mod1+Mod4+Shift+q kill +bindsym Mod1+Mod4+Shift+f fullscreen toggle + +# Binds for cycling workspaces +bindsym Mod1+Mod4+Shift+Right workspace next +bindsym Mod1+Mod4+Shift+Left workspace prev + +# Locked mode +mode locked { + bindsym Mod4+Mod1+Escape mode default +} +bindsym Mod4+Mod1+Escape mode locked + +# Hide bar permanently +exec --no-startup-id i3-msg bar mode invisible + +# Always open window in fullscreen +for_window [all] fullscreen enable +for_window [all] border none +default_border none + +# Execute startup script for webapp +exec --no-startup-id /usr/bin/bash ~/.config/i3/startapp.sh +i3config + +# Create firefox profile directory +mkdir -p ~/firefox + +# Firefox custom user.js +cat >~/firefox/user.js <<- 'firefox' +// First run +user_pref("app.normandy.first_run", false); +user_pref("toolkit.telemetry.reportingpolicy.firstRun", false); +user_pref("trailhead.firstrun.didSeeAboutWelcome", true); +user_pref("browser.startup.homepage_override.mstone", "ignore"); + +// Homepage +// user_pref("browser.startup.page", 1); +// user_pref("browser.startup.homepage", "https://myvelabs.app/"); + +// Security/privacy section +user_pref("app.shield.optoutstudies.enabled", false); +user_pref("browser.contentblocking.category", "standard"); +user_pref("datareporting.healthreport.uploadEnabled", false); +user_pref("extensions.pocket.enabled", false); +user_pref("dom.private-attribution.submission.enabled", false); +user_pref("network.trr.mode", 5); + +// Disable sponsored content on Firefox Home (Activity Stream) +user_pref("browser.newtabpage.activity-stream.showSearch", false); +user_pref("browser.newtabpage.activity-stream.showSponsored", false); +user_pref("browser.newtabpage.activity-stream.showSponsoredTopSites", false); +user_pref("browser.newtabpage.activity-stream.default.sites", ""); +user_pref("browser.newtabpage.activity-stream.topSitesRows", 4); + +// Disable about:config warning +user_pref("browser.aboutConfig.showWarning", false); + +// Disable url autocomplete +user_pref("browser.search.suggest.enabled", false); +user_pref("browser.urlbar.suggest.recentsearches", false); +user_pref("browser.urlbar.suggest.searches", false); + +// Closing firefox properties +user_pref("browser.warnOnQuitShortcut", false); +user_pref("browser.tabs.closeWindowWithLastTab", false); + +// Disable autohide toolbar on fullscreen +user_pref("browser.fullscreen.autohide", false); +firefox + +# i3/firefox startup +install /dev/stdin ~/.config/i3/startapp.sh <<- startup +#!/usr/bin/env bash +# Run firefox +while true +do + /usr/bin/firefox --profile ~/firefox ${MYVNC_FIREFOX_OPTS} --new-window ${MYVNC_FIREFOX_URL} +done +startup + +# Launch Tigervnc +/usr/bin/dbus-launch vncserver :0 diff --git a/i3/.env b/i3/.env new file mode 100644 index 0000000..7b52d01 --- /dev/null +++ b/i3/.env @@ -0,0 +1,8 @@ +# .env for myvnc i3 webtop +MYVNC_I3_PORT= +MYVNC_I3_VNCPASS= + +MYVNC_I3_CUSTOM_TITLE= +MYVNC_I3_PROXYPATH= + +MYVNC_I3_VNCSHARING= diff --git a/i3/90-tigervnc.sh b/i3/90-tigervnc.sh deleted file mode 100755 index c187a37..0000000 --- a/i3/90-tigervnc.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -# Launch Tigervnc -/usr/bin/dbus-launch vncserver :0 & diff --git a/i3/Dockerfile b/i3/Dockerfile index 4720f22..0610255 100644 --- a/i3/Dockerfile +++ b/i3/Dockerfile @@ -1,22 +1,44 @@ # syntax = docker/dockerfile:1 -FROM myvnc/arch/base +FROM quay.io/archlinux/archlinux:base-devel USER root +# Build ARG for additional packages to install (eg, openssh) +ARG addpkg + # Build ARG: use "root" for debugging ARG debug -# X session environment variable -ENV STARTXBIN=i3 +# Copy mirrorlist +COPY mirrorlist /etc/pacman.d/mirrorlist + +# Copy app folder +COPY entrypoint /entrypoint # Install packages RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman \ - pacman -Syu --ask 4 --needed \ - tigervnc \ - i3-wm ttf-dejavu \ - && pacman -Scc --ask 4 + pacman-key --init \ + && sed -i '/ParallelDownloads/c ParallelDownloads = 10' /etc/pacman.conf \ + && pacman -Sy --ask 4 archlinux-keyring \ + && pacman -Su --ask 4 --needed ${addpkg} \ + sudo bash bash-completion \ + tigervnc \ + i3-wm ttf-dejavu \ + && pacman -Scc --ask 4 \ + && useradd --create-home --gid users --shell /usr/bin/bash user \ + && printf '%s\n' 'user ALL=(ALL:ALL) NOPASSWD: /usr/bin/pacman' \ + 'Defaults lecture = never' >/etc/sudoers.d/zz-DOCKER \ + && passwd -l root >/dev/null 2>&1 -# i3 -COPY *.sh /app/init.d/ - -# Reset user home directory +# Default environment USER ${debug:-user} +WORKDIR /home/user +ENV HOME=/home/user +ENV DISPLAY=:0 +ENV SHELL=/usr/bin/bash +ENV PS1="[\u@\h \W \$?]\$ " + +# X session environment variable +ENV STARTXBIN=i3 + +# Docker entrypoint +ENTRYPOINT ["/entrypoint"] diff --git a/i3/build b/i3/build index 4eec18e..553b960 100755 --- a/i3/build +++ b/i3/build @@ -1,7 +1,15 @@ #!/usr/bin/env bash # Docker build -# Optional: buildtag +# Optional buildtag, addpkg +# addpkg=openssh \ +set -e + +## Fetch latest mirrorlist +curl --fail --silent https://git.myvelabs.com/lab/archlinux/raw/branch/master/mirrorlist -o mirrorlist + DOCKER_BUILDKIT=1 \ docker build . \ + --build-arg addpkg=${addpkg:-""} \ --build-arg debug=${debug:-""} \ - --tag ${buildtag:-myvnc/arch/base:i3} + --tag ${buildtag:-myvnc/arch/i3} +rm mirrorlist diff --git a/i3/docker-compose.yaml b/i3/docker-compose.yaml new file mode 100644 index 0000000..6b71539 --- /dev/null +++ b/i3/docker-compose.yaml @@ -0,0 +1,32 @@ +services: + i3: + image: myvnc/arch/i3 + container_name: i3 + restart: unless-stopped + # build: . + # shm_size: 2gb + # security_opt: + # - seccomp:unconfined + environment: + MYVNC_VNCPASS: ${MYVNC_I3_VNCPASS} + networks: + - i3 + i3-novnc: + image: myvnc/alpine/base + container_name: i3-novnc + restart: unless-stopped + ports: + - ${MYVNC_I3_PORT}:6900 + environment: + MYVNC_CUSTOM_TITLE: ${MYVNC_I3_CUSTOM_TITLE} + MYVNC_PROXYPATH: ${MYVNC_I3_PROXYPATH} + MYVNC_VNCSHARING: ${MYVNC_I3_VNCSHARING} + + MYVNC_VNCLISTEN_HOST: i3 + MYVNC_VNCLISTEN_PORT: 5900 + networks: + - i3 + +networks: + i3: + external: false diff --git a/i3/10-tigervnc.sh b/i3/entrypoint similarity index 71% rename from i3/10-tigervnc.sh rename to i3/entrypoint index a01ad1e..4387236 100755 --- a/i3/10-tigervnc.sh +++ b/i3/entrypoint @@ -1,4 +1,22 @@ #!/usr/bin/env bash +# Export all variables +set -a + +# Abort if an error is encountered +set -e + +# SSH config +if [ -f ~/.ssh/id_ed25519 ] +then + chmod 0600 ~/.ssh/id_ed25519 +fi + +# Create base directories +for dir in ssh config/tigervnc +do + [ -d ~/.${dir} ] || mkdir -p ~/.${dir}/ +done + # Check for MYVNC_VNCPASS variable if [ -z ${MYVNC_VNCPASS} ] then @@ -7,17 +25,17 @@ then fi # Configure tigervnc auth -if [ ! -f ~/.vnc/passwd ] +if [ ! -f ~/.config/tigervnc/passwd ] then - echo "${MYVNC_VNCPASS}" | vncpasswd -f >~/.vnc/passwd - chmod 0600 ~/.vnc/passwd + echo "${MYVNC_VNCPASS}" | vncpasswd -f >~/.config/tigervnc/passwd + chmod 0600 ~/.config/tigervnc/passwd fi # Remove VNCPASS env unset MYVNC_VNCPASS # VNC xstartup -install /dev/stdin ~/.vnc/xstartup <<- xstartup +install /dev/stdin ~/.config/tigervnc/xstartup <<- xstartup #!/usr/bin/env bash unset SESSION_MANAGER unset DBUS_SESSION_BUS_ADDRESS @@ -33,8 +51,9 @@ else fi # VNC config -cat >~/.vnc/config <<- vncconfig +cat >~/.config/tigervnc/config <<- vncconfig session=i3 +dpi=192 geometry=1920x1080 framerate=60 depth=32 @@ -75,3 +94,6 @@ default_border none # Execute startup script for webapp exec --no-startup-id /usr/bin/bash ~/.config/i3/startapp.sh i3config + +# Launch Tigervnc +/usr/bin/dbus-launch vncserver :0 diff --git a/i3/up b/i3/up new file mode 100755 index 0000000..fac9971 --- /dev/null +++ b/i3/up @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# Docker build +# Optional buildtag +# DOCKER_BUILDKIT=1 \ +# docker build . \ +# --build-arg debug=${debug:-""} \ +# --tag ${buildtag:-myvnc/novnc} +# Grab options +while [ ${1} ] +do + case ${1} in + -b | --build | b | build ) + build="--build" + ;; + -d | --detach | d | detach ) + detach="--detach" + ;; + bd | db ) + build="--build" + detach="--detach" + ;; + esac + shift +done + +# Compose up +docker compose up ${build} ${detach} diff --git a/plasma/Dockerfile b/plasma/Dockerfile index 879b6f9..f33a59f 100644 --- a/plasma/Dockerfile +++ b/plasma/Dockerfile @@ -33,9 +33,7 @@ RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman \ && useradd --create-home --gid users --shell /usr/bin/bash user \ && printf '%s\n' 'user ALL=(ALL:ALL) NOPASSWD: /usr/bin/pacman' \ 'Defaults lecture = never' >/etc/sudoers.d/zz-DOCKER \ - && passwd -l root >/dev/null 2>&1 \ - && mkdir -p /app/nginx /app/logs \ - && chown -R user:users /app + && passwd -l root >/dev/null 2>&1 # Default environment USER ${debug:-user} diff --git a/plasma/docker-compose.yaml b/plasma/docker-compose.yaml index 81f1a18..1b8ee81 100644 --- a/plasma/docker-compose.yaml +++ b/plasma/docker-compose.yaml @@ -3,6 +3,10 @@ services: image: myvnc/arch/plasma container_name: kdeplasma restart: unless-stopped + # build: . + # shm_size: 2gb + # security_opt: + # - seccomp:unconfined environment: MYVNC_VNCPASS: ${MYVNC_KDEPLASMA_VNCPASS} networks: @@ -20,7 +24,6 @@ services: image: myvnc/alpine/base container_name: kdeplasma-novnc restart: unless-stopped - # shm_size: 2gb ports: - ${MYVNC_KDEPLASMA_PORT}:6900 environment: diff --git a/plasma/entrypoint b/plasma/entrypoint index 1a2c7fe..2293152 100755 --- a/plasma/entrypoint +++ b/plasma/entrypoint @@ -36,7 +36,7 @@ unset MYVNC_VNCPASS # VNC xstartup install /dev/stdin ~/.config/tigervnc/xstartup <<- xstartup - +#!/usr/bin/env bash unset SESSION_MANAGER unset DBUS_SESSION_BUS_ADDRESS exec ${STARTXBIN} @@ -68,5 +68,8 @@ LockOnResume=false Timeout=0 kscreenlockerrc +# Read cli parameters, if any +exec "${@}" & + # Launch Tigervnc /usr/bin/dbus-launch vncserver :0