Merge remote-tracking branch 'remotes/base/master'

This commit is contained in:
mloginov 2024-07-03 11:50:18 +01:00
commit d109185fa2
45 changed files with 1046 additions and 502 deletions

View File

@ -1 +0,0 @@
**/xtscancodes.js

View File

@ -1,54 +0,0 @@
{
"env": {
"browser": true,
"es2020": true
},
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 2020
},
"extends": "eslint:recommended",
"rules": {
// Unsafe or confusing stuff that we forbid
"no-unused-vars": ["error", { "vars": "all", "args": "none", "ignoreRestSiblings": true }],
"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_"] }],
}
}

View File

@ -10,18 +10,18 @@ jobs:
npm:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- 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@v3
- 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@v3
- uses: actions/upload-artifact@v4
with:
name: npm
path: lib
@ -49,7 +49,7 @@ jobs:
snap:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- run: |
GITREV=$(git rev-parse --short HEAD)
echo $GITREV
@ -61,7 +61,7 @@ jobs:
sed -i "s/^version:.*/version: '$VERSION'/" snap/snapcraft.yaml
- uses: snapcore/action-build@v1
id: snapcraft
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: snap
path: ${{ steps.snapcraft.outputs.snap }}

View File

@ -6,14 +6,14 @@ jobs:
eslint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- 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@v3
- uses: actions/setup-node@v3
- 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

View File

@ -20,8 +20,8 @@ jobs:
fail-fast: false
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: npm update
- run: npm run test
env:

View File

@ -6,8 +6,8 @@ jobs:
translate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: npm update
- run: sudo apt-get install gettext
- run: make -C po update-pot

View File

@ -32,8 +32,6 @@ for a more complete list with additional info and links.
### News/help/contact
The project website is found at [novnc.com](http://novnc.com).
Notable commits, announcements and news are posted to
[@noVNC](http://www.twitter.com/noVNC).
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).
@ -59,7 +57,6 @@ profits such as:
[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](http://www.twitter.com/noVNC) if you do.
### Features

View File

@ -1,4 +1,5 @@
{
"HTTPS is required for full functionality": "Το HTTPS είναι απαιτούμενο για πλήρη λειτουργικότητα",
"Connecting...": "Συνδέεται...",
"Disconnecting...": "Aποσυνδέεται...",
"Reconnecting...": "Επανασυνδέεται...",
@ -7,19 +8,15 @@
"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": "Απαιτείται ο κωδικός πρόσβασης",
"Credentials are required": "Απαιτούνται διαπιστευτήρια",
"noVNC encountered an error:": "το noVNC αντιμετώπισε ένα σφάλμα:",
"Hide/Show the control bar": "Απόκρυψη/Εμφάνιση γραμμής ελέγχου",
"Drag": "Σύρσιμο",
"Move/Drag Viewport": "Μετακίνηση/Σύρσιμο Θεατού πεδίου",
"viewport drag": "σύρσιμο θεατού πεδίου",
"Active Mouse Button": "Ενεργό Πλήκτρο Ποντικιού",
"No mousebutton": "Χωρίς Πλήκτρο Ποντικιού",
"Left mousebutton": "Αριστερό Πλήκτρο Ποντικιού",
"Middle mousebutton": "Μεσαίο Πλήκτρο Ποντικιού",
"Right mousebutton": "Δεξί Πλήκτρο Ποντικιού",
"Keyboard": "Πληκτρολόγιο",
"Show Keyboard": "Εμφάνιση Πληκτρολογίου",
"Extra keys": "Επιπλέον πλήκτρα",
@ -28,6 +25,8 @@
"Toggle Ctrl": "Εναλλαγή Ctrl",
"Alt": "Alt",
"Toggle Alt": "Εναλλαγή Alt",
"Toggle Windows": "Εναλλαγή Παράθυρων",
"Windows": "Παράθυρα",
"Send Tab": "Αποστολή Tab",
"Tab": "Tab",
"Esc": "Esc",
@ -41,8 +40,7 @@
"Reboot": "Επανεκκίνηση",
"Reset": "Επαναφορά",
"Clipboard": "Πρόχειρο",
"Clear": "Καθάρισμα",
"Fullscreen": "Πλήρης Οθόνη",
"Edit clipboard content in the textarea below.": "Επεξεργαστείτε το περιεχόμενο του πρόχειρου στην περιοχή κειμένου παρακάτω.",
"Settings": "Ρυθμίσεις",
"Shared Mode": "Κοινόχρηστη Λειτουργία",
"View Only": "Μόνο Θέαση",
@ -52,6 +50,8 @@
"Local Scaling": "Τοπική Κλιμάκωση",
"Remote Resizing": "Απομακρυσμένη Αλλαγή μεγέθους",
"Advanced": "Για προχωρημένους",
"Quality:": "Ποιότητα:",
"Compression level:": "Επίπεδο συμπίεσης:",
"Repeater ID:": "Repeater ID:",
"WebSocket": "WebSocket",
"Encrypt": "Κρυπτογράφηση",
@ -60,10 +60,20 @@
"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:": "Κωδικός Πρόσβασης:",
"Cancel": "Ακύρωση",
"Canvas not supported.": "Δεν υποστηρίζεται το στοιχείο Canvas"
"Send Credentials": "Αποστολή Διαπιστευτηρίων",
"Cancel": "Ακύρωση"
}

View File

@ -1,5 +1,4 @@
{
"HTTPS is required for full functionality": "",
"Connecting...": "En cours de connexion...",
"Disconnecting...": "Déconnexion en cours...",
"Reconnecting...": "Reconnexion en cours...",
@ -40,7 +39,8 @@
"Reboot": "Redémarrer",
"Reset": "Réinitialiser",
"Clipboard": "Presse-papiers",
"Edit clipboard content in the textarea below.": "",
"Clear": "Effacer",
"Fullscreen": "Plein écran",
"Settings": "Paramètres",
"Shared Mode": "Mode partagé",
"View Only": "Afficher uniquement",
@ -65,12 +65,6 @@
"Version:": "Version :",
"Disconnect": "Déconnecter",
"Connect": "Connecter",
"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": "",
"Username:": "Nom d'utilisateur :",
"Password:": "Mot de passe :",
"Send Credentials": "Envoyer les identifiants",

View File

@ -14,8 +14,6 @@
"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",
"Drag": "",
"Move/Drag Viewport": "",
"Keyboard": "Tastiera",
"Show Keyboard": "Mostra tastiera",
"Extra keys": "Tasti Aggiuntivi",
@ -44,7 +42,6 @@
"Settings": "Impostazioni",
"Shared Mode": "Modalità condivisa",
"View Only": "Sola Visualizzazione",
"Clip to Window": "",
"Scaling Mode:": "Modalità di ridimensionamento:",
"None": "Nessuna",
"Local Scaling": "Ridimensionamento Locale",
@ -61,7 +58,6 @@
"Automatic Reconnect": "Riconnessione Automatica",
"Reconnect Delay (ms):": "Ritardo Riconnessione (ms):",
"Show Dot when No Cursor": "Mostra Punto quando Nessun Cursore",
"Logging:": "",
"Version:": "Versione:",
"Disconnect": "Disconnetti",
"Connect": "Connetti",

View File

@ -1,4 +1,5 @@
{
"HTTPS is required for full functionality": "すべての機能を使用するにはHTTPS接続が必要です",
"Connecting...": "接続しています...",
"Disconnecting...": "切断しています...",
"Reconnecting...": "再接続しています...",
@ -21,10 +22,10 @@
"Extra keys": "追加キー",
"Show Extra Keys": "追加キーを表示",
"Ctrl": "Ctrl",
"Toggle Ctrl": "Ctrl キーを切り替え",
"Toggle Ctrl": "Ctrl キーをトグル",
"Alt": "Alt",
"Toggle Alt": "Alt キーを切り替え",
"Toggle Windows": "Windows キーを切り替え",
"Toggle Alt": "Alt キーをトグル",
"Toggle Windows": "Windows キーをトグル",
"Windows": "Windows",
"Send Tab": "Tab キーを送信",
"Tab": "Tab",
@ -39,11 +40,11 @@
"Reboot": "再起動",
"Reset": "リセット",
"Clipboard": "クリップボード",
"Clear": "クリア",
"Fullscreen": "全画面表示",
"Edit clipboard content in the textarea below.": "以下の入力欄からクリップボードの内容を編集できます。",
"Full Screen": "全画面表示",
"Settings": "設定",
"Shared Mode": "共有モード",
"View Only": "表示のみ",
"View Only": "表示専用",
"Clip to Window": "ウィンドウにクリップ",
"Scaling Mode:": "スケーリングモード:",
"None": "なし",
@ -60,11 +61,18 @@
"Path:": "パス:",
"Automatic Reconnect": "自動再接続",
"Reconnect Delay (ms):": "再接続する遅延 (ミリ秒):",
"Show Dot when No Cursor": "カーソルがないときにドットを表示",
"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": "資格情報を送信",

View File

@ -1,10 +1,11 @@
{
"HTTPS is required for full functionality": "HTTPS krävs för full funktionalitet",
"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",

View File

@ -1,69 +1,69 @@
{
"Connecting...": "连接中...",
"Connected (encrypted) to ": "已连接(已加密)到",
"Connected (unencrypted) to ": "已连接(未加密)到",
"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": "连接被拒绝",
"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": "鼠标右",
"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...": "关机/重...",
"Ctrl+Alt+Del": "Ctrl+Alt+Del",
"Send Ctrl-Alt-Del": "发送 Ctrl+Alt+Del 键",
"Shutdown/Reboot": "关机/重启",
"Shutdown/Reboot...": "关机/重启...",
"Power": "电源",
"Shutdown": "关机",
"Reboot": "重",
"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",
"Encrypt": "加密",
"Host:": "主机:",
"Port:": "端口:",
"Path:": "路径:",
"Automatic Reconnect": "自动重新连接",
"Reconnect Delay (ms):": "重新连接间隔 (ms)",
"Logging:": "日志级别:",
"Disconnect": "断连接",
"Disconnect": "连接",
"Connect": "连接",
"Password:": "密码:",
"Cancel": "取消"
"Cancel": "取消",
"Canvas not supported.": "不支持 Canvas。"
}

View File

@ -67,7 +67,7 @@ const UI = {
// insecure context
if (!window.isSecureContext) {
// FIXME: This gets hidden when connecting
UI.showStatus(_("HTTPS is required for full functionality"), 'error');
UI.showStatus(_("Running without HTTPS is not recommended, crashes or other issues are likely."), 'error');
}
// Try to fetch version number
@ -1097,10 +1097,18 @@ const UI = {
}
url += '/' + path;
UI.rfb = new RFB(document.getElementById('noVNC_container'), url,
{ shared: UI.getSetting('shared'),
repeaterID: UI.getSetting('repeaterID'),
credentials: { password: password } });
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);

View File

@ -212,7 +212,7 @@ function localStorageGet(name) {
try {
r = localStorage.getItem(name);
} catch (e) {
if (e instanceof DOMException || !localStorage) {
if (e instanceof DOMException) {
logOnce(cookiesMsg);
logOnce("'localStorage.getItem(" + name + ")' failed: " + e,
"debug");

View File

@ -285,7 +285,73 @@ export default class TightDecoder {
}
_gradientFilter(streamId, x, y, width, height, sock, display, depth) {
throw new Error("Gradient filter not implemented");
// 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) {

View File

@ -7,7 +7,7 @@
*/
import { deflateInit, deflate } from "../vendor/pako/lib/zlib/deflate.js";
import { Z_FULL_FLUSH } 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 {
@ -15,9 +15,8 @@ export default class Deflator {
this.strm = new ZStream();
this.chunkSize = 1024 * 10 * 10;
this.outputBuffer = new Uint8Array(this.chunkSize);
this.windowBits = 5;
deflateInit(this.strm, this.windowBits);
deflateInit(this.strm, Z_DEFAULT_COMPRESSION);
}
deflate(inData) {

View File

@ -22,6 +22,7 @@ export const encodings = {
pseudoEncodingLastRect: -224,
pseudoEncodingCursor: -239,
pseudoEncodingQEMUExtendedKeyEvent: -258,
pseudoEncodingQEMULedEvent: -261,
pseudoEncodingDesktopName: -307,
pseudoEncodingExtendedDesktopSize: -308,
pseudoEncodingXvp: -309,

View File

@ -14,9 +14,8 @@ export default class Inflate {
this.strm = new ZStream();
this.chunkSize = 1024 * 10 * 10;
this.strm.output = new Uint8Array(this.chunkSize);
this.windowBits = 5;
inflateInit(this.strm, this.windowBits);
inflateInit(this.strm);
}
setInput(data) {

View File

@ -36,7 +36,7 @@ export default class Keyboard {
// ===== PRIVATE METHODS =====
_sendKeyEvent(keysym, code, down) {
_sendKeyEvent(keysym, code, down, numlock = null, capslock = null) {
if (down) {
this._keyDownList[code] = keysym;
} else {
@ -48,8 +48,9 @@ export default class Keyboard {
}
Log.Debug("onkeyevent " + (down ? "down" : "up") +
", keysym: " + keysym, ", code: " + code);
this.onkeyevent(keysym, code, down);
", keysym: " + keysym, ", code: " + code +
", numlock: " + numlock + ", capslock: " + capslock);
this.onkeyevent(keysym, code, down, numlock, capslock);
}
_getKeyCode(e) {
@ -86,6 +87,14 @@ export default class Keyboard {
_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,
@ -107,7 +116,7 @@ export default class Keyboard {
// key to "AltGraph".
keysym = KeyTable.XK_ISO_Level3_Shift;
} else {
this._sendKeyEvent(KeyTable.XK_Control_L, "ControlLeft", true);
this._sendKeyEvent(KeyTable.XK_Control_L, "ControlLeft", true, numlock, capslock);
}
}
@ -118,8 +127,8 @@ export default class Keyboard {
// 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);
this._sendKeyEvent(keysym, code, false);
this._sendKeyEvent(keysym, code, true, numlock, capslock);
this._sendKeyEvent(keysym, code, false, numlock, capslock);
}
stopEvent(e);
@ -157,8 +166,8 @@ export default class Keyboard {
// while meta is held down
if ((browser.isMac() || browser.isIOS()) &&
(e.metaKey && code !== 'MetaLeft' && code !== 'MetaRight')) {
this._sendKeyEvent(keysym, code, true);
this._sendKeyEvent(keysym, code, false);
this._sendKeyEvent(keysym, code, true, numlock, capslock);
this._sendKeyEvent(keysym, code, false, numlock, capslock);
stopEvent(e);
return;
}
@ -168,8 +177,8 @@ export default class Keyboard {
// 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);
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', false);
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', true, numlock, capslock);
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', false, numlock, capslock);
stopEvent(e);
return;
}
@ -182,8 +191,8 @@ export default class Keyboard {
KeyTable.XK_Hiragana,
KeyTable.XK_Romaji ];
if (browser.isWindows() && jpBadKeys.includes(keysym)) {
this._sendKeyEvent(keysym, code, true);
this._sendKeyEvent(keysym, code, false);
this._sendKeyEvent(keysym, code, true, numlock, capslock);
this._sendKeyEvent(keysym, code, false, numlock, capslock);
stopEvent(e);
return;
}
@ -199,7 +208,7 @@ export default class Keyboard {
return;
}
this._sendKeyEvent(keysym, code, true);
this._sendKeyEvent(keysym, code, true, numlock, capslock);
}
_handleKeyUp(e) {

View File

@ -67,7 +67,7 @@ export function getKeycode(evt) {
// Get 'KeyboardEvent.key', handling legacy browsers
export function getKey(evt) {
// Are we getting a proper key value?
if (evt.key !== undefined) {
if ((evt.key !== undefined) && (evt.key !== 'Unidentified')) {
// Mozilla isn't fully in sync with the spec yet
switch (evt.key) {
case 'OS': return 'Meta';

View File

@ -260,6 +260,8 @@ export default class RFB extends EventTargetMixin {
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();
@ -996,11 +998,39 @@ export default class RFB extends EventTargetMixin {
}
}
_sendPingMessage() {
window.parent.postMessage(JSON.stringify({method: 'noVNCPing'}), '*');
}
_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");
_handleKeyEvent(keysym, code, down) {
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._sendPingMessage();
this.sendKey(keysym, code, down);
}
@ -1938,7 +1968,11 @@ export default class RFB extends EventTargetMixin {
_negotiateAuthentication() {
switch (this._rfbAuthScheme) {
case securityTypeNone:
this._rfbInitState = 'SecurityResult';
if (this._rfbVersion >= 3.8) {
this._rfbInitState = 'SecurityResult';
} else {
this._rfbInitState = 'ClientInitialisation';
}
return true;
case securityTypeXVP:
@ -1975,13 +2009,6 @@ export default class RFB extends EventTargetMixin {
}
_handleSecurityResult() {
// There is no security choice, and hence no security result
// until RFB 3.7
if (this._rfbVersion < 3.7) {
this._rfbInitState = 'ClientInitialisation';
return true;
}
if (this._sock.rQwait('VNC auth response ', 4)) { return false; }
const status = this._sock.rQshift32();
@ -2117,6 +2144,7 @@ export default class RFB extends EventTargetMixin {
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);
@ -2341,7 +2369,7 @@ export default class RFB extends EventTargetMixin {
textData = textData.slice(0, -1);
}
textData = textData.replace("\r\n", "\n");
textData = textData.replaceAll("\r\n", "\n");
this.dispatchEvent(new CustomEvent(
"clipboard",
@ -2552,6 +2580,9 @@ export default class RFB extends EventTargetMixin {
case encodings.pseudoEncodingExtendedDesktopSize:
return this._handleExtendedDesktopSize();
case encodings.pseudoEncodingQEMULedEvent:
return this._handleLedEvent();
default:
return this._handleDataRect();
}
@ -2729,6 +2760,21 @@ export default class RFB extends EventTargetMixin {
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;

View File

@ -69,7 +69,9 @@ export default class Cursor {
this._target.removeEventListener('mousemove', this._eventHandlers.mousemove, options);
this._target.removeEventListener('mouseup', this._eventHandlers.mouseup, options);
document.body.removeChild(this._canvas);
if (document.contains(this._canvas)) {
document.body.removeChild(this._canvas);
}
}
this._target = null;

View File

@ -3,13 +3,13 @@
.SH NAME
novnc_proxy - noVNC proxy server
.SH SYNOPSIS
.B novnc_proxy [--listen PORT] [--vnc VNC_HOST:PORT] [--cert CERT] [--ssl-only]
.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 PORT Port for proxy/webserver to listen on
Default: 6080
--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

102
eslint.config.mjs Normal file
View File

@ -0,0 +1,102 @@
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,
},
},
];

View File

@ -1,6 +1,6 @@
{
"name": "@novnc/novnc",
"version": "1.4.0",
"version": "1.5.0",
"description": "An HTML5 VNC client",
"browser": "lib/rfb",
"directories": {
@ -14,9 +14,7 @@
"VERSION",
"docs/API.md",
"docs/LIBRARY.md",
"docs/LICENSE*",
"core",
"vendor/pako"
"docs/LICENSE*"
],
"scripts": {
"lint": "eslint app core po/po2js po/xgettext-html tests utils",
@ -39,19 +37,14 @@
"homepage": "https://github.com/novnc/noVNC",
"devDependencies": {
"@babel/core": "latest",
"@babel/plugin-syntax-dynamic-import": "latest",
"@babel/plugin-transform-modules-commonjs": "latest",
"@babel/preset-env": "latest",
"@babel/cli": "latest",
"babel-plugin-import-redirect": "latest",
"browserify": "latest",
"babelify": "latest",
"core-js": "latest",
"chai": "latest",
"commander": "latest",
"es-module-loader": "latest",
"eslint": "latest",
"fs-extra": "latest",
"globals": "latest",
"jsdom": "latest",
"karma": "latest",
"karma-mocha": "latest",

View File

@ -1,5 +0,0 @@
{
"env": {
"node": true,
},
}

262
po/el.po
View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: noVNC 0.6.1\n"
"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
"POT-Creation-Date: 2017-11-17 21:40+0200\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"
@ -17,273 +17,349 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: ../app/ui.js:404
#: ../app/ui.js:69
msgid "HTTPS is required for full functionality"
msgstr "Το HTTPS είναι απαιτούμενο για πλήρη λειτουργικότητα"
#: ../app/ui.js:410
msgid "Connecting..."
msgstr "Συνδέεται..."
#: ../app/ui.js:411
#: ../app/ui.js:417
msgid "Disconnecting..."
msgstr "Aποσυνδέεται..."
#: ../app/ui.js:417
#: ../app/ui.js:423
msgid "Reconnecting..."
msgstr "Επανασυνδέεται..."
#: ../app/ui.js:422
#: ../app/ui.js:428
msgid "Internal error"
msgstr "Εσωτερικό σφάλμα"
#: ../app/ui.js:1019
#: ../app/ui.js:1026
msgid "Must set host"
msgstr "Πρέπει να οριστεί ο διακομιστής"
#: ../app/ui.js:1099
#: ../app/ui.js:1110
msgid "Connected (encrypted) to "
msgstr "Συνδέθηκε (κρυπτογραφημένα) με το "
#: ../app/ui.js:1101
#: ../app/ui.js:1112
msgid "Connected (unencrypted) to "
msgstr "Συνδέθηκε (μη κρυπτογραφημένα) με το "
#: ../app/ui.js:1119
#: ../app/ui.js:1135
msgid "Something went wrong, connection is closed"
msgstr "Κάτι πήγε στραβά, η σύνδεση διακόπηκε"
#: ../app/ui.js:1129
#: ../app/ui.js:1138
msgid "Failed to connect to server"
msgstr "Αποτυχία στη σύνδεση με το διακομιστή"
#: ../app/ui.js:1150
msgid "Disconnected"
msgstr "Αποσυνδέθηκε"
#: ../app/ui.js:1142
#: ../app/ui.js:1165
msgid "New connection has been rejected with reason: "
msgstr "Η νέα σύνδεση απορρίφθηκε διότι: "
#: ../app/ui.js:1145
#: ../app/ui.js:1168
msgid "New connection has been rejected"
msgstr "Η νέα σύνδεση απορρίφθηκε "
#: ../app/ui.js:1166
msgid "Password is required"
msgstr "Απαιτείται ο κωδικός πρόσβασης"
#: ../app/ui.js:1234
msgid "Credentials are required"
msgstr "Απαιτούνται διαπιστευτήρια"
#: ../vnc.html:89
#: ../vnc.html:57
msgid "noVNC encountered an error:"
msgstr "το noVNC αντιμετώπισε ένα σφάλμα:"
#: ../vnc.html:99
#: ../vnc.html:67
msgid "Hide/Show the control bar"
msgstr "Απόκρυψη/Εμφάνιση γραμμής ελέγχου"
#: ../vnc.html:106
#: ../vnc.html:76
msgid "Drag"
msgstr "Σύρσιμο"
#: ../vnc.html:76
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
#: ../vnc.html:82
msgid "Keyboard"
msgstr "Πληκτρολόγιο"
#: ../vnc.html:124
#: ../vnc.html:82
msgid "Show Keyboard"
msgstr "Εμφάνιση Πληκτρολογίου"
#: ../vnc.html:131
#: ../vnc.html:87
msgid "Extra keys"
msgstr "Επιπλέον πλήκτρα"
#: ../vnc.html:131
#: ../vnc.html:87
msgid "Show Extra Keys"
msgstr "Εμφάνιση Επιπλέον Πλήκτρων"
#: ../vnc.html:136
#: ../vnc.html:92
msgid "Ctrl"
msgstr "Ctrl"
#: ../vnc.html:136
#: ../vnc.html:92
msgid "Toggle Ctrl"
msgstr "Εναλλαγή Ctrl"
#: ../vnc.html:139
#: ../vnc.html:95
msgid "Alt"
msgstr "Alt"
#: ../vnc.html:139
#: ../vnc.html:95
msgid "Toggle Alt"
msgstr "Εναλλαγή Alt"
#: ../vnc.html:142
#: ../vnc.html:98
msgid "Toggle Windows"
msgstr "Εναλλαγή Παράθυρων"
#: ../vnc.html:98
msgid "Windows"
msgstr "Παράθυρα"
#: ../vnc.html:101
msgid "Send Tab"
msgstr "Αποστολή Tab"
#: ../vnc.html:142
#: ../vnc.html:101
msgid "Tab"
msgstr "Tab"
#: ../vnc.html:145
#: ../vnc.html:104
msgid "Esc"
msgstr "Esc"
#: ../vnc.html:145
#: ../vnc.html:104
msgid "Send Escape"
msgstr "Αποστολή Escape"
#: ../vnc.html:148
#: ../vnc.html:107
msgid "Ctrl+Alt+Del"
msgstr "Ctrl+Alt+Del"
#: ../vnc.html:148
#: ../vnc.html:107
msgid "Send Ctrl-Alt-Del"
msgstr "Αποστολή Ctrl-Alt-Del"
#: ../vnc.html:156
#: ../vnc.html:114
msgid "Shutdown/Reboot"
msgstr "Κλείσιμο/Επανεκκίνηση"
#: ../vnc.html:156
#: ../vnc.html:114
msgid "Shutdown/Reboot..."
msgstr "Κλείσιμο/Επανεκκίνηση..."
#: ../vnc.html:162
#: ../vnc.html:120
msgid "Power"
msgstr "Απενεργοποίηση"
#: ../vnc.html:164
#: ../vnc.html:122
msgid "Shutdown"
msgstr "Κλείσιμο"
#: ../vnc.html:165
#: ../vnc.html:123
msgid "Reboot"
msgstr "Επανεκκίνηση"
#: ../vnc.html:166
#: ../vnc.html:124
msgid "Reset"
msgstr "Επαναφορά"
#: ../vnc.html:171 ../vnc.html:177
#: ../vnc.html:129 ../vnc.html:135
msgid "Clipboard"
msgstr "Πρόχειρο"
#: ../vnc.html:181
msgid "Clear"
msgstr "Καθάρισμα"
#: ../vnc.html:137
msgid "Edit clipboard content in the textarea below."
msgstr "Επεξεργαστείτε το περιεχόμενο του πρόχειρου στην περιοχή κειμένου παρακάτω."
#: ../vnc.html:187
msgid "Fullscreen"
#: ../vnc.html:145
#, fuzzy
msgid "Full Screen"
msgstr "Πλήρης Οθόνη"
#: ../vnc.html:192 ../vnc.html:199
#: ../vnc.html:150 ../vnc.html:156
msgid "Settings"
msgstr "Ρυθμίσεις"
#: ../vnc.html:202
#: ../vnc.html:160
msgid "Shared Mode"
msgstr "Κοινόχρηστη Λειτουργία"
#: ../vnc.html:205
#: ../vnc.html:163
msgid "View Only"
msgstr "Μόνο Θέαση"
#: ../vnc.html:209
#: ../vnc.html:167
msgid "Clip to Window"
msgstr "Αποκοπή στο όριο του Παράθυρου"
#: ../vnc.html:212
#: ../vnc.html:170
msgid "Scaling Mode:"
msgstr "Λειτουργία Κλιμάκωσης:"
#: ../vnc.html:214
#: ../vnc.html:172
msgid "None"
msgstr "Καμία"
#: ../vnc.html:215
#: ../vnc.html:173
msgid "Local Scaling"
msgstr "Τοπική Κλιμάκωση"
#: ../vnc.html:216
#: ../vnc.html:174
msgid "Remote Resizing"
msgstr "Απομακρυσμένη Αλλαγή μεγέθους"
#: ../vnc.html:221
#: ../vnc.html:179
msgid "Advanced"
msgstr "Για προχωρημένους"
#: ../vnc.html:224
#: ../vnc.html:182
msgid "Quality:"
msgstr "Ποιότητα:"
#: ../vnc.html:186
msgid "Compression level:"
msgstr "Επίπεδο συμπίεσης:"
#: ../vnc.html:191
msgid "Repeater ID:"
msgstr "Repeater ID:"
#: ../vnc.html:228
#: ../vnc.html:195
msgid "WebSocket"
msgstr "WebSocket"
#: ../vnc.html:231
#: ../vnc.html:198
msgid "Encrypt"
msgstr "Κρυπτογράφηση"
#: ../vnc.html:234
#: ../vnc.html:201
msgid "Host:"
msgstr "Όνομα διακομιστή:"
#: ../vnc.html:238
#: ../vnc.html:205
msgid "Port:"
msgstr "Πόρτα διακομιστή:"
#: ../vnc.html:242
#: ../vnc.html:209
msgid "Path:"
msgstr "Διαδρομή:"
#: ../vnc.html:249
#: ../vnc.html:216
msgid "Automatic Reconnect"
msgstr "Αυτόματη επανασύνδεση"
#: ../vnc.html:252
#: ../vnc.html:219
msgid "Reconnect Delay (ms):"
msgstr "Καθυστέρηση επανασύνδεσης (ms):"
#: ../vnc.html:258
#: ../vnc.html:224
msgid "Show Dot when No Cursor"
msgstr "Εμφάνιση Τελείας όταν δεν υπάρχει Δρομέας"
#: ../vnc.html:229
msgid "Logging:"
msgstr "Καταγραφή:"
#: ../vnc.html:270
#: ../vnc.html:238
msgid "Version:"
msgstr "Έκδοση:"
#: ../vnc.html:246
msgid "Disconnect"
msgstr "Αποσύνδεση"
#: ../vnc.html:289
#: ../vnc.html:269
msgid "Connect"
msgstr "Σύνδεση"
#: ../vnc.html:299
#: ../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:313
#: ../vnc.html:314
msgid "Send Credentials"
msgstr "Αποστολή Διαπιστευτηρίων"
#: ../vnc.html:323
msgid "Cancel"
msgstr "Ακύρωση"
#: ../vnc.html:329
msgid "Canvas not supported."
msgstr "Δεν υποστηρίζεται το στοιχείο Canvas"
#~ 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 "Παρέλευση χρονικού ορίου αποσύνδεσης"

View File

@ -6,9 +6,9 @@
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: noVNC 1.4.0\n"
"Project-Id-Version: noVNC 1.5.0\n"
"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
"POT-Creation-Date: 2022-12-27 15:24+0100\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"
@ -18,7 +18,8 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
#: ../app/ui.js:69
msgid "HTTPS is required for full functionality"
msgid ""
"Running without HTTPS is not recommended, crashes or other issues are likely."
msgstr ""
#: ../app/ui.js:410
@ -41,292 +42,296 @@ msgstr ""
msgid "Must set host"
msgstr ""
#: ../app/ui.js:1110
#: ../app/ui.js:1052
msgid "Failed to connect to server: "
msgstr ""
#: ../app/ui.js:1118
msgid "Connected (encrypted) to "
msgstr ""
#: ../app/ui.js:1112
#: ../app/ui.js:1120
msgid "Connected (unencrypted) to "
msgstr ""
#: ../app/ui.js:1135
#: ../app/ui.js:1143
msgid "Something went wrong, connection is closed"
msgstr ""
#: ../app/ui.js:1138
#: ../app/ui.js:1146
msgid "Failed to connect to server"
msgstr ""
#: ../app/ui.js:1150
#: ../app/ui.js:1158
msgid "Disconnected"
msgstr ""
#: ../app/ui.js:1165
#: ../app/ui.js:1173
msgid "New connection has been rejected with reason: "
msgstr ""
#: ../app/ui.js:1168
#: ../app/ui.js:1176
msgid "New connection has been rejected"
msgstr ""
#: ../app/ui.js:1234
#: ../app/ui.js:1242
msgid "Credentials are required"
msgstr ""
#: ../vnc.html:57
#: ../vnc.html:55
msgid "noVNC encountered an error:"
msgstr ""
#: ../vnc.html:67
#: ../vnc.html:65
msgid "Hide/Show the control bar"
msgstr ""
#: ../vnc.html:76
#: ../vnc.html:74
msgid "Drag"
msgstr ""
#: ../vnc.html:76
#: ../vnc.html:74
msgid "Move/Drag Viewport"
msgstr ""
#: ../vnc.html:82
#: ../vnc.html:80
msgid "Keyboard"
msgstr ""
#: ../vnc.html:82
#: ../vnc.html:80
msgid "Show Keyboard"
msgstr ""
#: ../vnc.html:87
#: ../vnc.html:85
msgid "Extra keys"
msgstr ""
#: ../vnc.html:87
#: ../vnc.html:85
msgid "Show Extra Keys"
msgstr ""
#: ../vnc.html:92
#: ../vnc.html:90
msgid "Ctrl"
msgstr ""
#: ../vnc.html:92
#: ../vnc.html:90
msgid "Toggle Ctrl"
msgstr ""
#: ../vnc.html:95
#: ../vnc.html:93
msgid "Alt"
msgstr ""
#: ../vnc.html:95
#: ../vnc.html:93
msgid "Toggle Alt"
msgstr ""
#: ../vnc.html:98
#: ../vnc.html:96
msgid "Toggle Windows"
msgstr ""
#: ../vnc.html:98
#: ../vnc.html:96
msgid "Windows"
msgstr ""
#: ../vnc.html:101
#: ../vnc.html:99
msgid "Send Tab"
msgstr ""
#: ../vnc.html:101
#: ../vnc.html:99
msgid "Tab"
msgstr ""
#: ../vnc.html:104
#: ../vnc.html:102
msgid "Esc"
msgstr ""
#: ../vnc.html:104
#: ../vnc.html:102
msgid "Send Escape"
msgstr ""
#: ../vnc.html:107
#: ../vnc.html:105
msgid "Ctrl+Alt+Del"
msgstr ""
#: ../vnc.html:107
#: ../vnc.html:105
msgid "Send Ctrl-Alt-Del"
msgstr ""
#: ../vnc.html:114
#: ../vnc.html:112
msgid "Shutdown/Reboot"
msgstr ""
#: ../vnc.html:114
#: ../vnc.html:112
msgid "Shutdown/Reboot..."
msgstr ""
#: ../vnc.html:120
#: ../vnc.html:118
msgid "Power"
msgstr ""
#: ../vnc.html:122
#: ../vnc.html:120
msgid "Shutdown"
msgstr ""
#: ../vnc.html:123
#: ../vnc.html:121
msgid "Reboot"
msgstr ""
#: ../vnc.html:124
#: ../vnc.html:122
msgid "Reset"
msgstr ""
#: ../vnc.html:129 ../vnc.html:135
#: ../vnc.html:127 ../vnc.html:133
msgid "Clipboard"
msgstr ""
#: ../vnc.html:137
#: ../vnc.html:135
msgid "Edit clipboard content in the textarea below."
msgstr ""
#: ../vnc.html:145
#: ../vnc.html:143
msgid "Full Screen"
msgstr ""
#: ../vnc.html:150 ../vnc.html:156
#: ../vnc.html:148 ../vnc.html:154
msgid "Settings"
msgstr ""
#: ../vnc.html:160
#: ../vnc.html:158
msgid "Shared Mode"
msgstr ""
#: ../vnc.html:163
#: ../vnc.html:161
msgid "View Only"
msgstr ""
#: ../vnc.html:167
#: ../vnc.html:165
msgid "Clip to Window"
msgstr ""
#: ../vnc.html:170
#: ../vnc.html:168
msgid "Scaling Mode:"
msgstr ""
#: ../vnc.html:172
#: ../vnc.html:170
msgid "None"
msgstr ""
#: ../vnc.html:173
#: ../vnc.html:171
msgid "Local Scaling"
msgstr ""
#: ../vnc.html:174
#: ../vnc.html:172
msgid "Remote Resizing"
msgstr ""
#: ../vnc.html:179
#: ../vnc.html:177
msgid "Advanced"
msgstr ""
#: ../vnc.html:182
#: ../vnc.html:180
msgid "Quality:"
msgstr ""
#: ../vnc.html:186
#: ../vnc.html:184
msgid "Compression level:"
msgstr ""
#: ../vnc.html:191
#: ../vnc.html:189
msgid "Repeater ID:"
msgstr ""
#: ../vnc.html:195
#: ../vnc.html:193
msgid "WebSocket"
msgstr ""
#: ../vnc.html:198
#: ../vnc.html:196
msgid "Encrypt"
msgstr ""
#: ../vnc.html:201
#: ../vnc.html:199
msgid "Host:"
msgstr ""
#: ../vnc.html:205
#: ../vnc.html:203
msgid "Port:"
msgstr ""
#: ../vnc.html:209
#: ../vnc.html:207
msgid "Path:"
msgstr ""
#: ../vnc.html:216
#: ../vnc.html:214
msgid "Automatic Reconnect"
msgstr ""
#: ../vnc.html:219
#: ../vnc.html:217
msgid "Reconnect Delay (ms):"
msgstr ""
#: ../vnc.html:224
#: ../vnc.html:222
msgid "Show Dot when No Cursor"
msgstr ""
#: ../vnc.html:229
#: ../vnc.html:227
msgid "Logging:"
msgstr ""
#: ../vnc.html:238
#: ../vnc.html:236
msgid "Version:"
msgstr ""
#: ../vnc.html:246
#: ../vnc.html:244
msgid "Disconnect"
msgstr ""
#: ../vnc.html:269
#: ../vnc.html:267
msgid "Connect"
msgstr ""
#: ../vnc.html:278
#: ../vnc.html:276
msgid "Server identity"
msgstr ""
#: ../vnc.html:281
#: ../vnc.html:279
msgid "The server has provided the following identifying information:"
msgstr ""
#: ../vnc.html:285
#: ../vnc.html:283
msgid "Fingerprint:"
msgstr ""
#: ../vnc.html:288
#: ../vnc.html:286
msgid ""
"Please verify that the information is correct and press \"Approve\". "
"Otherwise press \"Reject\"."
msgstr ""
#: ../vnc.html:293
#: ../vnc.html:291
msgid "Approve"
msgstr ""
#: ../vnc.html:294
#: ../vnc.html:292
msgid "Reject"
msgstr ""
#: ../vnc.html:302
#: ../vnc.html:300
msgid "Credentials"
msgstr ""
#: ../vnc.html:306
#: ../vnc.html:304
msgid "Username:"
msgstr ""
#: ../vnc.html:310
#: ../vnc.html:308
msgid "Password:"
msgstr ""
#: ../vnc.html:314
#: ../vnc.html:312
msgid "Send Credentials"
msgstr ""
#: ../vnc.html:323
#: ../vnc.html:321
msgid "Cancel"
msgstr ""

View File

@ -32,11 +32,13 @@ if (opt.argv.length != 2) {
const data = po2json.parseFileSync(opt.argv[0]);
const bodyPart = Object.keys(data).filter(msgid => msgid !== "").map((msgid) => {
if (msgid === "") return;
const msgstr = data[msgid][1];
return " " + JSON.stringify(msgid) + ": " + JSON.stringify(msgstr);
}).join(",\n");
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}";

View File

@ -8,20 +8,23 @@ msgid ""
msgstr ""
"Project-Id-Version: noVNC 1.3.0\n"
"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
"POT-Creation-Date: 2023-01-20 12:54+0100\n"
"PO-Revision-Date: 2023-01-20 12:58+0100\n"
"Last-Translator: Samuel Mannehed <samuel@cendio.se>\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.2.2\n"
"X-Generator: Poedit 3.4.4\n"
#: ../app/ui.js:69
msgid "HTTPS is required for full functionality"
msgstr "HTTPS krävs för full funktionalitet"
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..."
@ -43,35 +46,39 @@ msgstr "Internt fel"
msgid "Must set host"
msgstr "Du måste specifiera en värd"
#: ../app/ui.js:1110
#: ../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:1112
#: ../app/ui.js:1120
msgid "Connected (unencrypted) to "
msgstr "Ansluten (okrypterat) till "
#: ../app/ui.js:1135
#: ../app/ui.js:1143
msgid "Something went wrong, connection is closed"
msgstr "Något gick fel, anslutningen avslutades"
#: ../app/ui.js:1138
#: ../app/ui.js:1146
msgid "Failed to connect to server"
msgstr "Misslyckades att ansluta till servern"
#: ../app/ui.js:1150
#: ../app/ui.js:1158
msgid "Disconnected"
msgstr "Frånkopplad"
#: ../app/ui.js:1165
#: ../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:1168
#: ../app/ui.js:1176
msgid "New connection has been rejected"
msgstr "Ny anslutning har blivit nekad"
#: ../app/ui.js:1234
#: ../app/ui.js:1242
msgid "Credentials are required"
msgstr "Användaruppgifter krävs"
@ -304,8 +311,8 @@ 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\"."
"Kontrollera att informationen är korrekt och tryck sedan \"Godkänn\". Tryck "
"annars \"Neka\"."
#: ../vnc.html:291
msgid "Approve"
@ -335,5 +342,8 @@ msgstr "Skicka Användaruppgifter"
msgid "Cancel"
msgstr "Avbryt"
#~ msgid "HTTPS is required for full functionality"
#~ msgstr "HTTPS krävs för full funktionalitet"
#~ msgid "Clear"
#~ msgstr "Rensa"

View File

@ -15,58 +15,42 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: ../app/ui.js:395
#: ../app/ui.js:430
msgid "Connecting..."
msgstr "连接中..."
#: ../app/ui.js:402
#: ../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: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
#: ../app/ui.js:450
msgid "Disconnected"
msgstr "已断开连接"
#: ../app/ui.js:1146
msgid "New connection has been rejected with reason: "
msgstr "连接被拒绝,原因:"
#: ../app/ui.js:1052 ../core/rfb.js:248
msgid "Must set host"
msgstr "必须设置主机"
#: ../app/ui.js:1149
msgid "New connection has been rejected"
msgstr "连接被拒绝"
#: ../app/ui.js:1101
msgid "Reconnecting..."
msgstr "重新连接中..."
#: ../app/ui.js:1170
#: ../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 遇到一个错误:"
@ -77,31 +61,31 @@ msgstr "显示/隐藏控制栏"
#: ../vnc.html:106
msgid "Move/Drag Viewport"
msgstr "拖放显示范围"
msgstr "移动/拖动窗口"
#: ../vnc.html:106
msgid "viewport drag"
msgstr "显示范围拖放"
msgstr "窗口拖动"
#: ../vnc.html:112 ../vnc.html:115 ../vnc.html:118 ../vnc.html:121
msgid "Active Mouse Button"
msgstr "启动鼠标按"
msgstr "启动鼠标按"
#: ../vnc.html:112
msgid "No mousebutton"
msgstr "禁用鼠标按"
msgstr "禁用鼠标按"
#: ../vnc.html:115
msgid "Left mousebutton"
msgstr "鼠标左"
msgstr "鼠标左"
#: ../vnc.html:118
msgid "Middle mousebutton"
msgstr "鼠标中"
msgstr "鼠标中"
#: ../vnc.html:121
msgid "Right mousebutton"
msgstr "鼠标右"
msgstr "鼠标右"
#: ../vnc.html:124
msgid "Keyboard"
@ -127,6 +111,10 @@ msgstr "Ctrl"
msgid "Toggle Ctrl"
msgstr "切换 Ctrl"
#: ../vnc.html:136
msgid "Edit clipboard content in the textarea below."
msgstr "在下面的文本区域中编辑剪贴板内容。"
#: ../vnc.html:139
msgid "Alt"
msgstr "Alt"
@ -153,19 +141,19 @@ msgstr "发送 Escape 键"
#: ../vnc.html:148
msgid "Ctrl+Alt+Del"
msgstr "Ctrl-Alt-Del"
msgstr "Ctrl+Alt+Del"
#: ../vnc.html:148
msgid "Send Ctrl-Alt-Del"
msgstr "发送 Ctrl-Alt-Del 键"
msgstr "发送 Ctrl+Alt+Del 键"
#: ../vnc.html:156
msgid "Shutdown/Reboot"
msgstr "关机/重"
msgstr "关机/重启"
#: ../vnc.html:156
msgid "Shutdown/Reboot..."
msgstr "关机/重..."
msgstr "关机/重启..."
#: ../vnc.html:162
msgid "Power"
@ -177,7 +165,7 @@ msgstr "关机"
#: ../vnc.html:165
msgid "Reboot"
msgstr "重"
msgstr "重启"
#: ../vnc.html:166
msgid "Reset"
@ -199,6 +187,10 @@ msgstr "全屏"
msgid "Settings"
msgstr "设置"
#: ../vnc.html:200
msgid "Encrypt"
msgstr "加密"
#: ../vnc.html:202
msgid "Shared Mode"
msgstr "分享模式"
@ -224,61 +216,69 @@ msgid "Local Scaling"
msgstr "本地缩放"
#: ../vnc.html:216
msgid "Local Downscaling"
msgstr "降低本地尺寸"
#: ../vnc.html:217
msgid "Remote Resizing"
msgstr "远程调整大小"
#: ../vnc.html:221
#: ../vnc.html:222
msgid "Advanced"
msgstr "高级"
#: ../vnc.html:224
#: ../vnc.html:225
msgid "Local Cursor"
msgstr "本地光标"
#: ../vnc.html:229
msgid "Repeater ID:"
msgstr "中继站 ID"
#: ../vnc.html:228
#: ../vnc.html:233
msgid "WebSocket"
msgstr "WebSocket"
#: ../vnc.html:231
msgid "Encrypt"
msgstr "加密"
#: ../vnc.html:234
#: ../vnc.html:239
msgid "Host:"
msgstr "主机:"
#: ../vnc.html:238
#: ../vnc.html:243
msgid "Port:"
msgstr "端口:"
#: ../vnc.html:242
#: ../vnc.html:247
msgid "Path:"
msgstr "路径:"
#: ../vnc.html:249
#: ../vnc.html:254
msgid "Automatic Reconnect"
msgstr "自动重新连接"
#: ../vnc.html:252
#: ../vnc.html:257
msgid "Reconnect Delay (ms):"
msgstr "重新连接间隔 (ms)"
#: ../vnc.html:258
#: ../vnc.html:263
msgid "Logging:"
msgstr "日志级别:"
#: ../vnc.html:270
#: ../vnc.html:275
msgid "Disconnect"
msgstr "断连接"
msgstr "断连接"
#: ../vnc.html:289
#: ../vnc.html:294
msgid "Connect"
msgstr "连接"
#: ../vnc.html:299
#: ../vnc.html:304
msgid "Password:"
msgstr "密码:"
#: ../vnc.html:313
#: ../vnc.html:318
msgid "Cancel"
msgstr "取消"
#: ../vnc.html:334
msgid "Canvas not supported."
msgstr "不支持 Canvas。"

View File

@ -1,5 +1,5 @@
name: novnc
base: core18 # the base snap is the execution environment for this snap
base: core22 # the base snap is the execution environment for this snap
version: git
summary: Open Source VNC client using HTML5 (WebSockets, Canvas)
description: |
@ -42,7 +42,7 @@ parts:
- jq
websockify:
source: https://github.com/novnc/websockify/archive/v0.11.0.tar.gz
source: https://github.com/novnc/websockify/archive/v0.12.0.tar.gz
plugin: python
stage-packages:
- python3-numpy

View File

@ -1,15 +0,0 @@
{
"env": {
"node": true,
"mocha": true
},
"globals": {
"chai": false,
"sinon": false
},
"rules": {
"prefer-arrow-callback": 0,
// Too many anonymous callbacks
"func-names": "off",
}
}

View File

@ -1,4 +1,3 @@
/* eslint-disable no-console */
const expect = chai.expect;
import { isMac, isWindows, isIOS, isAndroid, isChromeOS,

View File

@ -1,4 +1,3 @@
/* eslint-disable no-console */
const expect = chai.expect;
import { inflateInit, inflate } from "../vendor/pako/lib/zlib/inflate.js";

View File

@ -108,6 +108,8 @@ describe('Helpers', function () {
});
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');

View File

@ -1,4 +1,3 @@
/* eslint-disable no-console */
const expect = chai.expect;
import { deflateInit, deflate, Z_FULL_FLUSH } from "../vendor/pako/lib/zlib/deflate.js";

View File

@ -1,4 +1,3 @@
/* eslint-disable no-console */
const expect = chai.expect;
import { toUnsigned32bit, toSigned32bit } from '../core/util/int.js';

View File

@ -14,6 +14,10 @@ describe('Key Event Handling', function () {
}
e.stopPropagation = sinon.spy();
e.preventDefault = sinon.spy();
e.getModifierState = function (key) {
return e[key];
};
return e;
}
@ -310,6 +314,50 @@ describe('Key Event Handling', function () {
});
});
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 () {

View File

@ -3,7 +3,7 @@ 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 } from "../vendor/pako/lib/zlib/deflate.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';
@ -54,7 +54,7 @@ function deflateWithSize(data) {
let strm = new ZStream();
let chunkSize = 1024 * 10 * 10;
strm.output = new Uint8Array(chunkSize);
deflateInit(strm, 5);
deflateInit(strm, Z_DEFAULT_COMPRESSION);
/* eslint-disable camelcase */
strm.input = unCompData;
@ -1257,6 +1257,14 @@ describe('Remote Frame Buffer Protocol Client', function () {
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 () {
@ -2230,16 +2238,36 @@ describe('Remote Frame Buffer Protocol Client', function () {
});
describe('Legacy SecurityResult', function () {
beforeEach(function () {
sendVer('003.007\n', client);
client._sock._websocket._getSentData();
sendSecurity(1, client);
client._sock._websocket._getSentData();
});
it('should not include reason in securityfailure event', 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);
@ -2979,6 +3007,149 @@ describe('Remote Frame Buffer Protocol Client', function () {
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 () {
@ -3092,11 +3263,11 @@ describe('Remote Frame Buffer Protocol Client', function () {
});
it('should update clipboard with correct escape characters from a Provide message ', function () {
let expectedData = "Oh\nmy!";
let expectedData = "Oh\nmy\n!";
let data = [3, 0, 0, 0];
const flags = [0x10, 0x00, 0x00, 0x01];
let text = encodeUTF8("Oh\r\nmy!\0");
let text = encodeUTF8("Oh\r\nmy\r\n!\0");
let deflatedText = deflateWithSize(text);

View File

@ -228,12 +228,59 @@ describe('Tight Decoder', function () {
expect(display).to.have.displayed(targetData);
});
it.skip('should handle uncompressed gradient rects', function () {
// Not implemented yet
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.skip('should handle compressed gradient rects', function () {
// Not implemented yet
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 () {
@ -275,6 +322,25 @@ describe('Tight Decoder', function () {
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 ]);

View File

@ -1,8 +0,0 @@
{
"env": {
"node": true
},
"rules": {
"no-console": 0
}
}

View File

@ -1,7 +1,7 @@
#!/usr/bin/env node
const path = require('path');
const program = require('commander');
const { program } = require('commander');
const fs = require('fs');
const fse = require('fs-extra');
const babel = require('@babel/core');

View File

@ -8,13 +8,13 @@ usage() {
echo "$*"
echo
fi
echo "Usage: ${NAME} [--listen PORT] [--vnc VNC_HOST:PORT] [--cert CERT] [--ssl-only]"
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 PORT Port for proxy/webserver to listen on"
echo " Default: 6080"
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"
@ -47,14 +47,16 @@ usage() {
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_ARG=""
RECORD=""
SYSLOG_ARG=""
HEARTBEAT_ARG=""
IDLETIMEOUT_ARG=""
@ -86,14 +88,14 @@ cleanup() {
while [ "$*" ]; do
param=$1; shift; OPTARG=$1
case $param in
--listen) PORT="${OPTARG}"; shift ;;
--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_ARG="--record ${OPTARG}"; shift ;;
--record) RECORD="${OPTARG}"; shift ;;
--syslog) SYSLOG_ARG="--syslog ${OPTARG}"; shift ;;
--heartbeat) HEARTBEAT_ARG="--heartbeat ${OPTARG}"; shift ;;
--idle-timeout) IDLETIMEOUT_ARG="--idle-timeout ${OPTARG}"; shift ;;
@ -107,14 +109,23 @@ while [ "$*" ]; do
esac
done
if [ "$LISTEN" != "$PORT" ]; then
HOST=${LISTEN%:*}
PORT=${LISTEN##*:}
# if no host was given, restore
[ "$HOST" = "$PORT" ] && HOST=""
fi
# Sanity checks
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>&-
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
@ -191,9 +202,14 @@ else
fi
fi
echo "Starting webserver and WebSockets proxy on port ${PORT}"
#${HERE}/websockify --web ${WEB} ${CERT:+--cert ${CERT}} ${PORT} ${VNC_DEST} &
${WEBSOCKIFY} ${SYSLOG_ARG} ${SSLONLY} ${FILEONLY_ARG} --web ${WEB} ${CERT:+--cert ${CERT}} ${KEY:+--key ${KEY}} ${PORT} ${VNC_DEST} ${HEARTBEAT_ARG} ${IDLETIMEOUT_ARG} ${RECORD_ARG} ${TIMEOUT_ARG} ${WEBAUTH_ARG} ${AUTHPLUGIN_ARG} ${AUTHSOURCE_ARG} &
# 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
@ -202,11 +218,15 @@ if [ -z "$proxy_pid" ] || ! ps -eo pid= | grep -w "$proxy_pid" > /dev/null; then
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://$(hostname):${PORT}/vnc.html?host=$(hostname)&port=${PORT}\n"
echo -e " http://${HOST}:${PORT}/vnc.html?host=${HOST}&port=${PORT}\n"
else
echo -e " https://$(hostname):${PORT}/vnc.html?host=$(hostname)&port=${PORT}\n"
echo -e " https://${HOST}:${PORT}/vnc.html?host=${HOST}&port=${PORT}\n"
fi
echo -e "Press Ctrl-C to exit\n\n"