Fix VNC connection dropping immediately after connect

- Replace ws_send_frame with ws_send_binary_frame for VNC WebSocket data
- This ensures proper binary frame handling for VNC protocol data
- Fixes immediate disconnection after 'VNC connected' message
parent e4033f9f
# Makefile for wssshd2 - Generated by configure.sh
# Do not edit manually, run ./configure.sh instead
CC = gcc
CFLAGS = -Wall -Wextra -O2 -I. -pthread
LDFLAGS = -lssl -lcrypto -lm -luuid -lsqlite3
PREFIX = /usr/local
BINDIR = $(PREFIX)/bin
MANDIR = $(PREFIX)/share/man
CONFIGDIR = /etc
# Source files
SRCS = main.c config.c tunnel.c terminal.c vnc.c websocket.c websocket_protocol.c web.c assets.c ssl.c
OBJS = $(SRCS:.c=.o)
# Target
TARGET = wssshd
.PHONY: all clean install uninstall
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
# Dependencies
main.o: main.c config.h websocket.h web.h
config.o: config.c config.h
tunnel.o: tunnel.c tunnel.h websocket.h
terminal.o: terminal.c terminal.h config.h
vnc.o: vnc.c vnc.h config.h
websocket.o: websocket.c websocket.h websocket_protocol.h config.h
websocket_protocol.o: websocket_protocol.c websocket_protocol.h
web.o: web.c web.h terminal.h vnc.h assets.h websocket.h html_pages/index_page.h html_pages/login_page.h html_pages/terminal_page.h html_pages/vnc_page.h html_pages/users_page.h
assets.o: assets.c assets.h
ssl.o: ssl.c ssl.h
# Asset embedding (run before compilation)
assets.o: image_data.h
image_data.h: embed_assets.sh
./embed_assets.sh
# HTML page generation
html_pages/index_page.h html_pages/login_page.h html_pages/terminal_page.h html_pages/vnc_page.h: embed_assets.sh
./embed_assets.sh
clean:
rm -f $(OBJS) $(TARGET) image_data.h favicon_data.h
install: $(TARGET)
install -d $(DESTDIR)$(BINDIR)
install -m 755 $(TARGET) $(DESTDIR)$(BINDIR)/
install -d $(DESTDIR)$(CONFIGDIR)
[ -f $(DESTDIR)$(CONFIGDIR)/wssshd.conf ] || install -m 644 wssshd.conf.example $(DESTDIR)$(CONFIGDIR)/wssshd.conf
install -d $(DESTDIR)$(MANDIR)/man8
install -m 644 wssshd.8 $(DESTDIR)$(MANDIR)/man8/
uninstall:
rm -f $(DESTDIR)$(BINDIR)/$(TARGET)
rm -f $(DESTDIR)$(MANDIR)/man8/wssshd.8
# Development targets
debug: CFLAGS += -g -DDEBUG
debug: clean all
test: $(TARGET)
@echo "Running basic functionality test..."
./$(TARGET) --help || true
distclean: clean
rm -f Makefile
# Help target
help:
@echo "Available targets:"
@echo " all - Build wssshd2 (default)"
@echo " clean - Remove build artifacts"
@echo " install - Install wssshd2 to system"
@echo " uninstall - Remove wssshd2 from system"
@echo " debug - Build with debug symbols"
@echo " test - Run basic tests"
@echo " distclean - Remove all generated files"
@echo " help - Show this help"
...@@ -41,6 +41,7 @@ static void set_default_config(wssshd_config_t *config) { ...@@ -41,6 +41,7 @@ static void set_default_config(wssshd_config_t *config) {
config->debug = false; config->debug = false;
config->debug_web = false; config->debug_web = false;
config->debug_database = false; config->debug_database = false;
config->debug_vnc = false;
} }
static void load_config_file(wssshd_config_t *config, const char *config_file) { static void load_config_file(wssshd_config_t *config, const char *config_file) {
...@@ -156,6 +157,7 @@ wssshd_config_t *load_config(int argc, char *argv[]) { ...@@ -156,6 +157,7 @@ wssshd_config_t *load_config(int argc, char *argv[]) {
{"debug", no_argument, 0, 'D'}, {"debug", no_argument, 0, 'D'},
{"debug-web", no_argument, 0, 'E'}, {"debug-web", no_argument, 0, 'E'},
{"debug-database", no_argument, 0, 'F'}, {"debug-database", no_argument, 0, 'F'},
{"debug-vnc", no_argument, 0, 'G'},
{"help", no_argument, 0, '?'}, {"help", no_argument, 0, '?'},
{0, 0, 0, 0} {0, 0, 0, 0}
}; };
...@@ -163,7 +165,7 @@ wssshd_config_t *load_config(int argc, char *argv[]) { ...@@ -163,7 +165,7 @@ wssshd_config_t *load_config(int argc, char *argv[]) {
int opt; int opt;
int option_index = 0; int option_index = 0;
while ((opt = getopt_long(argc, argv, "c:h:p:d:P:w:W:stDEF?", long_options, &option_index)) != -1) { while ((opt = getopt_long(argc, argv, "c:h:p:d:P:w:W:stDEFG?", long_options, &option_index)) != -1) {
switch (opt) { switch (opt) {
case 'c': case 'c':
if (config->config_file) free(config->config_file); if (config->config_file) free(config->config_file);
...@@ -206,6 +208,9 @@ wssshd_config_t *load_config(int argc, char *argv[]) { ...@@ -206,6 +208,9 @@ wssshd_config_t *load_config(int argc, char *argv[]) {
case 'F': case 'F':
config->debug_database = true; config->debug_database = true;
break; break;
case 'G':
config->debug_vnc = true;
break;
case '?': case '?':
printf("Usage: %s [OPTIONS]\n", argv[0]); printf("Usage: %s [OPTIONS]\n", argv[0]);
printf("Options:\n"); printf("Options:\n");
...@@ -221,6 +226,7 @@ wssshd_config_t *load_config(int argc, char *argv[]) { ...@@ -221,6 +226,7 @@ wssshd_config_t *load_config(int argc, char *argv[]) {
printf(" --debug Enable debug output\n"); printf(" --debug Enable debug output\n");
printf(" --debug-web Enable comprehensive web interface debug output\n"); printf(" --debug-web Enable comprehensive web interface debug output\n");
printf(" --debug-database Enable database debug output\n"); printf(" --debug-database Enable database debug output\n");
printf(" --debug-vnc Enable VNC debug output\n");
printf(" --help Show this help\n"); printf(" --help Show this help\n");
free_config(config); free_config(config);
exit(0); exit(0);
...@@ -297,4 +303,5 @@ void print_config(const wssshd_config_t *config) { ...@@ -297,4 +303,5 @@ void print_config(const wssshd_config_t *config) {
printf(" Debug: %s\n", config->debug ? "yes" : "no"); printf(" Debug: %s\n", config->debug ? "yes" : "no");
printf(" Debug Web: %s\n", config->debug_web ? "yes" : "no"); printf(" Debug Web: %s\n", config->debug_web ? "yes" : "no");
printf(" Debug Database: %s\n", config->debug_database ? "yes" : "no"); printf(" Debug Database: %s\n", config->debug_database ? "yes" : "no");
printf(" Debug VNC: %s\n", config->debug_vnc ? "yes" : "no");
} }
\ No newline at end of file
...@@ -36,6 +36,7 @@ typedef struct { ...@@ -36,6 +36,7 @@ typedef struct {
bool debug; bool debug;
bool debug_web; bool debug_web;
bool debug_database; bool debug_database;
bool debug_vnc;
} wssshd_config_t; } wssshd_config_t;
// Function declarations // Function declarations
......
#!/bin/bash
# Script to convert noVNC ES6 modules to global scripts
echo "Converting noVNC files from ES6 modules to global scripts..."
# Function to convert a single file
convert_file() {
local file="$1"
echo "Converting $file..."
# Create a backup
cp "$file" "${file}.bak"
# Remove import statements and convert exports
sed -i '
# Remove import statements
/^import.*from/d
/^export function /{
s/export function /function /
}
/^export const /{
s/export const /const /
}
/^export let /{
s/export let /let /
}
/^export default class /{
s/export default class /class /
}
/^export class /{
s/export class /class /
}
/^export default /{
s/export default /window./
}
/^export {/{
s/export {/window./
s/}/ = {/
s/ as /: /
}
' "$file"
# Add global assignments for exported items
if grep -q "^function " "$file" || grep -q "^const " "$file" || grep -q "^let " "$file" || grep -q "^class " "$file"; then
# Find exported functions/variables and add global assignments
grep "^function \|^const \|^let \|^class " "$file" | while read -r line; do
if [[ $line =~ ^(function|const|let|class)[[:space:]]+([a-zA-Z_][a-zA-Z0-9_]*) ]]; then
name="${BASH_REMATCH[2]}"
# Skip if already assigned to window
if ! grep -q "window\.$name" "$file"; then
echo "window.$name = $name;" >> "$file"
fi
fi
done
fi
}
# Convert all JS files
find templates/novnc -name "*.js" | while read -r file; do
convert_file "$file"
done
echo "Conversion complete!"
\ No newline at end of file
unsigned char favicon_ico[] = {
0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x10, 0x10, 0x00, 0x00, 0x01, 0x00,
0x08, 0x00, 0x68, 0x05, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x20, 0x20,
0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0xa8, 0x10, 0x00, 0x00, 0x8e, 0x05,
0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00,
0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xe9, 0xdc, 0xd4, 0x00, 0xe9, 0xdd,
0xd5, 0x00, 0xea, 0xdd, 0xd5, 0x00, 0xe8, 0xdc, 0xd4, 0x00, 0xea, 0xde,
0xd6, 0x00, 0xeb, 0xde, 0xd6, 0x00, 0xe9, 0xde, 0xd5, 0x00, 0xe8, 0xda,
0xd1, 0x00, 0xea, 0xdd, 0xd6, 0x00, 0xeb, 0xde, 0xd7, 0x00, 0xeb, 0xdd,
0xd5, 0x00, 0xe9, 0xdc, 0xd5, 0x00, 0xf3, 0xe9, 0xe7, 0x00, 0xf1, 0xe5,
0xe2, 0x00, 0xef, 0xe3, 0xde, 0x00, 0xf5, 0xed, 0xed, 0x00, 0xed, 0xdf,
0xd9, 0x00, 0xe8, 0xda, 0xd2, 0x00, 0xf3, 0xea, 0xe9, 0x00, 0xde, 0xcc,
0xbc, 0x00, 0xc8, 0xad, 0x94, 0x00, 0xc8, 0xae, 0x96, 0x00, 0xd4, 0xb9,
0xa3, 0x00, 0xf5, 0xec, 0xeb, 0x00, 0xe8, 0xdb, 0xd2, 0x00, 0xea, 0xdc,
0xd4, 0x00, 0xe9, 0xdd, 0xd4, 0x00, 0xe9, 0xdc, 0xd3, 0x00, 0xf1, 0xe6,
0xe4, 0x00, 0xe7, 0xd6, 0xc9, 0x00, 0xc3, 0xa8, 0x8d, 0x00, 0xd3, 0xce,
0xcb, 0x00, 0xd5, 0xd1, 0xcf, 0x00, 0xc8, 0xb2, 0xa2, 0x00, 0xd7, 0xbe,
0xac, 0x00, 0xfb, 0xf7, 0xfa, 0x00, 0xee, 0xe2, 0xdc, 0x00, 0xec, 0xe2,
0xdb, 0x00, 0xeb, 0xe0, 0xd8, 0x00, 0xeb, 0xe0, 0xd9, 0x00, 0xea, 0xdf,
0xd8, 0x00, 0xec, 0xe2, 0xdc, 0x00, 0xf8, 0xf0, 0xf0, 0x00, 0xd5, 0xba,
0xa4, 0x00, 0xd7, 0xcd, 0xc6, 0x00, 0xc1, 0xb5, 0xae, 0x00, 0xb6, 0xa4,
0x98, 0x00, 0xdc, 0xd9, 0xd8, 0x00, 0xc3, 0xa2, 0x8a, 0x00, 0xd5, 0xbc,
0xac, 0x00, 0xd1, 0xb2, 0x9d, 0x00, 0xc5, 0xa3, 0x8b, 0x00, 0xcd, 0xb1,
0x9e, 0x00, 0xc9, 0xac, 0x97, 0x00, 0xc9, 0xaa, 0x94, 0x00, 0xcd, 0xaf,
0x9a, 0x00, 0xd1, 0xb3, 0x9f, 0x00, 0xe6, 0xd9, 0xd1, 0x00, 0xf8, 0xef,
0xf0, 0x00, 0xdc, 0xc6, 0xb5, 0x00, 0xd0, 0xbf, 0xb1, 0x00, 0xd5, 0xd4,
0xd4, 0x00, 0xcf, 0xc9, 0xc6, 0x00, 0xd7, 0xce, 0xc8, 0x00, 0xd3, 0xb7,
0xa3, 0x00, 0xbe, 0x94, 0x75, 0x00, 0xb3, 0x82, 0x5e, 0x00, 0xc1, 0xa0,
0x88, 0x00, 0xba, 0x94, 0x79, 0x00, 0xb9, 0x94, 0x79, 0x00, 0xb9, 0x92,
0x77, 0x00, 0xae, 0x7d, 0x5a, 0x00, 0xbf, 0x95, 0x78, 0x00, 0xea, 0xdc,
0xd5, 0x00, 0xf2, 0xe4, 0xe1, 0x00, 0xf4, 0xee, 0xed, 0x00, 0xc5, 0xa6,
0x8f, 0x00, 0xcc, 0xb9, 0xaa, 0x00, 0xd3, 0xc4, 0xb8, 0x00, 0xc3, 0xa6,
0x90, 0x00, 0xde, 0xd2, 0xc9, 0x00, 0xdb, 0xce, 0xc6, 0x00, 0xd4, 0xc2,
0xb6, 0x00, 0xcf, 0xb8, 0xaa, 0x00, 0xcb, 0xb0, 0x9e, 0x00, 0xcb, 0xb1,
0xa0, 0x00, 0xcb, 0xb1, 0x9f, 0x00, 0xca, 0xb0, 0x9f, 0x00, 0xe2, 0xd2,
0xca, 0x00, 0xf0, 0xe0, 0xdc, 0x00, 0xf0, 0xdf, 0xdb, 0x00, 0xf5, 0xf2,
0xf1, 0x00, 0xe2, 0xd3, 0xca, 0x00, 0xbf, 0x9b, 0x81, 0x00, 0xcc, 0xba,
0xac, 0x00, 0xee, 0xec, 0xec, 0x00, 0xed, 0xe3, 0xe0, 0x00, 0xf4, 0xea,
0xe9, 0x00, 0xf7, 0xee, 0xef, 0x00, 0xf7, 0xf0, 0xf1, 0x00, 0xf7, 0xed,
0xed, 0x00, 0xf4, 0xe6, 0xe3, 0x00, 0xee, 0xde, 0xd9, 0x00, 0xf0, 0xdf,
0xda, 0x00, 0xf2, 0xe4, 0xe2, 0x00, 0xf4, 0xec, 0xec, 0x00, 0xfe, 0xfe,
0xff, 0x00, 0xe6, 0xe0, 0xdb, 0x00, 0xe2, 0xd7, 0xd1, 0x00, 0xf7, 0xf4,
0xf4, 0x00, 0xf6, 0xed, 0xed, 0x00, 0xf3, 0xe3, 0xe0, 0x00, 0xf2, 0xe2,
0xde, 0x00, 0xf2, 0xe1, 0xde, 0x00, 0xf2, 0xe3, 0xdf, 0x00, 0xf1, 0xe1,
0xde, 0x00, 0xf1, 0xe0, 0xdd, 0x00, 0xee, 0xde, 0xd8, 0x00, 0xef, 0xdf,
0xd9, 0x00, 0xf3, 0xe6, 0xe4, 0x00, 0xf4, 0xeb, 0xeb, 0x00, 0xee, 0xe8,
0xe7, 0x00, 0xf8, 0xf4, 0xf5, 0x00, 0xf5, 0xee, 0xee, 0x00, 0xf4, 0xe8,
0xe7, 0x00, 0xf3, 0xe2, 0xde, 0x00, 0xf2, 0xe1, 0xdd, 0x00, 0xf2, 0xe2,
0xdf, 0x00, 0xf1, 0xe0, 0xdc, 0x00, 0xef, 0xdf, 0xda, 0x00, 0xf3, 0xe4,
0xe2, 0x00, 0xf4, 0xe8, 0xe6, 0x00, 0xf6, 0xed, 0xec, 0x00, 0xf5, 0xeb,
0xea, 0x00, 0xf4, 0xe6, 0xe4, 0x00, 0xf3, 0xe2, 0xdf, 0x00, 0xf1, 0xe0,
0xdb, 0x00, 0xf0, 0xe0, 0xda, 0x00, 0xf1, 0xe1, 0xdd, 0x00, 0xf5, 0xe9,
0xe7, 0x00, 0xf5, 0xea, 0xe8, 0x00, 0xf3, 0xe3, 0xdf, 0x00, 0xf2, 0xe2,
0xdd, 0x00, 0xf3, 0xe4, 0xe0, 0x00, 0xf5, 0xe8, 0xe6, 0x00, 0xf4, 0xe7,
0xe4, 0x00, 0xf4, 0xe4, 0xe0, 0x00, 0xf1, 0xe1, 0xdc, 0x00, 0xf3, 0xe3,
0xdd, 0x00, 0xf4, 0xe4, 0xe1, 0x00, 0xf5, 0xe7, 0xe4, 0x00, 0xf6, 0xea,
0xe8, 0x00, 0xf6, 0xeb, 0xe9, 0x00, 0xf7, 0xec, 0xea, 0x00, 0xf7, 0xea,
0xe8, 0x00, 0xf5, 0xe6, 0xe3, 0x00, 0xf4, 0xe5, 0xe1, 0x00, 0xf5, 0xe4,
0xe1, 0x00, 0xf5, 0xe4, 0xe0, 0x00, 0xf3, 0xe3, 0xde, 0x00, 0xff, 0xff,
0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x94,
0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x92, 0x9f,
0x93, 0x81, 0x89, 0x8e, 0x8f, 0x65, 0x90, 0x8c, 0x8b, 0x91, 0x92, 0x8d,
0x8d, 0x8d, 0x8d, 0x7d, 0x93, 0x75, 0x89, 0x8a, 0x6f, 0x86, 0x8b, 0x85,
0x8c, 0x65, 0x8d, 0x7d, 0x70, 0x87, 0x87, 0x70, 0x80, 0x75, 0x81, 0x7e,
0x82, 0x83, 0x84, 0x0f, 0x85, 0x86, 0x87, 0x7d, 0x70, 0x70, 0x7f, 0x70,
0x88, 0x75, 0x76, 0x70, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x6f, 0x7d,
0x7e, 0x70, 0x7f, 0x7f, 0x80, 0x75, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x72, 0x73, 0x74, 0x75, 0x5a, 0x0f,
0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x0d, 0x61, 0x62, 0x63, 0x63, 0x64,
0x65, 0x66, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53,
0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x2a, 0x2b,
0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
0x26, 0x27, 0x28, 0x27, 0x29, 0x03, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
0x17, 0x10, 0x18, 0x19, 0x1a, 0x00, 0x19, 0x00, 0x1b, 0x0b, 0x00, 0x00,
0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x00, 0x04, 0x05, 0x02, 0x02, 0x05, 0x02,
0x01, 0x0b, 0x02, 0x01, 0x07, 0x08, 0x09, 0x07, 0x00, 0x02, 0x0a, 0x02,
0x05, 0x0a, 0x02, 0x05, 0x02, 0x0b, 0x01, 0x04, 0x04, 0x02, 0x02, 0x04,
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x01, 0x00, 0x01,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x20, 0x00,
0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xe0,
0xda, 0xff, 0xf1, 0xe1, 0xdb, 0xff, 0xf3, 0xe2, 0xdd, 0xff, 0xf4, 0xe3,
0xdf, 0xff, 0xf4, 0xe4, 0xe0, 0xff, 0xf5, 0xe5, 0xe1, 0xff, 0xf6, 0xe7,
0xe3, 0xff, 0xf6, 0xe8, 0xe5, 0xff, 0xf6, 0xea, 0xe8, 0xff, 0xf7, 0xeb,
0xe9, 0xff, 0xf7, 0xeb, 0xe9, 0xff, 0xf7, 0xeb, 0xea, 0xff, 0xf8, 0xed,
0xec, 0xff, 0xf7, 0xed, 0xec, 0xff, 0xf7, 0xec, 0xeb, 0xff, 0xf7, 0xea,
0xe8, 0xff, 0xf6, 0xe8, 0xe5, 0xff, 0xf5, 0xe6, 0xe2, 0xff, 0xf5, 0xe5,
0xe1, 0xff, 0xf5, 0xe5, 0xe1, 0xff, 0xf5, 0xe5, 0xe0, 0xff, 0xf5, 0xe5,
0xe1, 0xff, 0xf5, 0xe5, 0xe1, 0xff, 0xf5, 0xe4, 0xe0, 0xff, 0xf5, 0xe4,
0xe1, 0xff, 0xf5, 0xe4, 0xe0, 0xff, 0xf4, 0xe3, 0xde, 0xff, 0xf3, 0xe3,
0xde, 0xff, 0xf2, 0xe2, 0xdc, 0xff, 0xf1, 0xe1, 0xdc, 0xff, 0xf0, 0xe1,
0xdc, 0xff, 0xee, 0xe0, 0xda, 0xff, 0xef, 0xe0, 0xda, 0xff, 0xf1, 0xe1,
0xdb, 0xff, 0xf2, 0xe2, 0xdc, 0xff, 0xf3, 0xe3, 0xde, 0xff, 0xf4, 0xe4,
0xe0, 0xff, 0xf4, 0xe5, 0xe1, 0xff, 0xf4, 0xe6, 0xe3, 0xff, 0xf5, 0xe7,
0xe4, 0xff, 0xf5, 0xe9, 0xe6, 0xff, 0xf6, 0xea, 0xe8, 0xff, 0xf6, 0xea,
0xe8, 0xff, 0xf6, 0xeb, 0xe9, 0xff, 0xf6, 0xeb, 0xe9, 0xff, 0xf6, 0xeb,
0xe9, 0xff, 0xf6, 0xea, 0xe8, 0xff, 0xf6, 0xe8, 0xe6, 0xff, 0xf5, 0xe6,
0xe3, 0xff, 0xf5, 0xe5, 0xe1, 0xff, 0xf4, 0xe4, 0xe1, 0xff, 0xf4, 0xe4,
0xe0, 0xff, 0xf4, 0xe4, 0xe0, 0xff, 0xf4, 0xe4, 0xe1, 0xff, 0xf4, 0xe4,
0xe0, 0xff, 0xf5, 0xe4, 0xe0, 0xff, 0xf4, 0xe3, 0xe0, 0xff, 0xf4, 0xe3,
0xe0, 0xff, 0xf3, 0xe3, 0xdf, 0xff, 0xf3, 0xe3, 0xde, 0xff, 0xf2, 0xe2,
0xdc, 0xff, 0xf1, 0xe1, 0xdb, 0xff, 0xef, 0xe0, 0xda, 0xff, 0xed, 0xde,
0xd8, 0xff, 0xf0, 0xe0, 0xd9, 0xff, 0xf1, 0xe1, 0xdb, 0xff, 0xf1, 0xe2,
0xdc, 0xff, 0xf2, 0xe2, 0xde, 0xff, 0xf3, 0xe3, 0xe0, 0xff, 0xf4, 0xe4,
0xe0, 0xff, 0xf4, 0xe5, 0xe2, 0xff, 0xf5, 0xe7, 0xe4, 0xff, 0xf5, 0xe7,
0xe4, 0xff, 0xf5, 0xe9, 0xe7, 0xff, 0xf5, 0xe9, 0xe7, 0xff, 0xf5, 0xe9,
0xe7, 0xff, 0xf5, 0xe9, 0xe7, 0xff, 0xf6, 0xe9, 0xe7, 0xff, 0xf5, 0xe9,
0xe7, 0xff, 0xf4, 0xe7, 0xe4, 0xff, 0xf4, 0xe5, 0xe1, 0xff, 0xf4, 0xe4,
0xe0, 0xff, 0xf3, 0xe4, 0xe0, 0xff, 0xf3, 0xe3, 0xdf, 0xff, 0xf3, 0xe3,
0xdf, 0xff, 0xf3, 0xe3, 0xdf, 0xff, 0xf4, 0xe4, 0xe0, 0xff, 0xf3, 0xe3,
0xdf, 0xff, 0xf3, 0xe3, 0xe0, 0xff, 0xf4, 0xe3, 0xdf, 0xff, 0xf3, 0xe2,
0xdf, 0xff, 0xf3, 0xe2, 0xdf, 0xff, 0xf2, 0xe1, 0xdd, 0xff, 0xf0, 0xe0,
0xda, 0xff, 0xef, 0xdf, 0xd9, 0xff, 0xed, 0xde, 0xd8, 0xff, 0xef, 0xdf,
0xd9, 0xff, 0xf0, 0xe1, 0xdb, 0xff, 0xf1, 0xe1, 0xdc, 0xff, 0xf2, 0xe2,
0xde, 0xff, 0xf3, 0xe3, 0xdf, 0xff, 0xf4, 0xe4, 0xe1, 0xff, 0xf4, 0xe5,
0xe2, 0xff, 0xf4, 0xe6, 0xe3, 0xff, 0xf5, 0xe8, 0xe5, 0xff, 0xf5, 0xe8,
0xe6, 0xff, 0xf5, 0xea, 0xe9, 0xff, 0xf5, 0xeb, 0xea, 0xff, 0xf5, 0xe9,
0xe7, 0xff, 0xf4, 0xe8, 0xe6, 0xff, 0xf4, 0xe7, 0xe5, 0xff, 0xf4, 0xe5,
0xe2, 0xff, 0xf3, 0xe4, 0xe0, 0xff, 0xf3, 0xe3, 0xdf, 0xff, 0xf3, 0xe3,
0xde, 0xff, 0xf3, 0xe3, 0xdf, 0xff, 0xf3, 0xe3, 0xde, 0xff, 0xf2, 0xe2,
0xde, 0xff, 0xf3, 0xe3, 0xde, 0xff, 0xf3, 0xe3, 0xde, 0xff, 0xf3, 0xe3,
0xdf, 0xff, 0xf3, 0xe3, 0xdf, 0xff, 0xf3, 0xe2, 0xde, 0xff, 0xf2, 0xe2,
0xdd, 0xff, 0xf2, 0xe1, 0xdd, 0xff, 0xf1, 0xe1, 0xdb, 0xff, 0xef, 0xdf,
0xd9, 0xff, 0xed, 0xde, 0xd7, 0xff, 0xef, 0xdf, 0xd9, 0xff, 0xf0, 0xe0,
0xdb, 0xff, 0xf1, 0xe1, 0xdc, 0xff, 0xf2, 0xe2, 0xdd, 0xff, 0xf3, 0xe3,
0xdf, 0xff, 0xf4, 0xe4, 0xe1, 0xff, 0xf4, 0xe5, 0xe2, 0xff, 0xf4, 0xe7,
0xe4, 0xff, 0xf5, 0xe8, 0xe6, 0xff, 0xf5, 0xe9, 0xe8, 0xff, 0xf5, 0xeb,
0xea, 0xff, 0xf6, 0xeb, 0xea, 0xff, 0xf5, 0xea, 0xe8, 0xff, 0xf5, 0xe9,
0xe7, 0xff, 0xf4, 0xe7, 0xe4, 0xff, 0xf4, 0xe5, 0xe2, 0xff, 0xf4, 0xe3,
0xe0, 0xff, 0xf3, 0xe2, 0xdf, 0xff, 0xf3, 0xe2, 0xde, 0xff, 0xf3, 0xe2,
0xde, 0xff, 0xf2, 0xe2, 0xdd, 0xff, 0xf3, 0xe3, 0xde, 0xff, 0xf3, 0xe3,
0xdf, 0xff, 0xf3, 0xe3, 0xdf, 0xff, 0xf3, 0xe2, 0xdf, 0xff, 0xf3, 0xe2,
0xde, 0xff, 0xf3, 0xe2, 0xdf, 0xff, 0xf2, 0xe1, 0xdd, 0xff, 0xf2, 0xe1,
0xdc, 0xff, 0xf1, 0xe0, 0xdc, 0xff, 0xef, 0xdf, 0xd9, 0xff, 0xed, 0xdd,
0xd7, 0xff, 0xef, 0xdf, 0xd9, 0xff, 0xf1, 0xe0, 0xdb, 0xff, 0xf1, 0xe1,
0xdc, 0xff, 0xf1, 0xe1, 0xdd, 0xff, 0xf3, 0xe3, 0xe0, 0xff, 0xf3, 0xe4,
0xe1, 0xff, 0xf4, 0xe6, 0xe3, 0xff, 0xf4, 0xe8, 0xe6, 0xff, 0xf5, 0xe9,
0xe8, 0xff, 0xf5, 0xea, 0xe8, 0xff, 0xf5, 0xeb, 0xea, 0xff, 0xf6, 0xeb,
0xea, 0xff, 0xf5, 0xeb, 0xea, 0xff, 0xf5, 0xe9, 0xe8, 0xff, 0xf4, 0xe7,
0xe4, 0xff, 0xf4, 0xe5, 0xe2, 0xff, 0xf3, 0xe3, 0xe0, 0xff, 0xf3, 0xe2,
0xdf, 0xff, 0xf2, 0xe2, 0xde, 0xff, 0xf2, 0xe2, 0xde, 0xff, 0xf2, 0xe2,
0xde, 0xff, 0xf2, 0xe2, 0xde, 0xff, 0xf2, 0xe2, 0xde, 0xff, 0xf3, 0xe2,
0xdf, 0xff, 0xf3, 0xe2, 0xde, 0xff, 0xf3, 0xe2, 0xde, 0xff, 0xf3, 0xe2,
0xdf, 0xff, 0xf2, 0xe1, 0xdd, 0xff, 0xf1, 0xe0, 0xdc, 0xff, 0xf1, 0xe0,
0xdc, 0xff, 0xf0, 0xdf, 0xd9, 0xff, 0xed, 0xdd, 0xd7, 0xff, 0xef, 0xdf,
0xd9, 0xff, 0xf0, 0xe0, 0xdb, 0xff, 0xf1, 0xe1, 0xdc, 0xff, 0xf2, 0xe1,
0xde, 0xff, 0xf3, 0xe3, 0xe0, 0xff, 0xf3, 0xe5, 0xe2, 0xff, 0xf3, 0xe6,
0xe4, 0xff, 0xf4, 0xe8, 0xe7, 0xff, 0xf5, 0xe9, 0xe9, 0xff, 0xf5, 0xeb,
0xeb, 0xff, 0xf5, 0xed, 0xed, 0xff, 0xf5, 0xed, 0xed, 0xff, 0xf5, 0xec,
0xeb, 0xff, 0xf5, 0xe9, 0xe8, 0xff, 0xf4, 0xe7, 0xe5, 0xff, 0xf4, 0xe5,
0xe2, 0xff, 0xf3, 0xe3, 0xdf, 0xff, 0xf3, 0xe2, 0xde, 0xff, 0xf3, 0xe2,
0xde, 0xff, 0xf3, 0xe2, 0xde, 0xff, 0xf3, 0xe2, 0xde, 0xff, 0xf2, 0xe1,
0xdd, 0xff, 0xf2, 0xe1, 0xde, 0xff, 0xf3, 0xe2, 0xde, 0xff, 0xf2, 0xe2,
0xde, 0xff, 0xf2, 0xe2, 0xde, 0xff, 0xf2, 0xe2, 0xdf, 0xff, 0xf2, 0xe2,
0xde, 0xff, 0xf1, 0xe0, 0xdc, 0xff, 0xf1, 0xdf, 0xdb, 0xff, 0xf0, 0xde,
0xd8, 0xff, 0xee, 0xdd, 0xd7, 0xff, 0xef, 0xdf, 0xd9, 0xff, 0xf0, 0xe0,
0xdb, 0xff, 0xf1, 0xe1, 0xdd, 0xff, 0xf2, 0xe2, 0xde, 0xff, 0xf3, 0xe4,
0xe1, 0xff, 0xf4, 0xe6, 0xe3, 0xff, 0xf4, 0xe8, 0xe6, 0xff, 0xf5, 0xeb,
0xea, 0xff, 0xf4, 0xeb, 0xeb, 0xff, 0xf6, 0xef, 0xef, 0xff, 0xf6, 0xef,
0xee, 0xff, 0xf6, 0xef, 0xee, 0xff, 0xf5, 0xed, 0xec, 0xff, 0xf5, 0xe9,
0xe8, 0xff, 0xf4, 0xe7, 0xe5, 0xff, 0xf4, 0xe6, 0xe3, 0xff, 0xf3, 0xe3,
0xe0, 0xff, 0xf3, 0xe2, 0xdf, 0xff, 0xf2, 0xe2, 0xde, 0xff, 0xf3, 0xe2,
0xde, 0xff, 0xf2, 0xe2, 0xdd, 0xff, 0xf2, 0xe2, 0xdd, 0xff, 0xf2, 0xe1,
0xde, 0xff, 0xf2, 0xe2, 0xdf, 0xff, 0xf2, 0xe2, 0xdf, 0xff, 0xf3, 0xe2,
0xdf, 0xff, 0xf2, 0xe2, 0xde, 0xff, 0xf2, 0xe1, 0xde, 0xff, 0xf2, 0xe1,
0xdd, 0xff, 0xf1, 0xdf, 0xda, 0xff, 0xef, 0xde, 0xd8, 0xff, 0xed, 0xdd,
0xd7, 0xff, 0xee, 0xde, 0xd8, 0xff, 0xf0, 0xdf, 0xdb, 0xff, 0xf1, 0xe0,
0xdd, 0xff, 0xf2, 0xe2, 0xdf, 0xff, 0xf3, 0xe4, 0xe2, 0xff, 0xf4, 0xe7,
0xe4, 0xff, 0xf5, 0xea, 0xe8, 0xff, 0xf5, 0xec, 0xec, 0xff, 0xf5, 0xee,
0xee, 0xff, 0xf2, 0xed, 0xec, 0xff, 0xf6, 0xf0, 0xf0, 0xff, 0xf5, 0xee,
0xee, 0xff, 0xf5, 0xee, 0xee, 0xff, 0xf5, 0xed, 0xed, 0xff, 0xf5, 0xea,
0xe8, 0xff, 0xf4, 0xe7, 0xe5, 0xff, 0xf3, 0xe4, 0xe1, 0xff, 0xf3, 0xe2,
0xdf, 0xff, 0xf3, 0xe2, 0xde, 0xff, 0xf2, 0xe2, 0xde, 0xff, 0xf2, 0xe2,
0xdd, 0xff, 0xf2, 0xe1, 0xdd, 0xff, 0xf2, 0xe2, 0xde, 0xff, 0xf2, 0xe2,
0xdf, 0xff, 0xf3, 0xe2, 0xdf, 0xff, 0xf2, 0xe2, 0xdf, 0xff, 0xf2, 0xe2,
0xdf, 0xff, 0xf2, 0xe2, 0xde, 0xff, 0xf1, 0xe1, 0xdc, 0xff, 0xf0, 0xdf,
0xda, 0xff, 0xef, 0xdf, 0xd9, 0xff, 0xed, 0xde, 0xd8, 0xff, 0xee, 0xde,
0xd8, 0xff, 0xf0, 0xdf, 0xdb, 0xff, 0xf1, 0xe1, 0xde, 0xff, 0xf2, 0xe4,
0xe0, 0xff, 0xf3, 0xe6, 0xe3, 0xff, 0xf4, 0xe9, 0xe8, 0xff, 0xf4, 0xec,
0xec, 0xff, 0xf6, 0xf1, 0xf1, 0xff, 0xf1, 0xec, 0xec, 0xff, 0xdd, 0xd6,
0xd2, 0xff, 0xf7, 0xf3, 0xf3, 0xff, 0xf6, 0xf1, 0xf1, 0xff, 0xf6, 0xf1,
0xf1, 0xff, 0xf5, 0xee, 0xee, 0xff, 0xf5, 0xeb, 0xea, 0xff, 0xf4, 0xe7,
0xe5, 0xff, 0xf3, 0xe4, 0xe1, 0xff, 0xf2, 0xe3, 0xdf, 0xff, 0xf3, 0xe2,
0xdf, 0xff, 0xf2, 0xe2, 0xde, 0xff, 0xf2, 0xe1, 0xdd, 0xff, 0xf2, 0xe1,
0xdd, 0xff, 0xf3, 0xe2, 0xde, 0xff, 0xf2, 0xe2, 0xdf, 0xff, 0xf3, 0xe2,
0xdf, 0xff, 0xf2, 0xe2, 0xdf, 0xff, 0xf2, 0xe2, 0xdf, 0xff, 0xf2, 0xe2,
0xdf, 0xff, 0xf2, 0xe1, 0xde, 0xff, 0xf0, 0xe0, 0xdb, 0xff, 0xef, 0xdf,
0xd9, 0xff, 0xed, 0xdd, 0xd7, 0xff, 0xef, 0xdf, 0xd9, 0xff, 0xf0, 0xe0,
0xdb, 0xff, 0xf1, 0xe2, 0xde, 0xff, 0xf3, 0xe6, 0xe3, 0xff, 0xf4, 0xea,
0xe9, 0xff, 0xf4, 0xed, 0xec, 0xff, 0xf5, 0xef, 0xef, 0xff, 0xf9, 0xf6,
0xf8, 0xff, 0xec, 0xe5, 0xe3, 0xff, 0xd9, 0xce, 0xc6, 0xff, 0xff, 0xff,
0xff, 0xff, 0xf3, 0xef, 0xef, 0xff, 0xf4, 0xef, 0xef, 0xff, 0xf6, 0xf0,
0xf0, 0xff, 0xf5, 0xec, 0xec, 0xff, 0xf4, 0xe7, 0xe6, 0xff, 0xf3, 0xe4,
0xe1, 0xff, 0xf2, 0xe2, 0xdf, 0xff, 0xf3, 0xe2, 0xde, 0xff, 0xf3, 0xe2,
0xde, 0xff, 0xf2, 0xe2, 0xdf, 0xff, 0xf3, 0xe2, 0xde, 0xff, 0xf3, 0xe2,
0xdf, 0xff, 0xf3, 0xe3, 0xe0, 0xff, 0xf3, 0xe3, 0xe0, 0xff, 0xf2, 0xe3,
0xe0, 0xff, 0xf2, 0xe2, 0xdf, 0xff, 0xf2, 0xe2, 0xdf, 0xff, 0xf1, 0xe2,
0xdf, 0xff, 0xf1, 0xe0, 0xdc, 0xff, 0xef, 0xdf, 0xd9, 0xff, 0xed, 0xdd,
0xd7, 0xff, 0xee, 0xde, 0xd8, 0xff, 0xf1, 0xe1, 0xdd, 0xff, 0xf2, 0xe4,
0xe1, 0xff, 0xf3, 0xe8, 0xe7, 0xff, 0xf4, 0xed, 0xed, 0xff, 0xf5, 0xef,
0xef, 0xff, 0xf5, 0xf0, 0xf0, 0xff, 0xf7, 0xf3, 0xf3, 0xff, 0xf0, 0xeb,
0xe9, 0xff, 0xd1, 0xc4, 0xb9, 0xff, 0xd0, 0xba, 0xab, 0xff, 0xcc, 0xbd,
0xb1, 0xff, 0xf3, 0xf1, 0xf2, 0xff, 0xf5, 0xef, 0xef, 0xff, 0xf4, 0xed,
0xec, 0xff, 0xf4, 0xe9, 0xe8, 0xff, 0xf3, 0xe5, 0xe2, 0xff, 0xf2, 0xe2,
0xdf, 0xff, 0xf3, 0xe3, 0xdf, 0xff, 0xf3, 0xe3, 0xe0, 0xff, 0xf3, 0xe2,
0xe0, 0xff, 0xf2, 0xe3, 0xe0, 0xff, 0xf3, 0xe4, 0xe2, 0xff, 0xf3, 0xe4,
0xe2, 0xff, 0xf3, 0xe5, 0xe2, 0xff, 0xf2, 0xe5, 0xe2, 0xff, 0xf2, 0xe4,
0xe1, 0xff, 0xf2, 0xe3, 0xe0, 0xff, 0xf1, 0xe2, 0xdf, 0xff, 0xf1, 0xe1,
0xdd, 0xff, 0xf0, 0xdf, 0xda, 0xff, 0xed, 0xdd, 0xd7, 0xff, 0xef, 0xde,
0xd9, 0xff, 0xf1, 0xe1, 0xde, 0xff, 0xf3, 0xe7, 0xe5, 0xff, 0xf5, 0xed,
0xed, 0xff, 0xf4, 0xef, 0xef, 0xff, 0xf7, 0xf2, 0xf3, 0xff, 0xfd, 0xfd,
0xfe, 0xff, 0xfd, 0xfd, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb6, 0x92,
0x78, 0xff, 0xb4, 0x92, 0x7a, 0xff, 0xf7, 0xf7, 0xf8, 0xff, 0xfc, 0xfa,
0xfb, 0xff, 0xfa, 0xf7, 0xf9, 0xff, 0xf8, 0xf2, 0xf3, 0xff, 0xf5, 0xeb,
0xea, 0xff, 0xf3, 0xe5, 0xe3, 0xff, 0xf1, 0xe2, 0xdf, 0xff, 0xf1, 0xe3,
0xe0, 0xff, 0xf1, 0xe4, 0xe1, 0xff, 0xf2, 0xe4, 0xe2, 0xff, 0xf3, 0xe6,
0xe5, 0xff, 0xf2, 0xe7, 0xe6, 0xff, 0xf2, 0xe7, 0xe6, 0xff, 0xf3, 0xe8,
0xe6, 0xff, 0xf2, 0xe6, 0xe4, 0xff, 0xf2, 0xe4, 0xe2, 0xff, 0xf2, 0xe4,
0xe1, 0xff, 0xf1, 0xe2, 0xdf, 0xff, 0xf1, 0xe1, 0xde, 0xff, 0xf0, 0xdf,
0xdb, 0xff, 0xec, 0xdd, 0xd7, 0xff, 0xef, 0xdf, 0xd9, 0xff, 0xf2, 0xe3,
0xe0, 0xff, 0xf3, 0xea, 0xe9, 0xff, 0xf4, 0xee, 0xee, 0xff, 0xf9, 0xf7,
0xf7, 0xff, 0xf6, 0xf7, 0xf7, 0xff, 0xd3, 0xbe, 0xb0, 0xff, 0xbc, 0x97,
0x7e, 0xff, 0xb7, 0x8b, 0x6b, 0xff, 0xa0, 0x60, 0x33, 0xff, 0xd3, 0xca,
0xc4, 0xff, 0xdb, 0xda, 0xda, 0xff, 0xd5, 0xca, 0xc3, 0xff, 0xde, 0xd4,
0xce, 0xff, 0xe9, 0xe0, 0xdc, 0xff, 0xf1, 0xe9, 0xe7, 0xff, 0xf6, 0xed,
0xec, 0xff, 0xf8, 0xef, 0xf0, 0xff, 0xfa, 0xf4, 0xf6, 0xff, 0xfa, 0xf5,
0xf8, 0xff, 0xfb, 0xf7, 0xf9, 0xff, 0xfc, 0xf8, 0xfb, 0xff, 0xfb, 0xf6,
0xf9, 0xff, 0xfb, 0xf7, 0xfa, 0xff, 0xfb, 0xf8, 0xfb, 0xff, 0xfa, 0xf5,
0xf8, 0xff, 0xfa, 0xf4, 0xf6, 0xff, 0xfa, 0xf3, 0xf4, 0xff, 0xf7, 0xed,
0xed, 0xff, 0xf1, 0xe2, 0xdf, 0xff, 0xf0, 0xdf, 0xda, 0xff, 0xec, 0xdd,
0xd7, 0xff, 0xf0, 0xe0, 0xda, 0xff, 0xf3, 0xe5, 0xe2, 0xff, 0xf4, 0xec,
0xeb, 0xff, 0xf9, 0xf8, 0xf9, 0xff, 0xeb, 0xe5, 0xe1, 0xff, 0xb2, 0x7f,
0x5a, 0xff, 0xb2, 0x83, 0x60, 0xff, 0xc8, 0xad, 0x99, 0xff, 0xc0, 0xa9,
0x96, 0xff, 0xc7, 0xa9, 0x92, 0xff, 0xaa, 0x76, 0x50, 0xff, 0xcb, 0xae,
0x9a, 0xff, 0xef, 0xef, 0xf1, 0xff, 0xd8, 0xcc, 0xc3, 0xff, 0xcc, 0xb9,
0xac, 0xff, 0xca, 0xb6, 0xaa, 0xff, 0xc9, 0xb2, 0xa3, 0xff, 0xce, 0xb5,
0xa6, 0xff, 0xcc, 0xb4, 0xa4, 0xff, 0xd5, 0xc2, 0xb6, 0xff, 0xd5, 0xbf,
0xb1, 0xff, 0xd1, 0xb9, 0xaa, 0xff, 0xd7, 0xc6, 0xbc, 0xff, 0xd6, 0xc3,
0xb8, 0xff, 0xd6, 0xc2, 0xb6, 0xff, 0xdc, 0xcc, 0xc2, 0xff, 0xd7, 0xc3,
0xb8, 0xff, 0xd9, 0xc3, 0xb7, 0xff, 0xe1, 0xcf, 0xc5, 0xff, 0xf4, 0xe9,
0xe8, 0xff, 0xf0, 0xe0, 0xdb, 0xff, 0xec, 0xdc, 0xd6, 0xff, 0xf1, 0xe0,
0xdc, 0xff, 0xf3, 0xe6, 0xe5, 0xff, 0xf6, 0xf1, 0xf1, 0xff, 0xf3, 0xf1,
0xf0, 0xff, 0xac, 0x74, 0x49, 0xff, 0xb3, 0x95, 0x80, 0xff, 0xf5, 0xfc,
0xff, 0xff, 0xee, 0xf2, 0xf4, 0xff, 0xd5, 0xd4, 0xd4, 0xff, 0xf6, 0xfa,
0xfe, 0xff, 0xea, 0xea, 0xe9, 0xff, 0xa0, 0x6d, 0x47, 0xff, 0xcd, 0xb0,
0x9b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xe9, 0xe5, 0xff, 0xdb, 0xcc,
0xc2, 0xff, 0xe2, 0xd9, 0xd4, 0xff, 0xd1, 0xbd, 0xb0, 0xff, 0xda, 0xca,
0xc1, 0xff, 0xb0, 0x87, 0x69, 0xff, 0xb9, 0x96, 0x7c, 0xff, 0xcc, 0xb4,
0xa3, 0xff, 0xa8, 0x7a, 0x59, 0xff, 0xbb, 0x9a, 0x82, 0xff, 0xc4, 0xa7,
0x92, 0xff, 0xa6, 0x76, 0x54, 0xff, 0xc0, 0xa1, 0x8c, 0xff, 0xba, 0x97,
0x7f, 0xff, 0xc5, 0xab, 0x98, 0xff, 0xdf, 0xd0, 0xc8, 0xff, 0xf3, 0xe3,
0xe0, 0xff, 0xea, 0xdb, 0xd4, 0xff, 0xf2, 0xe1, 0xdd, 0xff, 0xf3, 0xe7,
0xe5, 0xff, 0xfd, 0xfe, 0xff, 0xff, 0xca, 0xa7, 0x8a, 0xff, 0xbd, 0x94,
0x77, 0xff, 0xe3, 0xea, 0xf0, 0xff, 0xc8, 0xbf, 0xba, 0xff, 0xd9, 0xd5,
0xd1, 0xff, 0xc9, 0xc2, 0xbd, 0xff, 0xd7, 0xd2, 0xce, 0xff, 0xce, 0xc9,
0xc7, 0xff, 0xd5, 0xd1, 0xcd, 0xff, 0xab, 0x73, 0x48, 0xff, 0xf2, 0xf1,
0xf1, 0xff, 0xcd, 0xae, 0x98, 0xff, 0xa0, 0x5e, 0x2f, 0xff, 0xbf, 0x9a,
0x7f, 0xff, 0xb0, 0x78, 0x51, 0xff, 0xe2, 0xdb, 0xd7, 0xff, 0xbf, 0x9f,
0x88, 0xff, 0xab, 0x75, 0x4f, 0xff, 0xcb, 0xb4, 0xa4, 0xff, 0xc7, 0xad,
0x99, 0xff, 0xac, 0x78, 0x54, 0xff, 0xc4, 0xa9, 0x96, 0xff, 0xc1, 0x9d,
0x84, 0xff, 0xa9, 0x74, 0x50, 0xff, 0xba, 0x92, 0x78, 0xff, 0xda, 0xc6,
0xb9, 0xff, 0xbd, 0x90, 0x71, 0xff, 0xf5, 0xea, 0xe9, 0xff, 0xe8, 0xd9,
0xd1, 0xff, 0xf2, 0xe2, 0xde, 0xff, 0xf3, 0xe9, 0xe8, 0xff, 0xf8, 0xf9,
0xfa, 0xff, 0xb9, 0x84, 0x5a, 0xff, 0xe0, 0xd4, 0xcc, 0xff, 0xf4, 0xf7,
0xf9, 0xff, 0xce, 0xca, 0xc6, 0xff, 0xbe, 0xae, 0xa4, 0xff, 0xc2, 0xb2,
0xa8, 0xff, 0xc2, 0xb4, 0xab, 0xff, 0xd8, 0xd3, 0xd0, 0xff, 0xfd, 0xff,
0xff, 0xff, 0xbd, 0x91, 0x72, 0xff, 0xd6, 0xbe, 0xac, 0xff, 0xc7, 0xa4,
0x89, 0xff, 0xac, 0x77, 0x51, 0xff, 0xa8, 0x6e, 0x43, 0xff, 0xaf, 0x7c,
0x56, 0xff, 0xda, 0xc9, 0xbe, 0xff, 0xbe, 0x99, 0x7e, 0xff, 0xb7, 0x8a,
0x68, 0xff, 0xd7, 0xc6, 0xba, 0xff, 0xbe, 0x9a, 0x81, 0xff, 0xb8, 0x8d,
0x6d, 0xff, 0xd5, 0xc2, 0xb5, 0xff, 0xba, 0x90, 0x73, 0xff, 0xb9, 0x91,
0x73, 0xff, 0xad, 0x74, 0x4e, 0xff, 0xa6, 0x68, 0x3c, 0xff, 0xbe, 0x8d,
0x6c, 0xff, 0xf0, 0xe7, 0xe4, 0xff, 0xe6, 0xd8, 0xd1, 0xff, 0xf1, 0xe1,
0xdd, 0xff, 0xf4, 0xeb, 0xea, 0xff, 0xf3, 0xee, 0xec, 0xff, 0xb3, 0x7e,
0x53, 0xff, 0xd0, 0xc8, 0xc3, 0xff, 0xd1, 0xcd, 0xcb, 0xff, 0xc8, 0xbc,
0xb3, 0xff, 0xb8, 0xa7, 0x9d, 0xff, 0xb6, 0xb2, 0xaf, 0xff, 0xc1, 0xae,
0xa0, 0xff, 0xd4, 0xcd, 0xc8, 0xff, 0xe1, 0xe2, 0xe4, 0xff, 0xc1, 0x9e,
0x85, 0xff, 0xbf, 0x98, 0x7a, 0xff, 0xbc, 0x96, 0x7a, 0xff, 0xce, 0xb0,
0x9c, 0xff, 0xb2, 0x7a, 0x50, 0xff, 0xca, 0xaa, 0x93, 0xff, 0xa8, 0x73,
0x4d, 0xff, 0xa5, 0x6c, 0x43, 0xff, 0xcb, 0xb4, 0xa2, 0xff, 0xc1, 0xa1,
0x8a, 0xff, 0x9e, 0x63, 0x39, 0xff, 0xce, 0xba, 0xaa, 0xff, 0xb2, 0x84,
0x64, 0xff, 0xa7, 0x72, 0x4b, 0xff, 0xd1, 0xbc, 0xae, 0xff, 0xb7, 0x87,
0x68, 0xff, 0xd0, 0xb4, 0xa1, 0xff, 0xb3, 0x83, 0x5f, 0xff, 0xee, 0xe6,
0xe1, 0xff, 0xe6, 0xd9, 0xd1, 0xff, 0xef, 0xe0, 0xdc, 0xff, 0xf4, 0xe9,
0xe7, 0xff, 0xf5, 0xf3, 0xf4, 0xff, 0xb6, 0x81, 0x55, 0xff, 0xd7, 0xcc,
0xc5, 0xff, 0xe5, 0xe6, 0xe5, 0xff, 0xce, 0xc7, 0xc2, 0xff, 0xb5, 0xa1,
0x93, 0xff, 0xb1, 0x9f, 0x92, 0xff, 0xc3, 0xb3, 0xa9, 0xff, 0xcf, 0xc8,
0xc3, 0xff, 0xde, 0xdf, 0xe1, 0xff, 0xb2, 0x89, 0x6b, 0xff, 0xc9, 0xa8,
0x8f, 0xff, 0xee, 0xea, 0xea, 0xff, 0xed, 0xe3, 0xe0, 0xff, 0xe3, 0xd0,
0xc7, 0xff, 0xe6, 0xd6, 0xcd, 0xff, 0xde, 0xcd, 0xc1, 0xff, 0xd2, 0xb7,
0xa4, 0xff, 0xcc, 0xae, 0x98, 0xff, 0xe6, 0xda, 0xd2, 0xff, 0xcd, 0xb2,
0x9e, 0xff, 0xd2, 0xb8, 0xa5, 0xff, 0xe3, 0xd5, 0xcc, 0xff, 0xc7, 0xa7,
0x8f, 0xff, 0xd3, 0xbb, 0xa9, 0xff, 0xdc, 0xca, 0xbe, 0xff, 0xe3, 0xd4,
0xca, 0xff, 0xda, 0xc6, 0xb8, 0xff, 0xe9, 0xde, 0xd7, 0xff, 0xe6, 0xda,
0xd4, 0xff, 0xed, 0xde, 0xd7, 0xff, 0xf1, 0xe4, 0xe0, 0xff, 0xfb, 0xfb,
0xff, 0xff, 0xc8, 0xa1, 0x7b, 0xff, 0xc6, 0xa2, 0x84, 0xff, 0xe9, 0xee,
0xf4, 0xff, 0xba, 0xad, 0xa4, 0xff, 0xd2, 0xca, 0xc5, 0xff, 0xcd, 0xc1,
0xb9, 0xff, 0xc2, 0xb8, 0xb2, 0xff, 0xd9, 0xd5, 0xd2, 0xff, 0xf3, 0xf7,
0xf9, 0xff, 0xac, 0x72, 0x47, 0xff, 0xe8, 0xdc, 0xd4, 0xff, 0xfb, 0xf9,
0xfc, 0xff, 0xf3, 0xe7, 0xe6, 0xff, 0xf0, 0xe3, 0xdf, 0xff, 0xeb, 0xde,
0xd6, 0xff, 0xeb, 0xe0, 0xd9, 0xff, 0xed, 0xe4, 0xdf, 0xff, 0xee, 0xe5,
0xe0, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xee, 0xe5, 0xdf, 0xff, 0xed, 0xe3,
0xdd, 0xff, 0xea, 0xdd, 0xd6, 0xff, 0xef, 0xe6, 0xe2, 0xff, 0xed, 0xe2,
0xdc, 0xff, 0xec, 0xe0, 0xd9, 0xff, 0xea, 0xde, 0xd7, 0xff, 0xeb, 0xe1,
0xda, 0xff, 0xe7, 0xdb, 0xd3, 0xff, 0xe7, 0xdb, 0xd4, 0xff, 0xea, 0xdd,
0xd5, 0xff, 0xee, 0xe0, 0xda, 0xff, 0xf6, 0xed, 0xee, 0xff, 0xec, 0xe6,
0xe0, 0xff, 0xb3, 0x82, 0x4c, 0xff, 0xb5, 0x9b, 0x87, 0xff, 0xec, 0xf1,
0xf6, 0xff, 0xe0, 0xe0, 0xe1, 0xff, 0xd1, 0xcd, 0xcb, 0xff, 0xf2, 0xf5,
0xf8, 0xff, 0xcd, 0xcf, 0xd1, 0xff, 0xaf, 0x7e, 0x5a, 0xff, 0xc6, 0x9f,
0x80, 0xff, 0xfb, 0xfd, 0xff, 0xff, 0xf2, 0xe7, 0xe4, 0xff, 0xf0, 0xe2,
0xde, 0xff, 0xeb, 0xdc, 0xd5, 0xff, 0xe9, 0xdc, 0xd4, 0xff, 0xe9, 0xdc,
0xd4, 0xff, 0xe9, 0xdb, 0xd2, 0xff, 0xe8, 0xdb, 0xd3, 0xff, 0xe9, 0xdc,
0xd4, 0xff, 0xe8, 0xdb, 0xd2, 0xff, 0xe8, 0xdb, 0xd3, 0xff, 0xe8, 0xdc,
0xd4, 0xff, 0xe6, 0xda, 0xd1, 0xff, 0xe8, 0xdb, 0xd3, 0xff, 0xe9, 0xdc,
0xd3, 0xff, 0xe9, 0xdc, 0xd4, 0xff, 0xe9, 0xdb, 0xd3, 0xff, 0xe9, 0xdd,
0xd5, 0xff, 0xe8, 0xdc, 0xd5, 0xff, 0xe9, 0xdc, 0xd4, 0xff, 0xea, 0xdd,
0xd5, 0xff, 0xf0, 0xe2, 0xde, 0xff, 0xf8, 0xf2, 0xf4, 0xff, 0xe4, 0xdb,
0xd0, 0xff, 0xb4, 0x87, 0x53, 0xff, 0xbf, 0x9b, 0x7b, 0xff, 0xc9, 0xb6,
0xa6, 0xff, 0xc5, 0xb5, 0xa8, 0xff, 0xcf, 0xba, 0xa7, 0xff, 0xae, 0x78,
0x4b, 0xff, 0xc2, 0x95, 0x70, 0xff, 0xf7, 0xf7, 0xf9, 0xff, 0xf4, 0xec,
0xec, 0xff, 0xf2, 0xe6, 0xe3, 0xff, 0xee, 0xdf, 0xd9, 0xff, 0xea, 0xdd,
0xd5, 0xff, 0xe9, 0xdc, 0xd4, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xea, 0xdd,
0xd5, 0xff, 0xeb, 0xdd, 0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdd,
0xd5, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xe9, 0xdd,
0xd5, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xe9, 0xdd,
0xd5, 0xff, 0xe9, 0xdc, 0xd4, 0xff, 0xea, 0xdd, 0xd4, 0xff, 0xe9, 0xdc,
0xd5, 0xff, 0xe8, 0xdc, 0xd4, 0xff, 0xe9, 0xdc, 0xd4, 0xff, 0xed, 0xde,
0xd8, 0xff, 0xf2, 0xe4, 0xe1, 0xff, 0xf8, 0xf2, 0xf5, 0xff, 0xee, 0xea,
0xe8, 0xff, 0xcc, 0xb2, 0x98, 0xff, 0xb8, 0x8f, 0x67, 0xff, 0xb5, 0x89,
0x60, 0xff, 0xbe, 0x96, 0x71, 0xff, 0xdf, 0xcc, 0xba, 0xff, 0xfa, 0xf9,
0xfc, 0xff, 0xf4, 0xeb, 0xea, 0xff, 0xf2, 0xe5, 0xe3, 0xff, 0xee, 0xe0,
0xda, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xea, 0xdd,
0xd5, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xea, 0xde,
0xd6, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xea, 0xde,
0xd6, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xea, 0xdd,
0xd5, 0xff, 0xe9, 0xde, 0xd6, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xea, 0xdc,
0xd4, 0xff, 0xea, 0xdd, 0xd4, 0xff, 0xe9, 0xdc, 0xd5, 0xff, 0xe9, 0xdc,
0xd4, 0xff, 0xe8, 0xdd, 0xd5, 0xff, 0xe9, 0xdc, 0xd4, 0xff, 0xed, 0xdf,
0xd9, 0xff, 0xf1, 0xe3, 0xe0, 0xff, 0xf5, 0xea, 0xe9, 0xff, 0xfa, 0xf6,
0xf9, 0xff, 0xf8, 0xf5, 0xf8, 0xff, 0xf7, 0xf4, 0xf6, 0xff, 0xfb, 0xfa,
0xfe, 0xff, 0xf9, 0xf5, 0xf8, 0xff, 0xf3, 0xe8, 0xe6, 0xff, 0xf1, 0xe3,
0xdf, 0xff, 0xed, 0xde, 0xd8, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xe9, 0xdd,
0xd4, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xea, 0xdd,
0xd5, 0xff, 0xeb, 0xdd, 0xd5, 0xff, 0xea, 0xde, 0xd6, 0xff, 0xe9, 0xdd,
0xd5, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xea, 0xdd,
0xd5, 0xff, 0xeb, 0xdd, 0xd5, 0xff, 0xea, 0xde, 0xd6, 0xff, 0xe9, 0xdd,
0xd5, 0xff, 0xea, 0xdc, 0xd5, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xea, 0xdd,
0xd4, 0xff, 0xe9, 0xdc, 0xd5, 0xff, 0xea, 0xdd, 0xd4, 0xff, 0xe9, 0xdd,
0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdc, 0xd4, 0xff, 0xeb, 0xdd,
0xd5, 0xff, 0xed, 0xde, 0xd8, 0xff, 0xef, 0xe0, 0xdc, 0xff, 0xf2, 0xe4,
0xe1, 0xff, 0xf2, 0xe5, 0xe2, 0xff, 0xf0, 0xe3, 0xde, 0xff, 0xee, 0xe1,
0xdc, 0xff, 0xed, 0xdf, 0xd9, 0xff, 0xeb, 0xdd, 0xd5, 0xff, 0xe9, 0xdc,
0xd5, 0xff, 0xe8, 0xdc, 0xd5, 0xff, 0xea, 0xdd, 0xd4, 0xff, 0xea, 0xdd,
0xd5, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xea, 0xde,
0xd6, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xea, 0xde, 0xd6, 0xff, 0xea, 0xdd,
0xd5, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xea, 0xdd,
0xd5, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xe9, 0xdd,
0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xea, 0xdd, 0xd4, 0xff, 0xe9, 0xdc,
0xd5, 0xff, 0xea, 0xdc, 0xd4, 0xff, 0xea, 0xdc, 0xd4, 0xff, 0xea, 0xdc,
0xd4, 0xff, 0xea, 0xdc, 0xd4, 0xff, 0xe9, 0xdc, 0xd4, 0xff, 0xe9, 0xdc,
0xd4, 0xff, 0xea, 0xdc, 0xd5, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xec, 0xde,
0xd6, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xe9, 0xdc, 0xd4, 0xff, 0xe9, 0xdc,
0xd4, 0xff, 0xe9, 0xdc, 0xd4, 0xff, 0xe9, 0xdc, 0xd5, 0xff, 0xe8, 0xdc,
0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xeb, 0xde,
0xd6, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xea, 0xde,
0xd6, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xeb, 0xde,
0xd6, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xea, 0xde, 0xd6, 0xff, 0xeb, 0xdd,
0xd5, 0xff, 0xea, 0xde, 0xd6, 0xff, 0xea, 0xde, 0xd6, 0xff, 0xea, 0xdd,
0xd5, 0xff, 0xea, 0xdd, 0xd4, 0xff, 0xe9, 0xdc, 0xd5, 0xff, 0xea, 0xdd,
0xd4, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdd,
0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdc,
0xd4, 0xff, 0xe9, 0xdc, 0xd4, 0xff, 0xe9, 0xdc, 0xd4, 0xff, 0xe9, 0xdc,
0xd4, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xea, 0xdd,
0xd5, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xea, 0xdd,
0xd5, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xeb, 0xde,
0xd6, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xea, 0xde,
0xd6, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xeb, 0xde,
0xd6, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xea, 0xde, 0xd6, 0xff, 0xea, 0xdd,
0xd5, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xea, 0xde, 0xd6, 0xff, 0xe9, 0xdd,
0xd5, 0xff, 0xe9, 0xdc, 0xd5, 0xff, 0xe9, 0xdd, 0xd4, 0xff, 0xe9, 0xdd,
0xd5, 0xff, 0xea, 0xde, 0xd6, 0xff, 0xea, 0xde, 0xd6, 0xff, 0xea, 0xde,
0xd6, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdd,
0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xea, 0xdd,
0xd5, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xea, 0xde, 0xd6, 0xff, 0xeb, 0xde,
0xd6, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xea, 0xdd,
0xd5, 0xff, 0xea, 0xde, 0xd6, 0xff, 0xea, 0xde, 0xd6, 0xff, 0xea, 0xde,
0xd6, 0xff, 0xea, 0xde, 0xd6, 0xff, 0xea, 0xde, 0xd6, 0xff, 0xea, 0xde,
0xd6, 0xff, 0xea, 0xde, 0xd6, 0xff, 0xea, 0xde, 0xd6, 0xff, 0xea, 0xde,
0xd6, 0xff, 0xea, 0xde, 0xd6, 0xff, 0xeb, 0xdd, 0xd5, 0xff, 0xea, 0xdd,
0xd5, 0xff, 0xe8, 0xdd, 0xd6, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdc,
0xd5, 0xff, 0xea, 0xdd, 0xd4, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xea, 0xde,
0xd6, 0xff, 0xeb, 0xdd, 0xd5, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xeb, 0xde,
0xd6, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xeb, 0xde,
0xd6, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xeb, 0xde,
0xd6, 0xff, 0xeb, 0xdd, 0xd5, 0xff, 0xeb, 0xdd, 0xd5, 0xff, 0xeb, 0xdd,
0xd5, 0xff, 0xeb, 0xdd, 0xd5, 0xff, 0xeb, 0xde, 0xd5, 0xff, 0xeb, 0xde,
0xd5, 0xff, 0xeb, 0xde, 0xd5, 0xff, 0xeb, 0xde, 0xd5, 0xff, 0xeb, 0xde,
0xd5, 0xff, 0xeb, 0xde, 0xd5, 0xff, 0xeb, 0xde, 0xd5, 0xff, 0xeb, 0xde,
0xd5, 0xff, 0xeb, 0xde, 0xd5, 0xff, 0xeb, 0xde, 0xd5, 0xff, 0xeb, 0xde,
0xd6, 0xff, 0xeb, 0xde, 0xd6, 0xff, 0xe9, 0xde, 0xd6, 0xff, 0xe8, 0xdd,
0xd5, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xe9, 0xdc, 0xd5, 0xff, 0xea, 0xdc,
0xd4, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xea, 0xdd,
0xd5, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xea, 0xdd,
0xd6, 0xff, 0xea, 0xdd, 0xd6, 0xff, 0xea, 0xdd, 0xd6, 0xff, 0xea, 0xde,
0xd6, 0xff, 0xea, 0xdd, 0xd6, 0xff, 0xea, 0xdd, 0xd6, 0xff, 0xea, 0xde,
0xd6, 0xff, 0xea, 0xde, 0xd6, 0xff, 0xea, 0xde, 0xd6, 0xff, 0xea, 0xdd,
0xd6, 0xff, 0xea, 0xde, 0xd6, 0xff, 0xea, 0xdd, 0xd6, 0xff, 0xea, 0xdd,
0xd6, 0xff, 0xea, 0xdd, 0xd6, 0xff, 0xea, 0xdd, 0xd6, 0xff, 0xea, 0xdd,
0xd6, 0xff, 0xea, 0xdd, 0xd6, 0xff, 0xea, 0xdd, 0xd6, 0xff, 0xea, 0xdd,
0xd6, 0xff, 0xea, 0xdd, 0xd6, 0xff, 0xea, 0xdd, 0xd5, 0xff, 0xea, 0xdd,
0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdc,
0xd4, 0xff, 0xe8, 0xdc, 0xd5, 0xff, 0xe8, 0xdc, 0xd4, 0xff, 0xe8, 0xdc,
0xd4, 0xff, 0xe9, 0xdc, 0xd4, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdd,
0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdd,
0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdd,
0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdd,
0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdd,
0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdd,
0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdd,
0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdd,
0xd5, 0xff, 0xe9, 0xdd, 0xd5, 0xff, 0xe9, 0xdd, 0xd4, 0xff, 0xe9, 0xdc,
0xd4, 0xff, 0xe9, 0xdc, 0xd4, 0xff, 0xe8, 0xdc, 0xd4, 0xff, 0xe8, 0xdb,
0xd4, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
unsigned int favicon_ico_len = 5686;
/**
* base page HTML template for wssshd
*
* Copyright (C) 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me
*
* 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 <https://www.gnu.org/licenses/>.
*/
#ifndef BASE_PAGE_H
#define BASE_PAGE_H
// base page HTML template
static const char *base_page_html =
"<!DOCTYPE html>\n"
"<html lang=\"en\">\n"
"<head>\n"
" <meta charset=\"UTF-8\">\n"
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
" <title>{% block title %}WSSSHD control panel{% endblock %}</title>\n"
" <link rel=\"icon\" href=\"/favicon.ico\" type=\"image/x-icon\">\n"
" <link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css\" rel=\"stylesheet\">\n"
" <link href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css\" rel=\"stylesheet\">\n"
" <link rel=\"stylesheet\" href=\"https://unpkg.com/xterm@5.3.0/css/xterm.css\">\n"
" <script src=\"https://unpkg.com/xterm@5.3.0/lib/xterm.js\"></script>\n"
" <script src=\"https://unpkg.com/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.js\"></script>\n"
" <script>\n"
" // Ensure libraries are loaded\n"
" function checkLibraries() {\n"
" if (typeof Terminal === 'undefined') {\n"
" console.error('Terminal not loaded from CDN');\n"
" return false;\n"
" }\n"
" if (typeof FitAddon === 'undefined') {\n"
" console.error('FitAddon not loaded from CDN');\n"
" return false;\n"
" }\n"
" console.log('All xterm libraries loaded successfully');\n"
" return true;\n"
" }\n"
"\n"
" // Check immediately and after a delay\n"
" if (!checkLibraries()) {\n"
" setTimeout(checkLibraries, 1000);\n"
" }\n"
" </script>\n"
" <style>\n"
" .navbar-brand {\n"
" font-weight: bold;\n"
" }\n"
" .client-card {\n"
" transition: transform 0.2s;\n"
" }\n"
" .client-card:hover {\n"
" transform: translateY(-2px);\n"
" box-shadow: 0 4px 8px rgba(0,0,0,0.1);\n"
" }\n"
" .terminal-container {\n"
" background-color: #1e1e1e;\n"
" color: #f8f8f2;\n"
" font-family: 'Courier New', monospace;\n"
" border-radius: 8px;\n"
" height: calc(100vh - 200px);\n"
" min-height: 400px;\n"
" overflow: hidden;\n"
" position: relative;\n"
" }\n"
" .terminal-input {\n"
" background: transparent;\n"
" border: none;\n"
" color: #f8f8f2;\n"
" font-family: 'Courier New', monospace;\n"
" width: 100%;\n"
" outline: none;\n"
" }\n"
" .terminal-input:focus {\n"
" box-shadow: none;\n"
" }\n"
" </style>\n"
"</head>\n"
"<body>\n"
" <nav class=\"navbar navbar-expand-lg navbar-dark bg-primary\">\n"
" <div class=\"container\">\n"
" <a class=\"navbar-brand\" href=\"{{ url_for('index') }}\">\n"
" <i class=\"fas fa-terminal\"></i> WSSSHD control panel\n"
" </a>\n"
" <div class=\"navbar-nav ms-auto\">\n"
" {% if current_user.is_authenticated %}\n"
" <span class=\"navbar-text me-3\">\n"
" Welcome, {{ current_user.username }}!\n"
" </span>\n"
" <button class=\"btn btn-outline-warning btn-sm me-2\" data-bs-toggle=\"modal\" data-bs-target=\"#donationModal\">\n"
" <i class=\"fas fa-heart\"></i> Donate\n"
" </button>\n"
" <a class=\"nav-link\" href=\"{{ url_for('logout') }}\">\n"
" <i class=\"fas fa-sign-out-alt\"></i> Logout\n"
" </a>\n"
" {% endif %}\n"
" </div>\n"
" </div>\n"
" </nav>\n"
"\n"
" <div class=\"container mt-4\">\n"
" {% with messages = get_flashed_messages(with_categories=true) %}\n"
" {% if messages %}\n"
" {% for category, message in messages %}\n"
" <div class=\"alert alert-{{ 'danger' if category == 'error' else 'info' }} alert-dismissible fade show\" role=\"alert\">\n"
" {{ message }}\n"
" <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"alert\"></button>\n"
" </div>\n"
" {% endfor %}\n"
" {% endif %}\n"
" {% endwith %}\n"
"\n"
" {% block content %}{% endblock %}\n"
" </div>\n"
"\n"
" <!-- Donation Modal -->\n"
" <div class=\"modal fade\" id=\"donationModal\" tabindex=\"-1\">\n"
" <div class=\"modal-dialog\">\n"
" <div class=\"modal-content\">\n"
" <div class=\"modal-header\">\n"
" <h5 class=\"modal-title\">\n"
" <i class=\"fas fa-heart text-danger\"></i> Support WebSocket SSH Development\n"
" </h5>\n"
" <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\"></button>\n"
" </div>\n"
" <div class=\"modal-body\">\n"
" <p class=\"text-muted\">Your support helps us continue developing and maintaining this open-source project!</p>\n"
"\n"
" <div class=\"row\">\n"
" <div class=\"col-md-4 text-center mb-3\">\n"
" <h6><i class=\"fab fa-paypal text-primary\"></i> PayPal</h6>\n"
" <a href=\"https://www.paypal.com/paypalme/nexlab\" target=\"_blank\" class=\"btn btn-primary btn-sm\">\n"
" <i class=\"fab fa-paypal\"></i> Donate via PayPal\n"
" </a>\n"
" <small class=\"d-block text-muted mt-1\">info@nexlab.net</small>\n"
" </div>\n"
"\n"
" <div class=\"col-md-4 text-center mb-3\">\n"
" <h6><i class=\"fab fa-bitcoin text-warning\"></i> Bitcoin</h6>\n"
" <div class=\"mb-2\">\n"
" <img src=\"https://api.qrserver.com/v1/create-qr-code/?size=120x120&data=bitcoin:bc1q3zlkpu95amtcltsk85y0eacyzzk29v68tgc5hx\" alt=\"BTC QR Code\" class=\"img-fluid rounded\">\n"
" </div>\n"
" <div class=\"input-group input-group-sm\">\n"
" <input type=\"text\" class=\"form-control form-control-sm font-monospace\" value=\"bc1q3zlkpu95amtcltsk85y0eacyzzk29v68tgc5hx\" readonly style=\"font-size: 0.75rem;\">\n"
" <button class=\"btn btn-outline-secondary btn-sm\" type=\"button\" onclick=\"copyToClipboard('bc1q3zlkpu95amtcltsk85y0eacyzzk29v68tgc5hx')\">\n"
" <i class=\"fas fa-copy\"></i>\n"
" </button>\n"
" </div>\n"
" </div>\n"
"\n"
" <div class=\"col-md-4 text-center mb-3\">\n"
" <h6><i class=\"fab fa-ethereum text-secondary\"></i> Ethereum</h6>\n"
" <div class=\"mb-2\">\n"
" <img src=\"https://api.qrserver.com/v1/create-qr-code/?size=120x120&data=ethereum:0xdA6dAb526515b5cb556d20269207D43fcc760E51\" alt=\"ETH QR Code\" class=\"img-fluid rounded\">\n"
" </div>\n"
" <div class=\"input-group input-group-sm\">\n"
" <input type=\"text\" class=\"form-control form-control-sm font-monospace\" value=\"0xdA6dAb526515b5cb556d20269207D43fcc760E51\" readonly style=\"font-size: 0.75rem;\">\n"
" <button class=\"btn btn-outline-secondary btn-sm\" type=\"button\" onclick=\"copyToClipboard('0xdA6dAb526515b5cb556d20269207D43fcc760E51')\">\n"
" <i class=\"fas fa-copy\"></i>\n"
" </button>\n"
" </div>\n"
" </div>\n"
" </div>\n"
"\n"
" <hr>\n"
" <p class=\"text-center mb-0\">\n"
" <small class=\"text-muted\">\n"
" Thank you for your support! ❤️\n"
" </small>\n"
" </p>\n"
" </div>\n"
" </div>\n"
" </div>\n"
" </div>\n"
"\n"
" <script src=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js\"></script>\n"
" <script src=\"https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.js\"></script>\n"
" <script>\n"
" function copyToClipboard(text) {\n"
" navigator.clipboard.writeText(text).then(function() {\n"
" // Show a temporary success message\n"
" const btn = event.target.closest('button');\n"
" const originalHtml = btn.innerHTML;\n"
" btn.innerHTML = '<i class=\"fas fa-check\"></i>';\n"
" btn.classList.remove('btn-outline-secondary');\n"
" btn.classList.add('btn-success');\n"
" setTimeout(() => {\n"
" btn.innerHTML = originalHtml;\n"
" btn.classList.remove('btn-success');\n"
" btn.classList.add('btn-outline-secondary');\n"
" }, 1000);\n"
" });\n"
" }\n"
" </script>\n"
" {% block scripts %}{% endblock %}\n"
"</body>\n"
"</html>\n";
#endif /* BASE_PAGE_H */
/**
* index page HTML template for wssshd
*
* Copyright (C) 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me
*
* 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 <https://www.gnu.org/licenses/>.
*/
#ifndef INDEX_PAGE_H
#define INDEX_PAGE_H
// index page HTML template
static const char *index_page_html =
"<!DOCTYPE html>\n"
"<html lang=\"en\">\n"
"<head>\n"
" <meta charset=\"UTF-8\">\n"
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
" <title>Dashboard - WSSSHD control panel</title>\n"
" <link rel=\"icon\" href=\"/favicon.ico\" type=\"image/x-icon\">\n"
" <link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css\" rel=\"stylesheet\">\n"
" <link href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css\" rel=\"stylesheet\">\n"
"</head>\n"
"<body>\n"
"<nav class=\"navbar navbar-expand-lg navbar-dark bg-primary\">\n"
"<div class=\"container\">\n"
"<a class=\"navbar-brand\" href=\"/\">\n"
"<i class=\"fas fa-terminal\"></i> WSSSHD control panel</a>\n"
"<div class=\"navbar-nav ms-auto\">\n"
"<span class=\"navbar-text me-3\">%s</span>\n"
"<a class=\"nav-link\" href=\"/logout\">Logout</a>\n"
"</div></div></nav>\n"
"%s\n"
"<div class=\"container mt-4\">\n"
"<div class=\"row\">\n"
" <div class=\"col-md-8\">\n"
" <div class=\"card\">\n"
" <div class=\"card-header\">\n"
" <h3 class=\"card-title mb-0\">\n"
" <i class=\"fas fa-server\"></i> Registered Clients\n"
" </h3>\n"
" </div>\n"
" <div class=\"card-body\">\n"
" <div id=\"client-list\">%s</div>\n"
" </div>\n"
" </div>\n"
" </div>\n"
"\n"
" <div class=\"col-md-4\">\n"
" <div class=\"card\">\n"
" <div class=\"card-header\">\n"
" <h3 class=\"card-title mb-0\">\n"
" <i class=\"fas fa-cogs\"></i> Quick Actions\n"
" </h3>\n"
" </div>\n"
" <div class=\"card-body\">\n"
" %s\n"
" <button class=\"btn btn-outline-secondary btn-sm w-100\" onclick=\"location.reload()\">\n"
" <i class=\"fas fa-sync\"></i> Refresh Status\n"
" </button>\n"
" </div>\n"
" </div>\n"
"\n"
" <div class=\"card mt-3\">\n"
" <div class=\"card-header\">\n"
" <h3 class=\"card-title mb-0\">\n"
" <i class=\"fas fa-info-circle\"></i> System Info\n"
" </h3>\n"
" </div>\n"
" <div class=\"card-body\">\n"
" <p class=\"mb-1\"><strong>WebSocket Port:</strong> <span id=\"websocket-port\">%d</span></p>\n"
" <p class=\"mb-1\"><strong>Domain:</strong> <span id=\"domain\">%s</span></p>\n"
" <p class=\"mb-0\"><strong>Connected Clients:</strong> <span id=\"client-count\">%d</span></p>\n"
" </div>\n"
" </div>\n"
" </div>\n"
"</div>\n"
"</div>\n"
"<script src=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js\"></script>\n"
"<script>\n"
"function showNotification(message, type = 'info') {\n"
" const notificationArea = document.getElementById('notification-area');\n"
" const notification = document.createElement('div');\n"
" notification.className = `alert alert-${type} alert-dismissible fade show`;\n"
" notification.innerHTML = `${message}<button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"alert\"></button>`;\n"
" notificationArea.appendChild(notification);\n"
" setTimeout(() => {\n"
" if (notification.parentNode) {\n"
" notification.remove();\n"
" }\n"
" }, 5000);\n"
"}\n"
"\n"
"function updateClients() {\n"
" fetch('/api/clients')\n"
" .then(response => response.json())\n"
" .then(data => {\n"
" // Update client count\n"
" document.getElementById('client-count').textContent = data.count;\n"
"\n"
" // Generate HTML for client list\n"
" let clientListHtml = '';\n"
" if (data.count === 0) {\n"
" clientListHtml = '<div class=\"text-center py-5\"><i class=\"fas fa-server fa-4x text-muted mb-3\"></i><h4 class=\"text-muted\">No clients registered</h4><p class=\"text-muted\">Clients will appear here when they register.</p></div>';\n"
" } else {\n"
" for (const [clientId, clientData] of Object.entries(data.clients)) {\n"
" const statusIcon = clientData.status === 'connected' ? 'fa-desktop text-success' : 'fa-server text-warning';\n"
" const statusText = clientData.status === 'connected' ? 'Connected' : 'Registered';\n"
" const services = clientData.services;\n"
" let actionsHtml = '';\n"
" if (services.includes('ssh')) {\n"
" actionsHtml += `<a href=\"/terminal/${clientId}\" class=\"btn btn-primary btn-sm me-1\"><i class=\"fas fa-terminal\"></i> SSH</a>`;\n"
" }\n"
" if (services.includes('vnc')) {\n"
" actionsHtml += `<a href=\"/vnc/${clientId}\" class=\"btn btn-info btn-sm me-1\"><i class=\"fas fa-desktop\"></i> VNC</a>`;\n"
" }\n"
" clientListHtml += `\n"
" <div class=\"col-md-4 mb-3\">\n"
" <div class=\"card client-card h-100\">\n"
" <div class=\"card-body text-center\">\n"
" <i class=\"fas ${statusIcon} fa-3x mb-3\"></i>\n"
" <h5 class=\"card-title\">${clientId}</h5>\n"
" <p class=\"card-text text-muted\">${statusText}</p>\n"
" <p class=\"card-text small\">Services: ${services}</p>\n"
" <div class=\"d-flex justify-content-center\">${actionsHtml}</div>\n"
" </div>\n"
" </div>\n"
" </div>`;\n"
" }\n"
" }\n"
"\n"
" // Update client list only if changed\n"
" const clientListDiv = document.getElementById('client-list');\n"
" if (clientListDiv.innerHTML !== clientListHtml) {\n"
" clientListDiv.innerHTML = clientListHtml;\n"
" }\n"
" })\n"
" .catch(error => {\n"
" console.log('Error fetching client data:', error);\n"
" });\n"
"}\n"
"\n"
"// Update every 5 seconds\n"
"setInterval(updateClients, 5000);\n"
"\n"
"// Initial update after 1 second\n"
"setTimeout(updateClients, 1000);\n"
"</script>\n"
"</body>\n"
"</html>\n";
#endif /* INDEX_PAGE_H */
/**
* login page HTML template for wssshd
*
* Copyright (C) 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me
*
* 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 <https://www.gnu.org/licenses/>.
*/
#ifndef LOGIN_PAGE_H
#define LOGIN_PAGE_H
// login page HTML template
static const char *login_page_html =
"<!DOCTYPE html>\n"
"<html lang=\"en\">\n"
"<head>\n"
" <meta charset=\"UTF-8\">\n"
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
" <title>Login - WSSSHD control panel</title>\n"
" <link rel=\"icon\" href=\"/favicon.ico\" type=\"image/x-icon\">\n"
" <link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css\" rel=\"stylesheet\">\n"
" <link href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css\" rel=\"stylesheet\">\n"
"</head>\n"
"<body>\n"
"<nav class=\"navbar navbar-expand-lg navbar-dark bg-primary\">\n"
"<div class=\"container\">\n"
"<a class=\"navbar-brand\" href=\"/\">\n"
"<i class=\"fas fa-terminal\"></i> WSSSHD control panel</a>\n"
"</div></nav>\n"
"<div class=\"container mt-4\">\n"
"<div class=\"row justify-content-center\">\n"
" <div class=\"col-md-6\">\n"
" <div class=\"card\">\n"
" <div class=\"card-header\">\n"
" <h3 class=\"card-title mb-0\"><i class=\"fas fa-sign-in-alt\"></i> Login</h3>\n"
" </div>\n"
" <div class=\"card-body\">\n"
" <form method=\"post\">\n"
" <div class=\"mb-3\">\n"
" <label for=\"username\" class=\"form-label\">Username</label>\n"
" <input type=\"text\" class=\"form-control\" id=\"username\" name=\"username\" required>\n"
" </div>\n"
" <div class=\"mb-3\">\n"
" <label for=\"password\" class=\"form-label\">Password</label>\n"
" <input type=\"password\" class=\"form-control\" id=\"password\" name=\"password\" required>\n"
" </div>\n"
" <button type=\"submit\" class=\"btn btn-primary\">\n"
" <i class=\"fas fa-sign-in-alt\"></i> Login\n"
" </button>\n"
" </form>\n"
" <div class=\"mt-3\">\n"
" <small class=\"text-muted\">\n"
" Default credentials: admin / admin123\n"
" </small>\n"
" </div>\n"
" </div>\n"
" </div>\n"
" </div>\n"
"</div>\n"
"</div>\n"
"<script src=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js\"></script>\n"
"</body>\n"
"</html>\n";
#endif /* LOGIN_PAGE_H */
#ifndef NOVNC_BASE64_JS_PAGE_H
#define NOVNC_BASE64_JS_PAGE_H
extern const char *novnc_base64_js;
#endif /* NOVNC_BASE64_JS_PAGE_H */
/**
* noVNC CSS for wssshd
*
* Copyright (C) 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me
*
* 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 <https://www.gnu.org/licenses/>.
*/
#ifndef NOVNC_CSS_PAGE_H
#define NOVNC_CSS_PAGE_H
// noVNC CSS
const char *novnc_css =
"Couldn't find the requested file /lib/rfb.css in @novnc/novnc.\n";
#endif /* NOVNC_CSS_PAGE_H */
#ifndef NOVNC_DECODERS_COPYRECT_JS_PAGE_H
#define NOVNC_DECODERS_COPYRECT_JS_PAGE_H
extern const char *novnc_decoders_copyrect_js;
#endif /* NOVNC_DECODERS_COPYRECT_JS_PAGE_H */
#ifndef NOVNC_DECODERS_HEXTILE_JS_PAGE_H
#define NOVNC_DECODERS_HEXTILE_JS_PAGE_H
extern const char *novnc_decoders_hextile_js;
#endif /* NOVNC_DECODERS_HEXTILE_JS_PAGE_H */
#ifndef NOVNC_DECODERS_JPEG_JS_PAGE_H
#define NOVNC_DECODERS_JPEG_JS_PAGE_H
extern const char *novnc_decoders_jpeg_js;
#endif /* NOVNC_DECODERS_JPEG_JS_PAGE_H */
#ifndef NOVNC_DECODERS_RAW_JS_PAGE_H
#define NOVNC_DECODERS_RAW_JS_PAGE_H
extern const char *novnc_decoders_raw_js;
#endif /* NOVNC_DECODERS_RAW_JS_PAGE_H */
#ifndef NOVNC_DECODERS_RRE_JS_PAGE_H
#define NOVNC_DECODERS_RRE_JS_PAGE_H
extern const char *novnc_decoders_rre_js;
#endif /* NOVNC_DECODERS_RRE_JS_PAGE_H */
#ifndef NOVNC_DECODERS_TIGHT_JS_PAGE_H
#define NOVNC_DECODERS_TIGHT_JS_PAGE_H
extern const char *novnc_decoders_tight_js;
#endif /* NOVNC_DECODERS_TIGHT_JS_PAGE_H */
#ifndef NOVNC_DECODERS_TIGHTPNG_JS_PAGE_H
#define NOVNC_DECODERS_TIGHTPNG_JS_PAGE_H
extern const char *novnc_decoders_tightpng_js;
#endif /* NOVNC_DECODERS_TIGHTPNG_JS_PAGE_H */
#ifndef NOVNC_DECODERS_ZRLE_JS_PAGE_H
#define NOVNC_DECODERS_ZRLE_JS_PAGE_H
extern const char *novnc_decoders_zrle_js;
#endif /* NOVNC_DECODERS_ZRLE_JS_PAGE_H */
#ifndef NOVNC_DEFLATOR_JS_PAGE_H
#define NOVNC_DEFLATOR_JS_PAGE_H
extern const char *novnc_deflator_js;
#endif /* NOVNC_DEFLATOR_JS_PAGE_H */
#ifndef NOVNC_DES_JS_PAGE_H
#define NOVNC_DES_JS_PAGE_H
extern const char *novnc_des_js;
#endif /* NOVNC_DES_JS_PAGE_H */
#ifndef NOVNC_DISPLAY_JS_PAGE_H
#define NOVNC_DISPLAY_JS_PAGE_H
extern const char *novnc_display_js;
#endif /* NOVNC_DISPLAY_JS_PAGE_H */
#ifndef NOVNC_ENCODINGS_JS_PAGE_H
#define NOVNC_ENCODINGS_JS_PAGE_H
extern const char *novnc_encodings_js;
#endif /* NOVNC_ENCODINGS_JS_PAGE_H */
#ifndef NOVNC_INFLATOR_JS_PAGE_H
#define NOVNC_INFLATOR_JS_PAGE_H
extern const char *novnc_inflator_js;
#endif /* NOVNC_INFLATOR_JS_PAGE_H */
#ifndef NOVNC_INPUT_DOMKEYTABLE_JS_PAGE_H
#define NOVNC_INPUT_DOMKEYTABLE_JS_PAGE_H
extern const char *novnc_input_domkeytable_js;
#endif /* NOVNC_INPUT_DOMKEYTABLE_JS_PAGE_H */
#ifndef NOVNC_INPUT_FIXEDKEYS_JS_PAGE_H
#define NOVNC_INPUT_FIXEDKEYS_JS_PAGE_H
extern const char *novnc_input_fixedkeys_js;
#endif /* NOVNC_INPUT_FIXEDKEYS_JS_PAGE_H */
#ifndef NOVNC_INPUT_GESTUREHANDLER_JS_PAGE_H
#define NOVNC_INPUT_GESTUREHANDLER_JS_PAGE_H
extern const char *novnc_input_gesturehandler_js;
#endif /* NOVNC_INPUT_GESTUREHANDLER_JS_PAGE_H */
#ifndef NOVNC_INPUT_KEYBOARD_JS_PAGE_H
#define NOVNC_INPUT_KEYBOARD_JS_PAGE_H
extern const char *novnc_input_keyboard_js;
#endif /* NOVNC_INPUT_KEYBOARD_JS_PAGE_H */
#ifndef NOVNC_INPUT_KEYSYM_JS_PAGE_H
#define NOVNC_INPUT_KEYSYM_JS_PAGE_H
extern const char *novnc_input_keysym_js;
#endif /* NOVNC_INPUT_KEYSYM_JS_PAGE_H */
#ifndef NOVNC_INPUT_KEYSYMDEF_JS_PAGE_H
#define NOVNC_INPUT_KEYSYMDEF_JS_PAGE_H
extern const char *novnc_input_keysymdef_js;
#endif /* NOVNC_INPUT_KEYSYMDEF_JS_PAGE_H */
#ifndef NOVNC_INPUT_UTIL_JS_PAGE_H
#define NOVNC_INPUT_UTIL_JS_PAGE_H
extern const char *novnc_input_util_js;
#endif /* NOVNC_INPUT_UTIL_JS_PAGE_H */
#ifndef NOVNC_INPUT_VKEYS_JS_PAGE_H
#define NOVNC_INPUT_VKEYS_JS_PAGE_H
extern const char *novnc_input_vkeys_js;
#endif /* NOVNC_INPUT_VKEYS_JS_PAGE_H */
#ifndef NOVNC_INPUT_XTSCANCODES_JS_PAGE_H
#define NOVNC_INPUT_XTSCANCODES_JS_PAGE_H
extern const char *novnc_input_xtscancodes_js;
#endif /* NOVNC_INPUT_XTSCANCODES_JS_PAGE_H */
#ifndef NOVNC_RA2_JS_PAGE_H
#define NOVNC_RA2_JS_PAGE_H
extern const char *novnc_ra2_js;
#endif /* NOVNC_RA2_JS_PAGE_H */
#ifndef NOVNC_RFB_JS_PAGE_H
#define NOVNC_RFB_JS_PAGE_H
extern const char *novnc_rfb_js;
#endif /* NOVNC_RFB_JS_PAGE_H */
#ifndef NOVNC_UTIL_BROWSER_JS_PAGE_H
#define NOVNC_UTIL_BROWSER_JS_PAGE_H
extern const char *novnc_util_browser_js;
#endif /* NOVNC_UTIL_BROWSER_JS_PAGE_H */
#ifndef NOVNC_UTIL_CURSOR_JS_PAGE_H
#define NOVNC_UTIL_CURSOR_JS_PAGE_H
extern const char *novnc_util_cursor_js;
#endif /* NOVNC_UTIL_CURSOR_JS_PAGE_H */
#ifndef NOVNC_UTIL_ELEMENT_JS_PAGE_H
#define NOVNC_UTIL_ELEMENT_JS_PAGE_H
extern const char *novnc_util_element_js;
#endif /* NOVNC_UTIL_ELEMENT_JS_PAGE_H */
#ifndef NOVNC_UTIL_EVENTS_JS_PAGE_H
#define NOVNC_UTIL_EVENTS_JS_PAGE_H
extern const char *novnc_util_events_js;
#endif /* NOVNC_UTIL_EVENTS_JS_PAGE_H */
#ifndef NOVNC_UTIL_EVENTTARGET_JS_PAGE_H
#define NOVNC_UTIL_EVENTTARGET_JS_PAGE_H
extern const char *novnc_util_eventtarget_js;
#endif /* NOVNC_UTIL_EVENTTARGET_JS_PAGE_H */
#ifndef NOVNC_UTIL_INT_JS_PAGE_H
#define NOVNC_UTIL_INT_JS_PAGE_H
extern const char *novnc_util_int_js;
#endif /* NOVNC_UTIL_INT_JS_PAGE_H */
#ifndef NOVNC_UTIL_LOGGING_JS_PAGE_H
#define NOVNC_UTIL_LOGGING_JS_PAGE_H
extern const char *novnc_util_logging_js;
#endif /* NOVNC_UTIL_LOGGING_JS_PAGE_H */
#ifndef NOVNC_UTIL_MD5_JS_PAGE_H
#define NOVNC_UTIL_MD5_JS_PAGE_H
extern const char *novnc_util_md5_js;
#endif /* NOVNC_UTIL_MD5_JS_PAGE_H */
#ifndef NOVNC_UTIL_STRINGS_JS_PAGE_H
#define NOVNC_UTIL_STRINGS_JS_PAGE_H
extern const char *novnc_util_strings_js;
#endif /* NOVNC_UTIL_STRINGS_JS_PAGE_H */
#ifndef NOVNC_VENDOR_PAKO_LIB_ZLIB_ADLER32_JS_PAGE_H
#define NOVNC_VENDOR_PAKO_LIB_ZLIB_ADLER32_JS_PAGE_H
extern const char *novnc_vendor_pako_lib_zlib_adler32_js;
#endif /* NOVNC_VENDOR_PAKO_LIB_ZLIB_ADLER32_JS_PAGE_H */
#ifndef NOVNC_VENDOR_PAKO_LIB_ZLIB_CONSTANTS_JS_PAGE_H
#define NOVNC_VENDOR_PAKO_LIB_ZLIB_CONSTANTS_JS_PAGE_H
extern const char *novnc_vendor_pako_lib_zlib_constants_js;
#endif /* NOVNC_VENDOR_PAKO_LIB_ZLIB_CONSTANTS_JS_PAGE_H */
#ifndef NOVNC_VENDOR_PAKO_LIB_ZLIB_CRC32_JS_PAGE_H
#define NOVNC_VENDOR_PAKO_LIB_ZLIB_CRC32_JS_PAGE_H
extern const char *novnc_vendor_pako_lib_zlib_crc32_js;
#endif /* NOVNC_VENDOR_PAKO_LIB_ZLIB_CRC32_JS_PAGE_H */
#ifndef NOVNC_VENDOR_PAKO_LIB_ZLIB_DEFLATE_JS_PAGE_H
#define NOVNC_VENDOR_PAKO_LIB_ZLIB_DEFLATE_JS_PAGE_H
extern const char *novnc_vendor_pako_lib_zlib_deflate_js;
#endif /* NOVNC_VENDOR_PAKO_LIB_ZLIB_DEFLATE_JS_PAGE_H */
#ifndef NOVNC_VENDOR_PAKO_LIB_ZLIB_INFFAST_JS_PAGE_H
#define NOVNC_VENDOR_PAKO_LIB_ZLIB_INFFAST_JS_PAGE_H
extern const char *novnc_vendor_pako_lib_zlib_inffast_js;
#endif /* NOVNC_VENDOR_PAKO_LIB_ZLIB_INFFAST_JS_PAGE_H */
#ifndef NOVNC_VENDOR_PAKO_LIB_ZLIB_INFLATE_JS_PAGE_H
#define NOVNC_VENDOR_PAKO_LIB_ZLIB_INFLATE_JS_PAGE_H
extern const char *novnc_vendor_pako_lib_zlib_inflate_js;
#endif /* NOVNC_VENDOR_PAKO_LIB_ZLIB_INFLATE_JS_PAGE_H */
#ifndef NOVNC_VENDOR_PAKO_LIB_ZLIB_INFTREES_JS_PAGE_H
#define NOVNC_VENDOR_PAKO_LIB_ZLIB_INFTREES_JS_PAGE_H
extern const char *novnc_vendor_pako_lib_zlib_inftrees_js;
#endif /* NOVNC_VENDOR_PAKO_LIB_ZLIB_INFTREES_JS_PAGE_H */
#ifndef NOVNC_VENDOR_PAKO_LIB_ZLIB_MESSAGES_JS_PAGE_H
#define NOVNC_VENDOR_PAKO_LIB_ZLIB_MESSAGES_JS_PAGE_H
extern const char *novnc_vendor_pako_lib_zlib_messages_js;
#endif /* NOVNC_VENDOR_PAKO_LIB_ZLIB_MESSAGES_JS_PAGE_H */
#ifndef NOVNC_VENDOR_PAKO_LIB_ZLIB_TREES_JS_PAGE_H
#define NOVNC_VENDOR_PAKO_LIB_ZLIB_TREES_JS_PAGE_H
extern const char *novnc_vendor_pako_lib_zlib_trees_js;
#endif /* NOVNC_VENDOR_PAKO_LIB_ZLIB_TREES_JS_PAGE_H */
#ifndef NOVNC_VENDOR_PAKO_LIB_ZLIB_ZSTREAM_JS_PAGE_H
#define NOVNC_VENDOR_PAKO_LIB_ZLIB_ZSTREAM_JS_PAGE_H
extern const char *novnc_vendor_pako_lib_zlib_zstream_js;
#endif /* NOVNC_VENDOR_PAKO_LIB_ZLIB_ZSTREAM_JS_PAGE_H */
#ifndef NOVNC_WEBSOCK_JS_PAGE_H
#define NOVNC_WEBSOCK_JS_PAGE_H
extern const char *novnc_websock_js;
#endif /* NOVNC_WEBSOCK_JS_PAGE_H */
/**
* terminal page HTML template for wssshd
*
* Copyright (C) 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me
*
* 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 <https://www.gnu.org/licenses/>.
*/
#ifndef TERMINAL_PAGE_H
#define TERMINAL_PAGE_H
// terminal page HTML template
static const char *terminal_page_html =
"<!DOCTYPE html>\n"
"<html lang=\"en\">\n"
"<head>\n"
" <meta charset=\"UTF-8\">\n"
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
" <title>Terminal - %s</title>\n"
" <link rel=\"icon\" href=\"/favicon.ico\" type=\"image/x-icon\">\n"
" <link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css\" rel=\"stylesheet\">\n"
" <link href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css\" rel=\"stylesheet\">\n"
" <link rel=\"stylesheet\" href=\"https://unpkg.com/xterm@5.3.0/css/xterm.css\">\n"
" <script src=\"/xterm.js\"></script>\n"
" <script src=\"/xterm-addon-fit.js\"></script>\n"
" <script>\n"
" // Ensure libraries are loaded\n"
" function checkLibraries() {\n"
" if (typeof Terminal === 'undefined') {\n"
" console.error('Terminal not loaded from CDN');\n"
" return false;\n"
" }\n"
" if (typeof FitAddon === 'undefined') {\n"
" console.error('FitAddon not loaded from CDN');\n"
" return false;\n"
" }\n"
" console.log('All xterm libraries loaded successfully');\n"
" return true;\n"
" }\n"
"\n"
" // Check immediately and after a delay\n"
" if (!checkLibraries()) {\n"
" setTimeout(checkLibraries, 1000);\n"
" }\n"
" </script>\n"
" <style>\n"
" .navbar-brand {\n"
" font-weight: bold;\n"
" }\n"
" .client-card {\n"
" transition: transform 0.2s;\n"
" }\n"
" .client-card:hover {\n"
" transform: translateY(-2px);\n"
" box-shadow: 0 4px 8px rgba(0,0,0,0.1);\n"
" }\n"
" .terminal-container {\n"
" background-color: #1e1e1e;\n"
" color: #f8f8f2;\n"
" font-family: 'Courier New', monospace;\n"
" border-radius: 8px;\n"
" height: calc(100vh - 200px);\n"
" min-height: 400px;\n"
" overflow: hidden;\n"
" position: relative;\n"
" }\n"
" .terminal-input {\n"
" background: transparent;\n"
" border: none;\n"
" color: #f8f8f2;\n"
" font-family: 'Courier New', monospace;\n"
" width: 100%;\n"
" outline: none;\n"
" }\n"
" .terminal-input:focus {\n"
" box-shadow: none;\n"
" }\n"
" /* GNOME-like window decorations */\n"
" .terminal-window {\n"
" border: 1px solid #ccc;\n"
" border-radius: 8px;\n"
" overflow: hidden;\n"
" box-shadow: 0 2px 10px rgba(0,0,0,0.2);\n"
" }\n"
" .window-titlebar {\n"
" background: linear-gradient(to bottom, #f0f0f0, #e0e0e0);\n"
" border-bottom: 1px solid #ccc;\n"
" padding: 4px 8px;\n"
" display: flex;\n"
" justify-content: space-between;\n"
" align-items: center;\n"
" height: 32px;\n"
" }\n"
" .window-title {\n"
" font-weight: bold;\n"
" color: #333;\n"
" font-size: 14px;\n"
" }\n"
" .window-controls {\n"
" display: flex;\n"
" align-items: center;\n"
" gap: 4px;\n"
" }\n"
" .window-btn {\n"
" width: 24px;\n"
" height: 24px;\n"
" border: none;\n"
" border-radius: 50%;\n"
" display: flex;\n"
" align-items: center;\n"
" justify-content: center;\n"
" font-size: 14px;\n"
" font-weight: bold;\n"
" cursor: pointer;\n"
" transition: all 0.2s;\n"
" }\n"
" .window-btn:hover {\n"
" transform: scale(1.1);\n"
" }\n"
" .close-btn {\n"
" background: #ff605c;\n"
" color: white;\n"
" }\n"
" .close-btn:hover {\n"
" background: #ff403c;\n"
" }\n"
" .maximize-btn {\n"
" background: #28ca42;\n"
" color: white;\n"
" }\n"
" .maximize-btn:hover {\n"
" background: #24b83a;\n"
" }\n"
" .minimize-btn {\n"
" background: #ffbd44;\n"
" color: white;\n"
" display: none; /* Hidden by default, shown in fullscreen */\n"
" }\n"
" .minimize-btn:hover {\n"
" background: #ffad2c;\n"
" }\n"
" .window-content {\n"
" background: #1e1e1e;\n"
" padding: 8px;\n"
" }\n"
" /* Fullscreen styles */\n"
" .terminal-window.fullscreen {\n"
" position: fixed;\n"
" top: 0;\n"
" left: 0;\n"
" width: 100vw;\n"
" height: 100vh;\n"
" border: none;\n"
" border-radius: 0;\n"
" z-index: 9999;\n"
" }\n"
" .terminal-window.fullscreen .terminal-container {\n"
" height: calc(100vh - 40px) !important;\n"
" min-height: calc(100vh - 40px) !important;\n"
" }\n"
" .terminal-window.fullscreen .minimize-btn {\n"
" display: flex;\n"
" }\n"
" </style>\n"
"</head>\n"
"<body>\n"
"<nav class=\"navbar navbar-expand-lg navbar-dark bg-primary\">\n"
"<div class=\"container\">\n"
"<a class=\"navbar-brand\" href=\"/\">\n"
"<i class=\"fas fa-terminal\"></i> WSSSHD control panel</a>\n"
"<div class=\"navbar-nav ms-auto\">\n"
"<span class=\"navbar-text me-3\">SSH Terminal - %s</span>\n"
"<a class=\"nav-link\" href=\"/logout\">Logout</a>\n"
"</div></div></nav>\n"
"<div id=\"notification-area\" class=\"position-fixed top-0 end-0 p-3\" style=\"z-index: 1050;\"></div>\n"
"<div class=\"container mt-4\">\n"
"<div class=\"row\">\n"
" <div class=\"col-12\">\n"
" <div class=\"terminal-window\">\n"
" <div class=\"window-titlebar\">\n"
" <div class=\"window-title\">\n"
" <i class=\"fas fa-terminal\"></i> SSH Terminal - %s\n"
" </div>\n"
" <div class=\"window-controls\">\n"
" <input type=\"text\" id=\"sshUsername\" class=\"form-control form-control-sm d-inline-block w-auto me-2\" placeholder=\"Username\" value=\"root\" style=\"height: 24px; font-size: 12px; border: none; background: rgba(255,255,255,0.1); color: white;\">\n"
" <button id=\"connectBtn\" class=\"btn btn-success btn-sm me-1\" style=\"height: 24px; font-size: 12px; padding: 0 8px;\">\n"
" <i class=\"fas fa-play\"></i> Connect\n"
" </button>\n"
" <button id=\"disconnectBtn\" class=\"btn btn-danger btn-sm me-1\" disabled style=\"height: 24px; font-size: 12px; padding: 0 8px;\">\n"
" <i class=\"fas fa-stop\"></i> Disconnect\n"
" </button>\n"
" <button class=\"window-btn minimize-btn\" title=\"Exit Fullscreen\">_</button>\n"
" <button class=\"window-btn maximize-btn\" title=\"Fullscreen\"><span class=\"maximize-icon\">□</span></button>\n"
" <button class=\"window-btn close-btn\" title=\"Disconnect\">×</button>\n"
" </div>\n"
" </div>\n"
" <div class=\"window-content\">\n"
" <div id=\"terminal\" class=\"terminal-container w-100\"></div>\n"
" </div>\n"
" </div>\n"
" </div>\n"
"</div>\n"
"</div>\n"
"\n"
"<script src=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js\"></script>\n"
"<script>\n"
"console.log('Terminal script starting...');\n"
"console.log('xterm available:', typeof Terminal);\n"
"console.log('xterm-fit available:', typeof FitAddon);\n"
"\n"
"let term = null;\n"
"let fitAddon = null;\n"
"let connected = false;\n"
"let requestId = null;\n"
"let websocket = null;\n"
"\n"
"console.log('Terminal page loaded, adding event listeners');\n"
"document.getElementById('connectBtn').addEventListener('click', connect);\n"
"document.getElementById('disconnectBtn').addEventListener('click', disconnect);\n"
"\n"
"// Window control buttons\n"
"document.querySelector('.close-btn').addEventListener('click', disconnect);\n"
"document.querySelector('.maximize-btn').addEventListener('click', toggleFullscreen);\n"
"document.querySelector('.minimize-btn').addEventListener('click', toggleFullscreen);\n"
"console.log('Event listeners added');\n"
"\n"
"// Fullscreen functionality\n"
"function toggleFullscreen() {\n"
" const terminalWindow = document.querySelector('.terminal-window');\n"
" const maximizeBtn = document.querySelector('.maximize-btn');\n"
" const maximizeIcon = maximizeBtn.querySelector('.maximize-icon');\n"
"\n"
" if (terminalWindow.classList.contains('fullscreen')) {\n"
" // Exit fullscreen\n"
" terminalWindow.classList.remove('fullscreen');\n"
" maximizeIcon.textContent = '□';\n"
" maximizeBtn.title = 'Fullscreen';\n"
" } else {\n"
" // Enter fullscreen\n"
" terminalWindow.classList.add('fullscreen');\n"
" maximizeIcon.textContent = '▭'; // Restore icon\n"
" maximizeBtn.title = 'Exit Fullscreen';\n"
" }\n"
"\n"
" // Resize terminal after fullscreen change\n"
" setTimeout(() => {\n"
" if (window.fitTerminal) {\n"
" window.fitTerminal();\n"
" }\n"
" // Update backend terminal size if connected\n"
" if (connected && requestId && fitAddon) {\n"
" const newDimensions = fitAddon.proposeDimensions();\n"
" const newCols = newDimensions.cols || 80;\n"
" const newRows = newDimensions.rows || 24;\n"
"\n"
" fetch('/terminal/%s/xterm/resize', {\n"
" method: 'POST',\n"
" headers: {\n"
" 'Content-Type': 'application/x-www-form-urlencoded',\n"
" },\n"
" body: 'request_id=' + encodeURIComponent(requestId) +\n"
" '&cols=' + encodeURIComponent(newCols) +\n"
" '&rows=' + encodeURIComponent(newRows)\n"
" }).catch(error => {\n"
" console.error('Resize error during fullscreen toggle:', error);\n"
" });\n"
" }\n"
" }, 100);\n"
"}\n"
"\n"
"\n"
"function connect() {\n"
" console.log('Connect button clicked');\n"
" console.log('Connect function called');\n"
" if (typeof Terminal === 'undefined') {\n"
" alert('xterm.js library failed to load. Please check your internet connection or refresh the page.');\n"
" return;\n"
" }\n"
" const username = document.getElementById('sshUsername').value;\n"
" console.log('Username value:', username);\n"
" if (!username) {\n"
" alert('Please enter a username');\n"
" return;\n"
" }\n"
" console.log('Username validation passed');\n"
"\n"
" // Initialize xterm with proper configuration\n"
" if (!term) {\n"
" term = new Terminal({\n"
" cursorBlink: true,\n"
" cursorStyle: 'block',\n"
" fontSize: 14,\n"
" fontFamily: 'Monaco, Menlo, \"Ubuntu Mono\", monospace',\n"
" theme: {\n"
" background: '#1e1e1e',\n"
" foreground: '#f8f8f2',\n"
" cursor: '#f8f8f2',\n"
" cursorAccent: '#1e1e1e',\n"
" selection: 'rgba(248, 248, 242, 0.3)'\n"
" },\n"
" allowTransparency: true,\n"
" scrollback: 1000,\n"
" tabStopWidth: 4,\n"
" convertEol: true,\n"
" disableStdin: false,\n"
" cursorWidth: 2,\n"
" bellStyle: 'none',\n"
" rightClickSelectsWord: true,\n"
" fastScrollModifier: 'alt',\n"
" fastScrollSensitivity: 5,\n"
" screenReaderMode: false,\n"
" macOptionIsMeta: false,\n"
" macOptionClickForcesSelection: false,\n"
" minimumContrastRatio: 1\n"
" });\n"
" term.open(document.getElementById('terminal'));\n"
"\n"
" // Load fit addon\n"
" try {\n"
" if (typeof FitAddon !== 'undefined') {\n"
" fitAddon = new FitAddon.FitAddon();\n"
" term.loadAddon(fitAddon);\n"
" console.log('FitAddon loaded successfully');\n"
" } else {\n"
" console.error('FitAddon is not available');\n"
" throw new Error('FitAddon not loaded');\n"
" }\n"
" } catch (e) {\n"
" console.error('Failed to load FitAddon:', e);\n"
" term.write('Warning: Terminal auto-resizing not available\\r\\n');\n"
" // Continue without fit addon - terminal will still work\n"
" }\n"
"\n"
" // Initial fit after a short delay to ensure DOM is ready\n"
" setTimeout(() => {\n"
" window.fitTerminal();\n"
" // Calculate dimensions after initial fit\n"
" let initialDimensions = { cols: 80, rows: 24 };\n"
" if (fitAddon) {\n"
" initialDimensions = fitAddon.proposeDimensions();\n"
" }\n"
" term._initialCols = initialDimensions.cols || 80;\n"
" term._initialRows = initialDimensions.rows || 24;\n"
" }, 100);\n"
"\n"
" // Fit on window resize and update backend terminal size\n"
" window.addEventListener('resize', () => {\n"
" window.fitTerminal();\n"
" // Update terminal size on backend if connected\n"
" if (connected && requestId && fitAddon) {\n"
" const newDimensions = fitAddon.proposeDimensions();\n"
" const newCols = newDimensions.cols || 80;\n"
" const newRows = newDimensions.rows || 24;\n"
"\n"
" fetch('/terminal/%s/xterm/resize', {\n"
" method: 'POST',\n"
" headers: {\n"
" 'Content-Type': 'application/x-www-form-urlencoded',\n"
" },\n"
" body: 'request_id=' + encodeURIComponent(requestId) +\n"
" '&cols=' + encodeURIComponent(newCols) +\n"
" '&rows=' + encodeURIComponent(newRows)\n"
" }).catch(error => {\n"
" console.error('Resize error:', error);\n"
" });\n"
" }\n"
" });\n"
"\n"
" term.focus();\n"
" }\n"
"\n"
" // Define fitTerminal function globally for fullscreen handling\n"
" window.fitTerminal = function() {\n"
" if (fitAddon) {\n"
" fitAddon.fit();\n"
" }\n"
" };\n"
"\n"
" term.write('Connecting to ' + username + '@%s...\\r\\n');\n"
"\n"
" connected = true;\n"
" document.getElementById('connectBtn').disabled = true;\n"
" document.getElementById('disconnectBtn').disabled = false;\n"
" document.getElementById('sshUsername').disabled = true;\n"
"\n"
" // Use calculated dimensions (either from initial fit or current)\n"
" let cols = term._initialCols || 80;\n"
" let rows = term._initialRows || 24;\n"
" if (fitAddon) {\n"
" const dimensions = fitAddon.proposeDimensions();\n"
" cols = dimensions.cols || cols;\n"
" rows = dimensions.rows || rows;\n"
" }\n"
"\n"
" // Establish WebSocket connection first\n"
" const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';\n"
" const wsUrl = wsProtocol + '//' + window.location.host + '/terminal/%s/ws';\n"
" console.log('Connecting WebSocket to:', wsUrl);\n"
"\n"
" websocket = new WebSocket(wsUrl);\n"
" websocket.binaryType = 'arraybuffer';\n"
"\n"
" websocket.onopen = function(event) {\n"
" console.log('WebSocket connected');\n"
" term.write('WebSocket connected, establishing terminal...\\r\\n');\n"
"\n"
" // Now send connect request with terminal dimensions\n"
" const connectUrl = '/terminal/%s/xterm/connect';\n"
" console.log('Sending connect request to:', connectUrl);\n"
"\n"
" fetch(connectUrl, {\n"
" method: 'POST',\n"
" headers: {\n"
" 'Content-Type': 'application/x-www-form-urlencoded',\n"
" },\n"
" body: 'username=' + encodeURIComponent(username) +\n"
" '&cols=' + encodeURIComponent(cols) +\n"
" '&rows=' + encodeURIComponent(rows),\n"
" credentials: 'same-origin'\n"
" })\n"
" .then(response => {\n"
" console.log('Connect response status:', response.status);\n"
" if (!response.ok) {\n"
" throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n"
" }\n"
" return response.json();\n"
" })\n"
" .then(data => {\n"
" console.log('Connect response data:', data);\n"
" if (data.request_id) {\n"
" requestId = data.request_id;\n"
" if (data.command) {\n"
" console.log('Launching command:', data.command);\n"
" }\n"
" term.write('Terminal connected successfully!\\r\\n');\n"
"\n"
" // Send resize with current dimensions to ensure correct sizing\n"
" if (fitAddon) {\n"
" const dimensions = fitAddon.proposeDimensions();\n"
" const newCols = dimensions.cols || 80;\n"
" const newRows = dimensions.rows || 24;\n"
" fetch('/terminal/%s/xterm/resize', {\n"
" method: 'POST',\n"
" headers: {\n"
" 'Content-Type': 'application/x-www-form-urlencoded',\n"
" },\n"
" body: 'request_id=' + encodeURIComponent(requestId) +\n"
" '&cols=' + encodeURIComponent(newCols) +\n"
" '&rows=' + encodeURIComponent(newRows)\n"
" }).catch(error => {\n"
" console.error('Initial resize error:', error);\n"
" });\n"
" }\n"
" } else {\n"
" term.write('Error: ' + (data.error || 'Unknown error') + '\\r\\n');\n"
" disconnect();\n"
" }\n"
" })\n"
" .catch(error => {\n"
" console.error('Terminal connection failed:', error);\n"
" term.write('Terminal connection failed: ' + error.message + '\\r\\n');\n"
" disconnect();\n"
" });\n"
" };\n"
"\n"
" websocket.onmessage = function(event) {\n"
" console.log('WebSocket message received:', event.data);\n"
" if (typeof event.data === 'string') {\n"
" // JSON message (like session ended)\n"
" try {\n"
" const data = JSON.parse(event.data);\n"
" if (data.ended) {\n"
" console.log('Session ended');\n"
" disconnect();\n"
" }\n"
" } catch (e) {\n"
" console.error('Failed to parse JSON message:', e);\n"
" }\n"
" } else {\n"
" // Binary data (terminal output)\n"
" term.write(new Uint8Array(event.data));\n"
" }\n"
" };\n"
"\n"
" websocket.onerror = function(error) {\n"
" console.error('WebSocket error:', error);\n"
" term.write('WebSocket error occurred\\r\\n');\n"
" disconnect();\n"
" };\n"
"\n"
" websocket.onclose = function(event) {\n"
" console.log('WebSocket closed:', event.code, event.reason);\n"
" term.write('Connection closed\\r\\n');\n"
" disconnect();\n"
" };\n"
"\n"
" // Handle input - send all keystrokes to server via WebSocket\n"
" term.onKey(e => {\n"
" if (!connected || !websocket || websocket.readyState !== WebSocket.OPEN) return;\n"
"\n"
" let data = e.key;\n"
" console.log('Sending input data via WebSocket:', data);\n"
"\n"
" // Send input data via WebSocket\n"
" websocket.send(data);\n"
"\n"
" // Prevent local display of input\n"
" e.domEvent.preventDefault();\n"
" });\n"
"}\n"
"\n"
"function disconnect() {\n"
" connected = false;\n"
" document.getElementById('connectBtn').disabled = false;\n"
" document.getElementById('disconnectBtn').disabled = true;\n"
" document.getElementById('sshUsername').disabled = false;\n"
"\n"
" if (websocket && websocket.readyState === WebSocket.OPEN) {\n"
" websocket.close();\n"
" }\n"
" websocket = null;\n"
"\n"
" if (requestId) {\n"
" fetch('/terminal/%s/xterm/disconnect', {\n"
" method: 'POST',\n"
" headers: {\n"
" 'Content-Type': 'application/x-www-form-urlencoded',\n"
" },\n"
" body: 'request_id=' + encodeURIComponent(requestId)\n"
" });\n"
" requestId = null;\n"
" }\n"
"\n"
" if (term) {\n"
" term.write('\\r\\nDisconnected\\r\\n');\n"
" setTimeout(() => {\n"
" location.reload();\n"
" }, 3000);\n"
" }\n"
"}\n"
"\n"
"\n"
"// Focus on terminal when connected\n"
"document.addEventListener('keydown', function(e) {\n"
" if (connected && term) {\n"
" term.focus();\n"
" }\n"
"});\n"
"</script>\n"
"</body>\n"
"</html>\n"
;
#endif /* TERMINAL_PAGE_H */
/**
* users page HTML template for wssshd
*
* Copyright (C) 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me
*
* 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 <https://www.gnu.org/licenses/>.
*/
#ifndef USERS_PAGE_H
#define USERS_PAGE_H
// users page HTML template
static const char *users_page_html =
"<!DOCTYPE html>\n"
"<html lang=\"en\">\n"
"<head>\n"
" <meta charset=\"UTF-8\">\n"
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
" <title>Users - WSSSHD control panel</title>\n"
" <link rel=\"icon\" href=\"/favicon.ico\" type=\"image/x-icon\">\n"
" <link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css\" rel=\"stylesheet\">\n"
" <link href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css\" rel=\"stylesheet\">\n"
"</head>\n"
"<body>\n"
"<nav class=\"navbar navbar-expand-lg navbar-dark bg-primary\">\n"
"<div class=\"container\">\n"
"<a class=\"navbar-brand\" href=\"/\">\n"
"<i class=\"fas fa-terminal\"></i> WSSSHD control panel</a>\n"
"<div class=\"navbar-nav ms-auto\">\n"
"<span class=\"navbar-text me-3\">%s</span>\n"
"<a class=\"nav-link\" href=\"/logout\">Logout</a>\n"
"</div></div></nav>\n"
"<div class=\"container mt-4\">\n"
" <div class=\"card\">\n"
" <div class=\"card-header d-flex justify-content-between align-items-center\">\n"
" <h3 class=\"card-title mb-0\">\n"
" <i class=\"fas fa-users\"></i> User Management\n"
" </h3>\n"
" <div>\n"
" <a href=\"/\" class=\"btn btn-outline-secondary btn-sm me-2\">\n"
" <i class=\"fas fa-home\"></i> Back to Home\n"
" </a>\n"
" <button class=\"btn btn-primary btn-sm\" data-bs-toggle=\"modal\" data-bs-target=\"#addUserModal\">\n"
" <i class=\"fas fa-plus\"></i> Add User\n"
" </button>\n"
" </div>\n"
" </div>\n"
" <div class=\"card-body\">\n"
" <div class=\"table-responsive\">\n"
" <table class=\"table table-striped\">\n"
" <thead>\n"
" <tr>\n"
" <th>Username</th>\n"
" <th>Role</th>\n"
" <th>Actions</th>\n"
" </tr>\n"
" </thead>\n"
" <tbody>\n"
" %s\n"
" </tbody>\n"
" </table>\n"
" </div>\n"
" </div>\n"
" </div>\n"
"</div>\n"
"\n"
"<!-- Add User Modal -->\n"
"<div class=\"modal fade\" id=\"addUserModal\" tabindex=\"-1\">\n"
" <div class=\"modal-dialog\">\n"
" <div class=\"modal-content\">\n"
" <div class=\"modal-header\">\n"
" <h5 class=\"modal-title\">Add New User</h5>\n"
" <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\"></button>\n"
" </div>\n"
" <form id=\"addUserForm\">\n"
" <div class=\"modal-body\">\n"
" <div class=\"mb-3\">\n"
" <label for=\"addUsername\" class=\"form-label\">Username</label>\n"
" <input type=\"text\" class=\"form-control\" id=\"addUsername\" name=\"username\" required>\n"
" </div>\n"
" <div class=\"mb-3\">\n"
" <label for=\"addPassword\" class=\"form-label\">Password</label>\n"
" <input type=\"password\" class=\"form-control\" id=\"addPassword\" name=\"password\" required>\n"
" </div>\n"
" <div class=\"mb-3 form-check\">\n"
" <input type=\"checkbox\" class=\"form-check-input\" id=\"addIsAdmin\" name=\"is_admin\">\n"
" <label class=\"form-check-label\" for=\"addIsAdmin\">Administrator</label>\n"
" </div>\n"
" </div>\n"
" <div class=\"modal-footer\">\n"
" <button type=\"button\" class=\"btn btn-secondary\" data-bs-dismiss=\"modal\">Cancel</button>\n"
" <button type=\"submit\" class=\"btn btn-primary\">Add User</button>\n"
" </div>\n"
" </form>\n"
" </div>\n"
" </div>\n"
"</div>\n"
"\n"
"<!-- Edit User Modal -->\n"
"<div class=\"modal fade\" id=\"editUserModal\" tabindex=\"-1\">\n"
" <div class=\"modal-dialog\">\n"
" <div class=\"modal-content\">\n"
" <div class=\"modal-header\">\n"
" <h5 class=\"modal-title\">Edit User</h5>\n"
" <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\"></button>\n"
" </div>\n"
" <form id=\"editUserForm\">\n"
" <input type=\"hidden\" id=\"editUserId\" name=\"user_id\">\n"
" <div class=\"modal-body\">\n"
" <div class=\"mb-3\">\n"
" <label for=\"editUsername\" class=\"form-label\">Username</label>\n"
" <input type=\"text\" class=\"form-control\" id=\"editUsername\" name=\"username\" required>\n"
" </div>\n"
" <div class=\"mb-3\">\n"
" <label for=\"editPassword\" class=\"form-label\">New Password (leave empty to keep current)</label>\n"
" <input type=\"password\" class=\"form-control\" id=\"editPassword\" name=\"password\">\n"
" </div>\n"
" <div class=\"mb-3 form-check\">\n"
" <input type=\"checkbox\" class=\"form-check-input\" id=\"editIsAdmin\" name=\"is_admin\">\n"
" <label class=\"form-check-label\" for=\"editIsAdmin\">Administrator</label>\n"
" </div>\n"
" </div>\n"
" <div class=\"modal-footer\">\n"
" <button type=\"button\" class=\"btn btn-secondary\" data-bs-dismiss=\"modal\">Cancel</button>\n"
" <button type=\"submit\" class=\"btn btn-primary\">Update User</button>\n"
" </div>\n"
" </form>\n"
" </div>\n"
" </div>\n"
"</div>\n"
"\n"
"<script src=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js\"></script>\n"
"<script>\n"
"function editUser(userId, username, isAdmin) {\n"
" document.getElementById('editUserId').value = userId;\n"
" document.getElementById('editUsername').value = username;\n"
" document.getElementById('editPassword').value = '';\n"
" document.getElementById('editIsAdmin').checked = isAdmin;\n"
" new bootstrap.Modal(document.getElementById('editUserModal')).show();\n"
"}\n"
"\n"
"function deleteUser(userId, username) {\n"
" if (confirm(`Are you sure you want to delete user \"${username}\"?`)) {\n"
" fetch(`/delete_user/${userId}`, {\n"
" method: 'POST',\n"
" headers: {'Content-Type': 'application/x-www-form-urlencoded'}\n"
" })\n"
" .then(response => response.json())\n"
" .then(data => {\n"
" if (data.success) location.reload();\n"
" else alert('Error: ' + data.error);\n"
" });\n"
" }\n"
"}\n"
"\n"
"document.getElementById('addUserForm').addEventListener('submit', function(e) {\n"
" e.preventDefault();\n"
" const formData = new FormData(this);\n"
" fetch('/add_user', {method: 'POST', body: formData})\n"
" .then(response => response.json())\n"
" .then(data => {\n"
" if (data.success) {\n"
" bootstrap.Modal.getInstance(document.getElementById('addUserModal')).hide();\n"
" location.reload();\n"
" } else alert('Error: ' + data.error);\n"
" });\n"
"});\n"
"\n"
"document.getElementById('editUserForm').addEventListener('submit', function(e) {\n"
" e.preventDefault();\n"
" const formData = new FormData(this);\n"
" const userId = document.getElementById('editUserId').value;\n"
" fetch(`/edit_user/${userId}`, {method: 'POST', body: formData})\n"
" .then(response => response.json())\n"
" .then(data => {\n"
" if (data.success) {\n"
" bootstrap.Modal.getInstance(document.getElementById('editUserModal')).hide();\n"
" location.reload();\n"
" } else alert('Error: ' + data.error);\n"
" });\n"
"});\n"
"</script>\n"
"</body>\n"
"</html>\n";
#endif /* USERS_PAGE_H */
/**
* vnc page HTML template for wssshd
*
* Copyright (C) 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me
*
* 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 <https://www.gnu.org/licenses/>.
*/
#ifndef VNC_PAGE_H
#define VNC_PAGE_H
// vnc page HTML template
static const char *vnc_page_html __attribute__((used)) =
"<!DOCTYPE html>\n"
"<html lang=\"en\">\n"
"<head>\n"
" <meta charset=\"UTF-8\">\n"
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
" <title>VNC - %s</title>\n"
" <link rel=\"icon\" href=\"/favicon.ico\" type=\"image/x-icon\">\n"
" <link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css\" rel=\"stylesheet\">\n"
" <link href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css\" rel=\"stylesheet\">\n"
" <link rel=\"stylesheet\" href=\"/novnc.css\">\n"
" <script src=\"/novnc/util/strings.js\"></script>\n"
" <script src=\"/novnc/util/logging.js\"></script>\n"
" <script src=\"/novnc/util/browser.js\"></script>\n"
" <script src=\"/novnc/util/element.js\"></script>\n"
" <script src=\"/novnc/util/events.js\"></script>\n"
" <script src=\"/novnc/util/eventtarget.js\"></script>\n"
" <script src=\"/novnc/util/int.js\"></script>\n"
" <script src=\"/novnc/util/cursor.js\"></script>\n"
" <script src=\"/novnc/util/md5.js\"></script>\n"
" <script src=\"/novnc/websock.js\"></script>\n"
" <script src=\"/novnc/des.js\"></script>\n"
" <script src=\"/novnc/input/keysym.js\"></script>\n"
" <script src=\"/novnc/input/keysymdef.js\"></script>\n"
" <script src=\"/novnc/input/xtscancodes.js\"></script>\n"
" <script src=\"/novnc/input/util.js\"></script>\n"
" <script src=\"/novnc/input/keyboard.js\"></script>\n"
" <script src=\"/novnc/input/gesturehandler.js\"></script>\n"
" <script src=\"/novnc/display.js\"></script>\n"
" <script src=\"/novnc/inflator.js\"></script>\n"
" <script src=\"/novnc/deflator.js\"></script>\n"
" <script src=\"/novnc/encodings.js\"></script>\n"
" <script src=\"/novnc/ra2.js\"></script>\n"
" <script src=\"/novnc/decoders/raw.js\"></script>\n"
" <script src=\"/novnc/decoders/copyrect.js\"></script>\n"
" <script src=\"/novnc/decoders/rre.js\"></script>\n"
" <script src=\"/novnc/decoders/hextile.js\"></script>\n"
" <script src=\"/novnc/decoders/tight.js\"></script>\n"
" <script src=\"/novnc/decoders/tightpng.js\"></script>\n"
" <script src=\"/novnc/decoders/zrle.js\"></script>\n"
" <script src=\"/novnc/decoders/jpeg.js\"></script>\n"
" <script src=\"/novnc/vendor/pako/lib/zlib/adler32.js\"></script>\n"
" <script src=\"/novnc/vendor/pako/lib/zlib/constants.js\"></script>\n"
" <script src=\"/novnc/vendor/pako/lib/zlib/crc32.js\"></script>\n"
" <script src=\"/novnc/vendor/pako/lib/zlib/deflate.js\"></script>\n"
" <script src=\"/novnc/vendor/pako/lib/zlib/inffast.js\"></script>\n"
" <script src=\"/novnc/vendor/pako/lib/zlib/inflate.js\"></script>\n"
" <script src=\"/novnc/vendor/pako/lib/zlib/inftrees.js\"></script>\n"
" <script src=\"/novnc/vendor/pako/lib/zlib/messages.js\"></script>\n"
" <script src=\"/novnc/vendor/pako/lib/zlib/trees.js\"></script>\n"
" <script src=\"/novnc/vendor/pako/lib/zlib/zstream.js\"></script>\n"
" <script src=\"/novnc/rfb.js\"></script>\n"
" <style>\n"
" .navbar-brand {\n"
" font-weight: bold;\n"
" }\n"
" .vnc-container {\n"
" background-color: #1e1e1e;\n"
" color: #f8f8f2;\n"
" border-radius: 8px;\n"
" height: calc(100vh - 200px);\n"
" min-height: 400px;\n"
" overflow: hidden;\n"
" position: relative;\n"
" }\n"
" /* GNOME-like window decorations */\n"
" .vnc-window {\n"
" border: 1px solid #ccc;\n"
" border-radius: 8px;\n"
" overflow: hidden;\n"
" box-shadow: 0 2px 10px rgba(0,0,0,0.2);\n"
" }\n"
" .window-titlebar {\n"
" background: linear-gradient(to bottom, #f0f0f0, #e0e0e0);\n"
" border-bottom: 1px solid #ccc;\n"
" padding: 4px 8px;\n"
" display: flex;\n"
" justify-content: space-between;\n"
" align-items: center;\n"
" height: 32px;\n"
" }\n"
" .window-title {\n"
" font-weight: bold;\n"
" color: #333;\n"
" font-size: 14px;\n"
" }\n"
" .window-controls {\n"
" display: flex;\n"
" align-items: center;\n"
" gap: 4px;\n"
" }\n"
" .window-btn {\n"
" width: 24px;\n"
" height: 24px;\n"
" border: none;\n"
" border-radius: 50%;\n"
" display: flex;\n"
" align-items: center;\n"
" justify-content: center;\n"
" font-size: 14px;\n"
" font-weight: bold;\n"
" cursor: pointer;\n"
" transition: all 0.2s;\n"
" }\n"
" .window-btn:hover {\n"
" transform: scale(1.1);\n"
" }\n"
" .close-btn {\n"
" background: #ff605c;\n"
" color: white;\n"
" }\n"
" .close-btn:hover {\n"
" background: #ff403c;\n"
" }\n"
" .maximize-btn {\n"
" background: #28ca42;\n"
" color: white;\n"
" }\n"
" .maximize-btn:hover {\n"
" background: #24b83a;\n"
" }\n"
" .minimize-btn {\n"
" background: #ffbd44;\n"
" color: white;\n"
" display: none; /* Hidden by default, shown in fullscreen */\n"
" }\n"
" .minimize-btn:hover {\n"
" background: #ffad2c;\n"
" }\n"
" .window-content {\n"
" background: #1e1e1e;\n"
" padding: 8px;\n"
" }\n"
" /* Fullscreen styles */\n"
" .vnc-window.fullscreen {\n"
" position: fixed;\n"
" top: 0;\n"
" left: 0;\n"
" width: 100vw;\n"
" height: 100vh;\n"
" border: none;\n"
" border-radius: 0;\n"
" z-index: 9999;\n"
" }\n"
" .vnc-window.fullscreen .vnc-container {\n"
" height: calc(100vh - 40px) !important;\n"
" min-height: calc(100vh - 40px) !important;\n"
" }\n"
" .vnc-window.fullscreen .minimize-btn {\n"
" display: flex;\n"
" }\n"
" #noVNC_screen {\n"
" width: 100%;\n"
" height: 100%;\n"
" }\n"
" </style>\n"
"</head>\n"
"<body>\n"
"<nav class=\"navbar navbar-expand-lg navbar-dark bg-primary\">\n"
"<div class=\"container\">\n"
"<a class=\"navbar-brand\" href=\"/\">\n"
"<i class=\"fas fa-desktop\"></i> WSSSHD control panel</a>\n"
"<div class=\"navbar-nav ms-auto\">\n"
"<span class=\"navbar-text me-3\">VNC Session - %s</span>\n"
"<a class=\"nav-link\" href=\"/logout\">Logout</a>\n"
"</div></div></nav>\n"
"<div id=\"notification-area\" class=\"position-fixed top-0 end-0 p-3\" style=\"z-index: 1050;\"></div>\n"
"<div class=\"container mt-4\">\n"
"<div class=\"row\">\n"
" <div class=\"col-12\">\n"
" <div class=\"vnc-window\">\n"
" <div class=\"window-titlebar\">\n"
" <div class=\"window-title\">\n"
" <i class=\"fas fa-desktop\"></i> VNC Session - %s\n"
" </div>\n"
" <div class=\"window-controls\">\n"
" <button id=\"connectBtn\" class=\"btn btn-success btn-sm me-1\" style=\"height: 24px; font-size: 12px; padding: 0 8px;\">\n"
" <i class=\"fas fa-play\"></i> Connect\n"
" </button>\n"
" <button id=\"disconnectBtn\" class=\"btn btn-danger btn-sm me-1\" disabled style=\"height: 24px; font-size: 12px; padding: 0 8px;\">\n"
" <i class=\"fas fa-stop\"></i> Disconnect\n"
" </button>\n"
" <button class=\"window-btn minimize-btn\" title=\"Exit Fullscreen\">_</button>\n"
" <button class=\"window-btn maximize-btn\" title=\"Fullscreen\"><span class=\"maximize-icon\">□</span></button>\n"
" <button class=\"window-btn close-btn\" title=\"Disconnect\">×</button>\n"
" </div>\n"
" </div>\n"
" <div class=\"window-content\">\n"
" <div id=\"vnc\" class=\"vnc-container w-100\">\n"
" <div id=\"noVNC_screen\">\n"
" <div id=\"noVNC_status\">Click Connect to start VNC session</div>\n"
" </div>\n"
" </div>\n"
" </div>\n"
" </div>\n"
" </div>\n"
"</div>\n"
"</div>\n"
"\n"
"<script src=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js\"></script>\n"
"<script>\n"
"console.log('VNC page loaded');\n"
"\n"
"let rfb = null;\n"
"let connected = false;\n"
"\n"
"document.getElementById('connectBtn').addEventListener('click', connect);\n"
"document.getElementById('disconnectBtn').addEventListener('click', disconnect);\n"
"\n"
"// Window control buttons\n"
"document.querySelector('.close-btn').addEventListener('click', disconnect);\n"
"document.querySelector('.maximize-btn').addEventListener('click', toggleFullscreen);\n"
"document.querySelector('.minimize-btn').addEventListener('click', toggleFullscreen);\n"
"\n"
"function showNotification(message, type = 'info') {\n"
" const notificationArea = document.getElementById('notification-area');\n"
" const notification = document.createElement('div');\n"
" notification.className = `alert alert-${type} alert-dismissible fade show`;\n"
" notification.innerHTML = `${message}<button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"alert\"></button>`;\n"
" notificationArea.appendChild(notification);\n"
" setTimeout(() => {\n"
" if (notification.parentNode) {\n"
" notification.remove();\n"
" }\n"
" }, 5000);\n"
"}\n"
"\n"
"function toggleFullscreen() {\n"
" const vncWindow = document.querySelector('.vnc-window');\n"
" const maximizeBtn = document.querySelector('.maximize-btn');\n"
" const maximizeIcon = maximizeBtn.querySelector('.maximize-icon');\n"
"\n"
" if (vncWindow.classList.contains('fullscreen')) {\n"
" // Exit fullscreen\n"
" vncWindow.classList.remove('fullscreen');\n"
" maximizeIcon.textContent = '□';\n"
" maximizeBtn.title = 'Fullscreen';\n"
" } else {\n"
" // Enter fullscreen\n"
" vncWindow.classList.add('fullscreen');\n"
" maximizeIcon.textContent = '▭'; // Restore icon\n"
" maximizeBtn.title = 'Exit Fullscreen';\n"
" }\n"
"}\n"
"\n"
"function connect() {\n"
" console.log('Connect button clicked');\n"
" if (connected) return;\n"
"\n"
" const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';\n"
" const wsUrl = wsProtocol + '//' + window.location.host + '/vnc/%s/ws';\n"
" console.log('Connecting VNC to:', wsUrl);\n"
"\n"
" try {\n"
" rfb = new RFB(document.getElementById('noVNC_screen'), wsUrl, {\n"
" credentials: {},\n"
" shared: true,\n"
" repeaterID: '',\n"
" wsProtocols: ['binary']\n"
" });\n"
"\n"
" rfb.addEventListener('connect', () => {\n"
" console.log('[VNC-DEBUG] VNC connected event fired');\n"
" console.log('[VNC-DEBUG] Connection state:', rfb._rfbConnectionState);\n"
" console.log('[VNC-DEBUG] Init state:', rfb._rfbInitState);\n"
" connected = true;\n"
" document.getElementById('connectBtn').disabled = true;\n"
" document.getElementById('disconnectBtn').disabled = false;\n"
" showNotification('VNC session connected', 'success');\n"
" });\n"
"\n"
" rfb.addEventListener('disconnect', () => {\n"
" console.log('[VNC-DEBUG] VNC disconnected event fired');\n"
" console.log('[VNC-DEBUG] Connection state:', rfb._rfbConnectionState);\n"
" console.log('[VNC-DEBUG] Init state:', rfb._rfbInitState);\n"
" connected = false;\n"
" document.getElementById('connectBtn').disabled = false;\n"
" document.getElementById('disconnectBtn').disabled = true;\n"
" showNotification('VNC session disconnected', 'info');\n"
" });\n"
"\n"
" rfb.addEventListener('credentialsrequired', (e) => {\n"
" console.log('VNC credentials required');\n"
" // For now, assume no credentials needed\n"
" });\n"
"\n"
" rfb.addEventListener('securityfailure', (e) => {\n"
" console.error('VNC security failure:', e.detail);\n"
" showNotification('VNC security failure: ' + e.detail.reason, 'danger');\n"
" });\n"
"\n"
" rfb.addEventListener('clipboard', (e) => {\n"
" console.log('VNC clipboard:', e.detail.text);\n"
" });\n"
"\n"
" } catch (e) {\n"
" console.error('Failed to create RFB:', e);\n"
" showNotification('Failed to connect VNC: ' + e.message, 'danger');\n"
" }\n"
"}\n"
"\n"
"function disconnect() {\n"
" if (rfb) {\n"
" rfb.disconnect();\n"
" rfb = null;\n"
" }\n"
" connected = false;\n"
" document.getElementById('connectBtn').disabled = false;\n"
" document.getElementById('disconnectBtn').disabled = true;\n"
" setTimeout(() => {\n"
" location.reload();\n"
" }, 3000);\n"
"}\n"
"</script>\n"
"</body>\n"
"</html>\n"
;
#endif /* VNC_PAGE_H */
/**
* xterm-addon-fit.js library for wssshd
*
* Copyright (C) 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me
*
* 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 <https://www.gnu.org/licenses/>.
*/
#ifndef XTERM_ADDON_PAGE_H
#define XTERM_ADDON_PAGE_H
// xterm-addon-fit.js library
const char *xterm_addon_fit_js =
"!function(e,t){\"object\"==typeof exports&&\"object\"==typeof module?module.exports=t():\"function\"==typeof define&&define.amd?define([],t):\"object\"==typeof exports?exports.FitAddon=t():e.FitAddon=t()}(self,(()=>(()=>{\"use strict\";var e={};return(()=>{var t=e;Object.defineProperty(t,\"__esModule\",{value:!0}),t.FitAddon=void 0,t.FitAddon=class{activate(e){this._terminal=e}dispose(){}fit(){const e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;const t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){if(!this._terminal)return;if(!this._terminal.element||!this._terminal.element.parentElement)return;const e=this._terminal._core,t=e._renderService.dimensions;if(0===t.css.cell.width||0===t.css.cell.height)return;const r=0===this._terminal.options.scrollback?0:e.viewport.scrollBarWidth,i=window.getComputedStyle(this._terminal.element.parentElement),o=parseInt(i.getPropertyValue(\"height\")),s=Math.max(0,parseInt(i.getPropertyValue(\"width\"))),n=window.getComputedStyle(this._terminal.element),l=o-(parseInt(n.getPropertyValue(\"padding-top\"))+parseInt(n.getPropertyValue(\"padding-bottom\"))),a=s-(parseInt(n.getPropertyValue(\"padding-right\"))+parseInt(n.getPropertyValue(\"padding-left\")))-r;return{cols:Math.max(2,Math.floor(a/t.css.cell.width)),rows:Math.max(1,Math.floor(l/t.css.cell.height))}}}})(),e})()));\n"
"//# sourceMappingURL=xterm-addon-fit.js.map\n";
#endif /* XTERM_ADDON_PAGE_H */
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
File added
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -18,8 +18,8 @@ ...@@ -18,8 +18,8 @@
#include "html_pages/novnc_input_fixedkeys_js_page.h" #include "html_pages/novnc_input_fixedkeys_js_page.h"
#include "html_pages/novnc_input_gesturehandler_js_page.h" #include "html_pages/novnc_input_gesturehandler_js_page.h"
#include "html_pages/novnc_input_keyboard_js_page.h" #include "html_pages/novnc_input_keyboard_js_page.h"
#include "html_pages/novnc_input_keysymdef_js_page.h"
#include "html_pages/novnc_input_keysym_js_page.h" #include "html_pages/novnc_input_keysym_js_page.h"
#include "html_pages/novnc_input_keysymdef_js_page.h"
#include "html_pages/novnc_input_util_js_page.h" #include "html_pages/novnc_input_util_js_page.h"
#include "html_pages/novnc_input_vkeys_js_page.h" #include "html_pages/novnc_input_vkeys_js_page.h"
#include "html_pages/novnc_input_xtscancodes_js_page.h" #include "html_pages/novnc_input_xtscancodes_js_page.h"
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include "html_pages/novnc_vendor_pako_lib_zlib_inffast_js_page.h" #include "html_pages/novnc_vendor_pako_lib_zlib_inffast_js_page.h"
#include "html_pages/novnc_vendor_pako_lib_zlib_inflate_js_page.h" #include "html_pages/novnc_vendor_pako_lib_zlib_inflate_js_page.h"
#include "html_pages/novnc_vendor_pako_lib_zlib_inftrees_js_page.h" #include "html_pages/novnc_vendor_pako_lib_zlib_inftrees_js_page.h"
#include "html_pages/novnc_vendor_pako_lib_zlib_messages_js_page.h"
#include "html_pages/novnc_vendor_pako_lib_zlib_trees_js_page.h" #include "html_pages/novnc_vendor_pako_lib_zlib_trees_js_page.h"
#include "html_pages/novnc_vendor_pako_lib_zlib_zstream_js_page.h" #include "html_pages/novnc_vendor_pako_lib_zlib_zstream_js_page.h"
#include "html_pages/novnc_websock_js_page.h" #include "html_pages/novnc_websock_js_page.h"
......
File added
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
*/ */
export default class CopyRectDecoder { class CopyRectDecoder {
decodeRect(x, y, width, height, sock, display, depth) { decodeRect(x, y, width, height, sock, display, depth) {
if (sock.rQwait("COPYRECT", 4)) { if (sock.rQwait("COPYRECT", 4)) {
return false; return false;
...@@ -25,3 +25,4 @@ export default class CopyRectDecoder { ...@@ -25,3 +25,4 @@ export default class CopyRectDecoder {
return true; return true;
} }
} }
window.CopyRectDecoder = CopyRectDecoder;
...@@ -7,9 +7,8 @@ ...@@ -7,9 +7,8 @@
* *
*/ */
import * as Log from '../util/logging.js';
export default class HextileDecoder { class HextileDecoder {
constructor() { constructor() {
this._tiles = 0; this._tiles = 0;
this._lastsubencoding = 0; this._lastsubencoding = 0;
...@@ -189,3 +188,4 @@ export default class HextileDecoder { ...@@ -189,3 +188,4 @@ export default class HextileDecoder {
this._tileBuffer, 0); this._tileBuffer, 0);
} }
} }
window.HextileDecoder = HextileDecoder;
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
*/ */
export default class JPEGDecoder { class JPEGDecoder {
constructor() { constructor() {
// RealVNC will reuse the quantization tables // RealVNC will reuse the quantization tables
// and Huffman tables, so we need to cache them. // and Huffman tables, so we need to cache them.
...@@ -139,3 +139,4 @@ export default class JPEGDecoder { ...@@ -139,3 +139,4 @@ export default class JPEGDecoder {
} }
} }
} }
window.JPEGDecoder = JPEGDecoder;
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
*/ */
export default class RawDecoder { class RawDecoder {
constructor() { constructor() {
this._lines = 0; this._lines = 0;
} }
...@@ -64,3 +64,4 @@ export default class RawDecoder { ...@@ -64,3 +64,4 @@ export default class RawDecoder {
return true; return true;
} }
} }
window.RawDecoder = RawDecoder;
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
*/ */
export default class RREDecoder { class RREDecoder {
constructor() { constructor() {
this._subrects = 0; this._subrects = 0;
} }
...@@ -42,3 +42,4 @@ export default class RREDecoder { ...@@ -42,3 +42,4 @@ export default class RREDecoder {
return true; return true;
} }
} }
window.RREDecoder = RREDecoder;
...@@ -8,10 +8,8 @@ ...@@ -8,10 +8,8 @@
* *
*/ */
import * as Log from '../util/logging.js';
import Inflator from "../inflator.js";
export default class TightDecoder { class TightDecoder {
constructor() { constructor() {
this._ctl = null; this._ctl = null;
this._filter = null; this._filter = null;
...@@ -21,7 +19,7 @@ export default class TightDecoder { ...@@ -21,7 +19,7 @@ export default class TightDecoder {
this._zlibs = []; this._zlibs = [];
for (let i = 0; i < 4; i++) { for (let i = 0; i < 4; i++) {
this._zlibs[i] = new Inflator(); this._zlibs[i] = new Inflate();
} }
} }
...@@ -329,3 +327,4 @@ export default class TightDecoder { ...@@ -329,3 +327,4 @@ export default class TightDecoder {
return this._scratchBuffer; return this._scratchBuffer;
} }
} }
window.TightDecoder = TightDecoder;
...@@ -7,9 +7,8 @@ ...@@ -7,9 +7,8 @@
* *
*/ */
import TightDecoder from './tight.js';
export default class TightPNGDecoder extends TightDecoder { class TightPNGDecoder extends TightDecoder {
_pngRect(x, y, width, height, sock, display, depth) { _pngRect(x, y, width, height, sock, display, depth) {
let data = this._readData(sock); let data = this._readData(sock);
if (data === null) { if (data === null) {
...@@ -25,3 +24,4 @@ export default class TightPNGDecoder extends TightDecoder { ...@@ -25,3 +24,4 @@ export default class TightPNGDecoder extends TightDecoder {
throw new Error("BasicCompression received in TightPNG rect"); throw new Error("BasicCompression received in TightPNG rect");
} }
} }
window.TightPNGDecoder = TightPNGDecoder;
...@@ -7,12 +7,11 @@ ...@@ -7,12 +7,11 @@
* *
*/ */
import Inflate from "../inflator.js";
const ZRLE_TILE_WIDTH = 64; const ZRLE_TILE_WIDTH = 64;
const ZRLE_TILE_HEIGHT = 64; const ZRLE_TILE_HEIGHT = 64;
export default class ZRLEDecoder { class ZRLEDecoder {
constructor() { constructor() {
this._length = 0; this._length = 0;
this._inflator = new Inflate(); this._inflator = new Inflate();
...@@ -183,3 +182,6 @@ export default class ZRLEDecoder { ...@@ -183,3 +182,6 @@ export default class ZRLEDecoder {
return length + 1; return length + 1;
} }
} }
window.ZRLE_TILE_WIDTH = ZRLE_TILE_WIDTH;
window.ZRLE_TILE_HEIGHT = ZRLE_TILE_HEIGHT;
window.ZRLEDecoder = ZRLEDecoder;
...@@ -6,11 +6,8 @@ ...@@ -6,11 +6,8 @@
* See README.md for usage and integration instructions. * See README.md for usage and integration instructions.
*/ */
import { deflateInit, deflate } from "../vendor/pako/lib/zlib/deflate.js";
import { Z_FULL_FLUSH } from "../vendor/pako/lib/zlib/constants.js";
import ZStream from "../vendor/pako/lib/zlib/zstream.js";
export default class Deflator { class Deflator {
constructor() { constructor() {
this.strm = new ZStream(); this.strm = new ZStream();
this.chunkSize = 1024 * 10 * 10; this.chunkSize = 1024 * 10 * 10;
...@@ -83,3 +80,4 @@ export default class Deflator { ...@@ -83,3 +80,4 @@ export default class Deflator {
} }
} }
window.Deflator = Deflator;
...@@ -128,7 +128,7 @@ 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, ...@@ -128,7 +128,7 @@ 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,
/* eslint-enable comma-spacing */ /* eslint-enable comma-spacing */
export default class DES { class DES {
constructor(password) { constructor(password) {
this.keys = []; this.keys = [];
...@@ -264,3 +264,15 @@ export default class DES { ...@@ -264,3 +264,15 @@ export default class DES {
return this.enc8(t.slice(0, 8)).concat(this.enc8(t.slice(8, 16))); return this.enc8(t.slice(0, 8)).concat(this.enc8(t.slice(8, 16)));
} }
} }
window.PC2 = PC2;
window.z = z;
window.a = a;
window.SP1 = SP1;
window.SP2 = SP2;
window.SP3 = SP3;
window.SP4 = SP4;
window.SP5 = SP5;
window.SP6 = SP6;
window.SP7 = SP7;
window.SP8 = SP8;
window.DES = DES;
...@@ -6,11 +6,8 @@ ...@@ -6,11 +6,8 @@
* See README.md for usage and integration instructions. * 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 { class Display {
constructor(target) { constructor(target) {
this._drawCtx = null; this._drawCtx = null;
...@@ -523,3 +520,4 @@ export default class Display { ...@@ -523,3 +520,4 @@ export default class Display {
} }
} }
} }
window.Display = Display;
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* See README.md for usage and integration instructions. * See README.md for usage and integration instructions.
*/ */
export const encodings = { const encodings = {
encodingRaw: 0, encodingRaw: 0,
encodingCopyRect: 1, encodingCopyRect: 1,
encodingRRE: 2, encodingRRE: 2,
...@@ -33,7 +33,7 @@ export const encodings = { ...@@ -33,7 +33,7 @@ export const encodings = {
pseudoEncodingExtendedClipboard: 0xc0a1e5ce pseudoEncodingExtendedClipboard: 0xc0a1e5ce
}; };
export function encodingName(num) { function encodingName(num) {
switch (num) { switch (num) {
case encodings.encodingRaw: return "Raw"; case encodings.encodingRaw: return "Raw";
case encodings.encodingCopyRect: return "CopyRect"; case encodings.encodingCopyRect: return "CopyRect";
...@@ -46,3 +46,5 @@ export function encodingName(num) { ...@@ -46,3 +46,5 @@ export function encodingName(num) {
default: return "[unknown encoding " + num + "]"; default: return "[unknown encoding " + num + "]";
} }
} }
window.encodings = encodings;
window.encodingName = encodingName;
...@@ -6,10 +6,8 @@ ...@@ -6,10 +6,8 @@
* See README.md for usage and integration instructions. * 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 { class Inflate {
constructor() { constructor() {
this.strm = new ZStream(); this.strm = new ZStream();
this.chunkSize = 1024 * 10 * 10; this.chunkSize = 1024 * 10 * 10;
...@@ -64,3 +62,4 @@ export default class Inflate { ...@@ -64,3 +62,4 @@ export default class Inflate {
inflateReset(this.strm); inflateReset(this.strm);
} }
} }
window.Inflate = Inflate;
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
* Licensed under MPL 2.0 or any later version (see LICENSE.txt) * 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" * Mapping between HTML key values and VNC/X11 keysyms for "special"
...@@ -308,4 +307,7 @@ addNumpad("7", KeyTable.XK_7, KeyTable.XK_KP_7); ...@@ -308,4 +307,7 @@ addNumpad("7", KeyTable.XK_7, KeyTable.XK_KP_7);
addNumpad("8", KeyTable.XK_8, KeyTable.XK_KP_8); addNumpad("8", KeyTable.XK_8, KeyTable.XK_KP_8);
addNumpad("9", KeyTable.XK_9, KeyTable.XK_KP_9); addNumpad("9", KeyTable.XK_9, KeyTable.XK_KP_9);
export default DOMKeyTable; window.DOMKeyTable;
window.addStandard = addStandard;
window.addLeftRight = addLeftRight;
window.addNumpad = addNumpad;
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
/* eslint-disable key-spacing */ /* eslint-disable key-spacing */
export default { window.{
// 3.1.1.1. Writing System Keys // 3.1.1.1. Writing System Keys
......
...@@ -33,7 +33,7 @@ const GH_LONGPRESS_TIMEOUT = 1000; ...@@ -33,7 +33,7 @@ const GH_LONGPRESS_TIMEOUT = 1000;
// Timeout when waiting to decide between PINCH and TWODRAG (ms) // Timeout when waiting to decide between PINCH and TWODRAG (ms)
const GH_TWOTOUCH_TIMEOUT = 50; const GH_TWOTOUCH_TIMEOUT = 50;
export default class GestureHandler { class GestureHandler {
constructor() { constructor() {
this._target = null; this._target = null;
...@@ -565,3 +565,19 @@ export default class GestureHandler { ...@@ -565,3 +565,19 @@ export default class GestureHandler {
last: { x: ldx, y: ldy } }; last: { x: ldx, y: ldy } };
} }
} }
window.GH_NOGESTURE = GH_NOGESTURE;
window.GH_ONETAP = GH_ONETAP;
window.GH_TWOTAP = GH_TWOTAP;
window.GH_THREETAP = GH_THREETAP;
window.GH_DRAG = GH_DRAG;
window.GH_LONGPRESS = GH_LONGPRESS;
window.GH_TWODRAG = GH_TWODRAG;
window.GH_PINCH = GH_PINCH;
window.GH_INITSTATE = GH_INITSTATE;
window.GH_MOVE_THRESHOLD = GH_MOVE_THRESHOLD;
window.GH_ANGLE_THRESHOLD = GH_ANGLE_THRESHOLD;
window.GH_MULTITOUCH_TIMEOUT = GH_MULTITOUCH_TIMEOUT;
window.GH_TAP_TIMEOUT = GH_TAP_TIMEOUT;
window.GH_LONGPRESS_TIMEOUT = GH_LONGPRESS_TIMEOUT;
window.GH_TWOTOUCH_TIMEOUT = GH_TWOTOUCH_TIMEOUT;
window.GestureHandler = GestureHandler;
...@@ -4,17 +4,12 @@ ...@@ -4,17 +4,12 @@
* Licensed under MPL 2.0 or any later version (see LICENSE.txt) * 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 // Keyboard event handler
// //
export default class Keyboard { class Keyboard {
constructor(target) { constructor(target) {
this._target = target || null; this._target = target || null;
...@@ -281,3 +276,4 @@ export default class Keyboard { ...@@ -281,3 +276,4 @@ export default class Keyboard {
//Log.Debug(">> Keyboard.ungrab"); //Log.Debug(">> Keyboard.ungrab");
} }
} }
window.Keyboard = Keyboard;
/* eslint-disable key-spacing */ /* eslint-disable key-spacing */
export default { window.KeyTable = {
XK_VoidSymbol: 0xffffff, /* Void symbol */ XK_VoidSymbol: 0xffffff, /* Void symbol */
XK_BackSpace: 0xff08, /* Back space, back char */ XK_BackSpace: 0xff08, /* Back space, back char */
......
...@@ -669,20 +669,19 @@ const codepoints = { ...@@ -669,20 +669,19 @@ const codepoints = {
0x30fc: 0x04b0, // XK_prolongedsound 0x30fc: 0x04b0, // XK_prolongedsound
}; };
export default { window.lookup = function(u) {
lookup(u) { // Latin-1 is one-to-one mapping
// Latin-1 is one-to-one mapping if ((u >= 0x20) && (u <= 0xff)) {
if ((u >= 0x20) && (u <= 0xff)) { return u;
return u; }
}
// Lookup table (fairly random) // Lookup table (fairly random)
const keysym = codepoints[u]; const keysym = codepoints[u];
if (keysym !== undefined) { if (keysym !== undefined) {
return keysym; return keysym;
} }
// General mapping as final fallback // General mapping as final fallback
return 0x01000000 | u; return 0x01000000 | u;
},
}; };
window.codepoints = codepoints;
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 // Get 'KeyboardEvent.code', handling legacy browsers
export function getKeycode(evt) { function getKeycode(evt) {
// Are we getting proper key identifiers? // Are we getting proper key identifiers?
// (unfortunately Firefox and Chrome are crappy here and gives // (unfortunately Firefox and Chrome are crappy here and gives
// us an empty string on some platforms, rather than leaving it // us an empty string on some platforms, rather than leaving it
...@@ -65,7 +59,7 @@ export function getKeycode(evt) { ...@@ -65,7 +59,7 @@ export function getKeycode(evt) {
} }
// Get 'KeyboardEvent.key', handling legacy browsers // Get 'KeyboardEvent.key', handling legacy browsers
export function getKey(evt) { function getKey(evt) {
// Are we getting a proper key value? // Are we getting a proper key value?
if (evt.key !== undefined) { if (evt.key !== undefined) {
// Mozilla isn't fully in sync with the spec yet // Mozilla isn't fully in sync with the spec yet
...@@ -108,7 +102,7 @@ export function getKey(evt) { ...@@ -108,7 +102,7 @@ export function getKey(evt) {
} }
// Get the most reliable keysym value we can get from a key event // Get the most reliable keysym value we can get from a key event
export function getKeysym(evt) { function getKeysym(evt) {
const key = getKey(evt); const key = getKey(evt);
if (key === 'Unidentified') { if (key === 'Unidentified') {
...@@ -189,3 +183,5 @@ export function getKeysym(evt) { ...@@ -189,3 +183,5 @@ export function getKeysym(evt) {
return null; return null;
} }
window.getKeycode = getKeycode;
window.getKeysym = getKeysym;
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* HTML key codes. * HTML key codes.
*/ */
export default { window.{
0x08: 'Backspace', 0x08: 'Backspace',
0x09: 'Tab', 0x09: 'Tab',
0x0a: 'NumpadClear', 0x0a: 'NumpadClear',
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* To re-generate, run: * To re-generate, run:
* keymap-gen code-map --lang=js keymaps.csv html atset1 * keymap-gen code-map --lang=js keymaps.csv html atset1
*/ */
export default { window.XtScancode = {
"Again": 0xe005, /* html:Again (Again) -> linux:129 (KEY_AGAIN) -> atset1:57349 */ "Again": 0xe005, /* html:Again (Again) -> linux:129 (KEY_AGAIN) -> atset1:57349 */
"AltLeft": 0x38, /* html:AltLeft (AltLeft) -> linux:56 (KEY_LEFTALT) -> atset1:56 */ "AltLeft": 0x38, /* html:AltLeft (AltLeft) -> linux:56 (KEY_LEFTALT) -> atset1:56 */
"AltRight": 0xe038, /* html:AltRight (AltRight) -> linux:100 (KEY_RIGHTALT) -> atset1:57400 */ "AltRight": 0xe038, /* html:AltRight (AltRight) -> linux:100 (KEY_RIGHTALT) -> atset1:57400 */
......
import Base64 from './base64.js';
import { encodeUTF8 } from './util/strings.js';
import EventTargetMixin from './util/eventtarget.js';
export class AESEAXCipher { class AESEAXCipher {
constructor() { constructor() {
this._rawKey = null; this._rawKey = null;
this._ctrKey = null; this._ctrKey = null;
...@@ -128,7 +125,7 @@ export class AESEAXCipher { ...@@ -128,7 +125,7 @@ export class AESEAXCipher {
} }
} }
export class RA2Cipher { class RA2Cipher {
constructor() { constructor() {
this._cipher = new AESEAXCipher(); this._cipher = new AESEAXCipher();
this._counter = new Uint8Array(16); this._counter = new Uint8Array(16);
...@@ -156,7 +153,7 @@ export class RA2Cipher { ...@@ -156,7 +153,7 @@ export class RA2Cipher {
} }
} }
export class RSACipher { class RSACipher {
constructor(keyLength) { constructor(keyLength) {
this._key = null; this._key = null;
this._keyLength = keyLength; this._keyLength = keyLength;
...@@ -306,7 +303,7 @@ export class RSACipher { ...@@ -306,7 +303,7 @@ export class RSACipher {
} }
} }
export default class RSAAESAuthenticationState extends EventTargetMixin { class RSAAESAuthenticationState extends EventTargetMixin {
constructor(sock, getCredentials) { constructor(sock, getCredentials) {
super(); super();
this._hasStarted = false; this._hasStarted = false;
...@@ -564,4 +561,7 @@ export default class RSAAESAuthenticationState extends EventTargetMixin { ...@@ -564,4 +561,7 @@ export default class RSAAESAuthenticationState extends EventTargetMixin {
set hasStarted(s) { set hasStarted(s) {
this._hasStarted = s; this._hasStarted = s;
} }
} }window.AESEAXCipher = AESEAXCipher;
\ No newline at end of file window.RA2Cipher = RA2Cipher;
window.RSACipher = RSACipher;
window.RSAAESAuthenticationState = RSAAESAuthenticationState;
...@@ -7,35 +7,7 @@ ...@@ -7,35 +7,7 @@
* *
*/ */
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 DES from "./des.js";
import KeyTable from "./input/keysym.js";
import XtScancode from "./input/xtscancodes.js";
import { encodings } from "./encodings.js";
import RSAAESAuthenticationState from "./ra2.js";
import { MD5 } from "./util/md5.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 // How many seconds to wait for a disconnect to finish
const DISCONNECT_TIMEOUT = 3; const DISCONNECT_TIMEOUT = 3;
...@@ -86,7 +58,7 @@ const extendedClipboardActionPeek = 1 << 26; ...@@ -86,7 +58,7 @@ const extendedClipboardActionPeek = 1 << 26;
const extendedClipboardActionNotify = 1 << 27; const extendedClipboardActionNotify = 1 << 27;
const extendedClipboardActionProvide = 1 << 28; const extendedClipboardActionProvide = 1 << 28;
export default class RFB extends EventTargetMixin { class RFB extends EventTargetMixin {
constructor(target, urlOrChannel, options) { constructor(target, urlOrChannel, options) {
if (!target) { if (!target) {
throw new Error("Must specify target"); throw new Error("Must specify target");
...@@ -629,13 +601,16 @@ export default class RFB extends EventTargetMixin { ...@@ -629,13 +601,16 @@ export default class RFB extends EventTargetMixin {
} }
_socketOpen() { _socketOpen() {
console.log("[VNC-DEBUG] WebSocket opened, starting VNC handshake");
if ((this._rfbConnectionState === 'connecting') && if ((this._rfbConnectionState === 'connecting') &&
(this._rfbInitState === '')) { (this._rfbInitState === '')) {
this._rfbInitState = 'ProtocolVersion'; this._rfbInitState = 'ProtocolVersion';
Log.Debug("Starting VNC handshake"); Log.Debug("Starting VNC handshake");
console.log("[VNC-DEBUG] Set init state to ProtocolVersion");
} else { } else {
console.log("[VNC-DEBUG] Unexpected socket open - connectionState:", this._rfbConnectionState, "initState:", this._rfbInitState);
this._fail("Unexpected server connection while " + this._fail("Unexpected server connection while " +
this._rfbConnectionState); this._rfbConnectionState);
} }
} }
...@@ -826,9 +801,11 @@ export default class RFB extends EventTargetMixin { ...@@ -826,9 +801,11 @@ export default class RFB extends EventTargetMixin {
*/ */
_updateConnectionState(state) { _updateConnectionState(state) {
const oldstate = this._rfbConnectionState; const oldstate = this._rfbConnectionState;
console.log("[VNC-DEBUG] _updateConnectionState called:", oldstate, "->", state);
if (state === oldstate) { if (state === oldstate) {
Log.Debug("Already in state '" + state + "', ignoring"); Log.Debug("Already in state '" + state + "', ignoring");
console.log("[VNC-DEBUG] Already in state '" + state + "', ignoring");
return; return;
} }
...@@ -954,37 +931,49 @@ export default class RFB extends EventTargetMixin { ...@@ -954,37 +931,49 @@ export default class RFB extends EventTargetMixin {
} }
_handleMessage() { _handleMessage() {
console.log("[VNC-DEBUG] _handleMessage called, rQlen:", this._sock.rQlen, "connectionState:", this._rfbConnectionState, "initState:", this._rfbInitState);
if (this._sock.rQlen === 0) { if (this._sock.rQlen === 0) {
Log.Warn("handleMessage called on an empty receive queue"); Log.Warn("handleMessage called on an empty receive queue");
console.log("[VNC-DEBUG] Empty receive queue");
return; return;
} }
switch (this._rfbConnectionState) { switch (this._rfbConnectionState) {
case 'disconnected': case 'disconnected':
Log.Error("Got data while disconnected"); Log.Error("Got data while disconnected");
console.log("[VNC-DEBUG] Got data while disconnected");
break; break;
case 'connected': case 'connected':
console.log("[VNC-DEBUG] Processing messages in connected state");
while (true) { while (true) {
if (this._flushing) { if (this._flushing) {
console.log("[VNC-DEBUG] Flushing, breaking message loop");
break; break;
} }
console.log("[VNC-DEBUG] Calling _normalMsg");
if (!this._normalMsg()) { if (!this._normalMsg()) {
console.log("[VNC-DEBUG] _normalMsg returned false, breaking");
break; break;
} }
if (this._sock.rQlen === 0) { if (this._sock.rQlen === 0) {
console.log("[VNC-DEBUG] No more data in queue");
break; break;
} }
} }
break; break;
case 'connecting': case 'connecting':
console.log("[VNC-DEBUG] Processing messages in connecting state");
while (this._rfbConnectionState === 'connecting') { while (this._rfbConnectionState === 'connecting') {
console.log("[VNC-DEBUG] Calling _initMsg, initState:", this._rfbInitState);
if (!this._initMsg()) { if (!this._initMsg()) {
console.log("[VNC-DEBUG] _initMsg returned false, breaking");
break; break;
} }
} }
break; break;
default: default:
Log.Error("Got data while in an invalid state"); Log.Error("Got data while in an invalid state");
console.log("[VNC-DEBUG] Got data in invalid state:", this._rfbConnectionState);
break; break;
} }
} }
...@@ -1343,22 +1332,29 @@ export default class RFB extends EventTargetMixin { ...@@ -1343,22 +1332,29 @@ export default class RFB extends EventTargetMixin {
// Message Handlers // Message Handlers
_negotiateProtocolVersion() { _negotiateProtocolVersion() {
console.log("[VNC-DEBUG] _negotiateProtocolVersion called");
if (this._sock.rQwait("version", 12)) { if (this._sock.rQwait("version", 12)) {
console.log("[VNC-DEBUG] Waiting for version data");
return false; return false;
} }
const sversion = this._sock.rQshiftStr(12).substr(4, 7); const versionStr = this._sock.rQshiftStr(12);
console.log("[VNC-DEBUG] Received version string:", versionStr);
const sversion = versionStr.substr(4, 7);
Log.Info("Server ProtocolVersion: " + sversion); Log.Info("Server ProtocolVersion: " + sversion);
let isRepeater = 0; let isRepeater = 0;
switch (sversion) { switch (sversion) {
case "000.000": // UltraVNC repeater case "000.000": // UltraVNC repeater
console.log("[VNC-DEBUG] Detected UltraVNC repeater");
isRepeater = 1; isRepeater = 1;
break; break;
case "003.003": case "003.003":
case "003.006": // UltraVNC case "003.006": // UltraVNC
console.log("[VNC-DEBUG] Setting RFB version to 3.3");
this._rfbVersion = 3.3; this._rfbVersion = 3.3;
break; break;
case "003.007": case "003.007":
console.log("[VNC-DEBUG] Setting RFB version to 3.7");
this._rfbVersion = 3.7; this._rfbVersion = 3.7;
break; break;
case "003.008": case "003.008":
...@@ -1366,9 +1362,11 @@ export default class RFB extends EventTargetMixin { ...@@ -1366,9 +1362,11 @@ export default class RFB extends EventTargetMixin {
case "004.000": // Intel AMT KVM case "004.000": // Intel AMT KVM
case "004.001": // RealVNC 4.6 case "004.001": // RealVNC 4.6
case "005.000": // RealVNC 5.3 case "005.000": // RealVNC 5.3
console.log("[VNC-DEBUG] Setting RFB version to 3.8");
this._rfbVersion = 3.8; this._rfbVersion = 3.8;
break; break;
default: default:
console.log("[VNC-DEBUG] Invalid server version:", sversion);
return this._fail("Invalid server version " + sversion); return this._fail("Invalid server version " + sversion);
} }
...@@ -2042,11 +2040,16 @@ export default class RFB extends EventTargetMixin { ...@@ -2042,11 +2040,16 @@ export default class RFB extends EventTargetMixin {
} }
_negotiateServerInit() { _negotiateServerInit() {
if (this._sock.rQwait("server initialization", 24)) { return false; } console.log("[VNC-DEBUG] _negotiateServerInit called");
if (this._sock.rQwait("server initialization", 24)) {
console.log("[VNC-DEBUG] Waiting for server initialization data");
return false;
}
/* Screen size */ /* Screen size */
const width = this._sock.rQshift16(); const width = this._sock.rQshift16();
const height = this._sock.rQshift16(); const height = this._sock.rQshift16();
console.log("[VNC-DEBUG] Server screen size:", width, "x", height);
/* PIXEL_FORMAT */ /* PIXEL_FORMAT */
const bpp = this._sock.rQshift8(); const bpp = this._sock.rQshift8();
...@@ -2062,6 +2065,8 @@ export default class RFB extends EventTargetMixin { ...@@ -2062,6 +2065,8 @@ export default class RFB extends EventTargetMixin {
const blueShift = this._sock.rQshift8(); const blueShift = this._sock.rQshift8();
this._sock.rQskipBytes(3); // padding this._sock.rQskipBytes(3); // padding
console.log("[VNC-DEBUG] Server pixel format - bpp:", bpp, "depth:", depth, "trueColor:", trueColor);
// NB(directxman12): we don't want to call any callbacks or print messages until // NB(directxman12): we don't want to call any callbacks or print messages until
// *after* we're past the point where we could backtrack // *after* we're past the point where we could backtrack
...@@ -2176,33 +2181,42 @@ export default class RFB extends EventTargetMixin { ...@@ -2176,33 +2181,42 @@ export default class RFB extends EventTargetMixin {
* ServerInitialization * ServerInitialization
*/ */
_initMsg() { _initMsg() {
console.log("[VNC-DEBUG] _initMsg called, initState:", this._rfbInitState);
switch (this._rfbInitState) { switch (this._rfbInitState) {
case 'ProtocolVersion': case 'ProtocolVersion':
console.log("[VNC-DEBUG] Negotiating protocol version");
return this._negotiateProtocolVersion(); return this._negotiateProtocolVersion();
case 'Security': case 'Security':
console.log("[VNC-DEBUG] Negotiating security");
return this._negotiateSecurity(); return this._negotiateSecurity();
case 'Authentication': case 'Authentication':
console.log("[VNC-DEBUG] Negotiating authentication");
return this._negotiateAuthentication(); return this._negotiateAuthentication();
case 'SecurityResult': case 'SecurityResult':
console.log("[VNC-DEBUG] Handling security result");
return this._handleSecurityResult(); return this._handleSecurityResult();
case 'SecurityReason': case 'SecurityReason':
console.log("[VNC-DEBUG] Handling security reason");
return this._handleSecurityReason(); return this._handleSecurityReason();
case 'ClientInitialisation': case 'ClientInitialisation':
console.log("[VNC-DEBUG] Sending client initialization");
this._sock.send([this._shared ? 1 : 0]); // ClientInitialisation this._sock.send([this._shared ? 1 : 0]); // ClientInitialisation
this._rfbInitState = 'ServerInitialisation'; this._rfbInitState = 'ServerInitialisation';
return true; return true;
case 'ServerInitialisation': case 'ServerInitialisation':
console.log("[VNC-DEBUG] Negotiating server initialization");
return this._negotiateServerInit(); return this._negotiateServerInit();
default: default:
console.log("[VNC-DEBUG] Unknown init state:", this._rfbInitState);
return this._fail("Unknown init state (state: " + return this._fail("Unknown init state (state: " +
this._rfbInitState + ")"); this._rfbInitState + ")");
} }
} }
...@@ -2461,13 +2475,17 @@ export default class RFB extends EventTargetMixin { ...@@ -2461,13 +2475,17 @@ export default class RFB extends EventTargetMixin {
msgType = this._sock.rQshift8(); msgType = this._sock.rQshift8();
} }
console.log("[VNC-DEBUG] _normalMsg processing message type:", msgType, "FBU.rects:", this._FBU.rects);
let first, ret; let first, ret;
switch (msgType) { switch (msgType) {
case 0: // FramebufferUpdate case 0: // FramebufferUpdate
console.log("[VNC-DEBUG] Handling FramebufferUpdate");
ret = this._framebufferUpdate(); ret = this._framebufferUpdate();
if (ret && !this._enabledContinuousUpdates) { if (ret && !this._enabledContinuousUpdates) {
console.log("[VNC-DEBUG] Sending FramebufferUpdateRequest");
RFB.messages.fbUpdateRequest(this._sock, true, 0, 0, RFB.messages.fbUpdateRequest(this._sock, true, 0, 0,
this._fbWidth, this._fbHeight); this._fbWidth, this._fbHeight);
} }
return ret; return ret;
...@@ -2505,8 +2523,10 @@ export default class RFB extends EventTargetMixin { ...@@ -2505,8 +2523,10 @@ export default class RFB extends EventTargetMixin {
return this._handleXvpMsg(); return this._handleXvpMsg();
default: default:
this._fail("Unexpected server message (type " + msgType + ")"); Log.Warn("Ignoring unknown server message (type " + msgType + ")");
Log.Debug("sock.rQslice(0, 30): " + this._sock.rQslice(0, 30)); Log.Debug("sock.rQslice(0, 30): " + this._sock.rQslice(0, 30));
// Skip the unknown message by consuming any remaining data in the current frame
this._sock.rQskipBytes(this._sock.rQlen);
return true; return true;
} }
} }
...@@ -2525,6 +2545,18 @@ export default class RFB extends EventTargetMixin { ...@@ -2525,6 +2545,18 @@ export default class RFB extends EventTargetMixin {
this._sock.rQskipBytes(1); // Padding this._sock.rQskipBytes(1); // Padding
this._FBU.rects = this._sock.rQshift16(); this._FBU.rects = this._sock.rQshift16();
console.log("[VNC-DEBUG] FramebufferUpdate header - rects:", this._FBU.rects);
// Validate rectangle count - should be reasonable (not 65534 or similar corruption)
if (this._FBU.rects > 10000 || this._FBU.rects < 0) {
console.log("[VNC-DEBUG] Invalid rectangle count:", this._FBU.rects, "- likely corrupted data, skipping message");
Log.Warn("Invalid rectangle count in FramebufferUpdate (" + this._FBU.rects + "), skipping corrupted message");
// Skip this corrupted message by consuming all remaining data in the current frame
this._sock.rQskipBytes(this._sock.rQlen);
this._FBU.rects = 0; // Reset for next message
return true; // Continue processing
}
// Make sure the previous frame is fully rendered first // Make sure the previous frame is fully rendered first
// to avoid building up an excessive queue // to avoid building up an excessive queue
if (this._display.pending()) { if (this._display.pending()) {
...@@ -2842,9 +2874,15 @@ export default class RFB extends EventTargetMixin { ...@@ -2842,9 +2874,15 @@ export default class RFB extends EventTargetMixin {
_handleDataRect() { _handleDataRect() {
let decoder = this._decoders[this._FBU.encoding]; let decoder = this._decoders[this._FBU.encoding];
if (!decoder) { if (!decoder) {
this._fail("Unsupported encoding (encoding: " + console.log("[VNC-DEBUG] Unsupported encoding:", this._FBU.encoding, "- skipping rectangle");
this._FBU.encoding + ")"); Log.Warn("Unsupported encoding (encoding: " +
return false; this._FBU.encoding + "), skipping rectangle");
// Skip the rectangle by consuming the data
const rectSize = this._FBU.width * this._FBU.height;
// For unknown encodings, we can't know how much data to skip
// So we'll skip a reasonable amount or fail gracefully
this._sock.rQskipBytes(this._sock.rQlen); // Skip all remaining data in buffer
return true;
} }
try { try {
...@@ -2853,8 +2891,11 @@ export default class RFB extends EventTargetMixin { ...@@ -2853,8 +2891,11 @@ export default class RFB extends EventTargetMixin {
this._sock, this._display, this._sock, this._display,
this._fbDepth); this._fbDepth);
} catch (err) { } catch (err) {
this._fail("Error decoding rect: " + err); console.log("[VNC-DEBUG] Error decoding rect:", err, "- skipping corrupted rectangle");
return false; Log.Warn("Error decoding rect: " + err + " - skipping corrupted rectangle");
// Skip remaining data in this rectangle to avoid further corruption
this._sock.rQskipBytes(this._sock.rQlen);
return true; // Continue processing instead of failing
} }
} }
...@@ -3365,3 +3406,35 @@ RFB.cursors = { ...@@ -3365,3 +3406,35 @@ RFB.cursors = {
hotx: 1, hoty: 1, hotx: 1, hoty: 1,
} }
}; };
// Make RFB available globally
window.RFB = RFB;
window.DISCONNECT_TIMEOUT = DISCONNECT_TIMEOUT;
window.DEFAULT_BACKGROUND = DEFAULT_BACKGROUND;
window.MOUSE_MOVE_DELAY = MOUSE_MOVE_DELAY;
window.WHEEL_STEP = WHEEL_STEP;
window.WHEEL_LINE_HEIGHT = WHEEL_LINE_HEIGHT;
window.GESTURE_ZOOMSENS = GESTURE_ZOOMSENS;
window.GESTURE_SCRLSENS = GESTURE_SCRLSENS;
window.DOUBLE_TAP_TIMEOUT = DOUBLE_TAP_TIMEOUT;
window.DOUBLE_TAP_THRESHOLD = DOUBLE_TAP_THRESHOLD;
window.securityTypeNone = securityTypeNone;
window.securityTypeVNCAuth = securityTypeVNCAuth;
window.securityTypeRA2ne = securityTypeRA2ne;
window.securityTypeTight = securityTypeTight;
window.securityTypeVeNCrypt = securityTypeVeNCrypt;
window.securityTypeXVP = securityTypeXVP;
window.securityTypeARD = securityTypeARD;
window.securityTypeMSLogonII = securityTypeMSLogonII;
window.securityTypeUnixLogon = securityTypeUnixLogon;
window.securityTypePlain = securityTypePlain;
window.extendedClipboardFormatText = extendedClipboardFormatText;
window.extendedClipboardFormatRtf = extendedClipboardFormatRtf;
window.extendedClipboardFormatHtml = extendedClipboardFormatHtml;
window.extendedClipboardFormatDib = extendedClipboardFormatDib;
window.extendedClipboardFormatFiles = extendedClipboardFormatFiles;
window.extendedClipboardActionCaps = extendedClipboardActionCaps;
window.extendedClipboardActionRequest = extendedClipboardActionRequest;
window.extendedClipboardActionPeek = extendedClipboardActionPeek;
window.extendedClipboardActionNotify = extendedClipboardActionNotify;
window.extendedClipboardActionProvide = extendedClipboardActionProvide;
...@@ -8,10 +8,9 @@ ...@@ -8,10 +8,9 @@
* Browser feature support detection * Browser feature support detection
*/ */
import * as Log from './logging.js';
// Touch detection // Touch detection
export let isTouchDevice = ('ontouchstart' in document.documentElement) || let isTouchDevice = ('ontouchstart' in document.documentElement) ||
// requried for Chrome debugger // requried for Chrome debugger
(document.ontouchstart !== undefined) || (document.ontouchstart !== undefined) ||
// required for MS Surface // required for MS Surface
...@@ -25,7 +24,7 @@ window.addEventListener('touchstart', function onFirstTouch() { ...@@ -25,7 +24,7 @@ window.addEventListener('touchstart', function onFirstTouch() {
// The goal is to find a certain physical width, the devicePixelRatio // The goal is to find a certain physical width, the devicePixelRatio
// brings us a bit closer but is not optimal. // brings us a bit closer but is not optimal.
export let dragThreshold = 10 * (window.devicePixelRatio || 1); let dragThreshold = 10 * (window.devicePixelRatio || 1);
let _supportsCursorURIs = false; let _supportsCursorURIs = false;
...@@ -43,7 +42,7 @@ try { ...@@ -43,7 +42,7 @@ try {
Log.Error("Data URI scheme cursor test exception: " + exc); Log.Error("Data URI scheme cursor test exception: " + exc);
} }
export const supportsCursorURIs = _supportsCursorURIs; const supportsCursorURIs = _supportsCursorURIs;
let _hasScrollbarGutter = true; let _hasScrollbarGutter = true;
try { try {
...@@ -68,7 +67,7 @@ try { ...@@ -68,7 +67,7 @@ try {
} catch (exc) { } catch (exc) {
Log.Error("Scrollbar test exception: " + exc); Log.Error("Scrollbar test exception: " + exc);
} }
export const hasScrollbarGutter = _hasScrollbarGutter; const hasScrollbarGutter = _hasScrollbarGutter;
/* /*
* The functions for detection of platforms and browsers below are exported * The functions for detection of platforms and browsers below are exported
...@@ -79,74 +78,93 @@ export const hasScrollbarGutter = _hasScrollbarGutter; ...@@ -79,74 +78,93 @@ export const hasScrollbarGutter = _hasScrollbarGutter;
/* OS */ /* OS */
export function isMac() { function isMac() {
return !!(/mac/i).exec(navigator.platform); return !!(/mac/i).exec(navigator.platform);
} }
export function isWindows() { function isWindows() {
return !!(/win/i).exec(navigator.platform); return !!(/win/i).exec(navigator.platform);
} }
export function isIOS() { function isIOS() {
return (!!(/ipad/i).exec(navigator.platform) || return (!!(/ipad/i).exec(navigator.platform) ||
!!(/iphone/i).exec(navigator.platform) || !!(/iphone/i).exec(navigator.platform) ||
!!(/ipod/i).exec(navigator.platform)); !!(/ipod/i).exec(navigator.platform));
} }
export function isAndroid() { function isAndroid() {
/* Android sets navigator.platform to Linux :/ */ /* Android sets navigator.platform to Linux :/ */
return !!navigator.userAgent.match('Android '); return !!navigator.userAgent.match('Android ');
} }
export function isChromeOS() { function isChromeOS() {
/* ChromeOS sets navigator.platform to Linux :/ */ /* ChromeOS sets navigator.platform to Linux :/ */
return !!navigator.userAgent.match(' CrOS '); return !!navigator.userAgent.match(' CrOS ');
} }
/* Browser */ /* Browser */
export function isSafari() { function isSafari() {
return !!navigator.userAgent.match('Safari/...') && return !!navigator.userAgent.match('Safari/...') &&
!navigator.userAgent.match('Chrome/...') && !navigator.userAgent.match('Chrome/...') &&
!navigator.userAgent.match('Chromium/...') && !navigator.userAgent.match('Chromium/...') &&
!navigator.userAgent.match('Epiphany/...'); !navigator.userAgent.match('Epiphany/...');
} }
export function isFirefox() { function isFirefox() {
return !!navigator.userAgent.match('Firefox/...') && return !!navigator.userAgent.match('Firefox/...') &&
!navigator.userAgent.match('Seamonkey/...'); !navigator.userAgent.match('Seamonkey/...');
} }
export function isChrome() { function isChrome() {
return !!navigator.userAgent.match('Chrome/...') && return !!navigator.userAgent.match('Chrome/...') &&
!navigator.userAgent.match('Chromium/...') && !navigator.userAgent.match('Chromium/...') &&
!navigator.userAgent.match('Edg/...') && !navigator.userAgent.match('Edg/...') &&
!navigator.userAgent.match('OPR/...'); !navigator.userAgent.match('OPR/...');
} }
export function isChromium() { function isChromium() {
return !!navigator.userAgent.match('Chromium/...'); return !!navigator.userAgent.match('Chromium/...');
} }
export function isOpera() { function isOpera() {
return !!navigator.userAgent.match('OPR/...'); return !!navigator.userAgent.match('OPR/...');
} }
export function isEdge() { function isEdge() {
return !!navigator.userAgent.match('Edg/...'); return !!navigator.userAgent.match('Edg/...');
} }
/* Engine */ /* Engine */
export function isGecko() { function isGecko() {
return !!navigator.userAgent.match('Gecko/...'); return !!navigator.userAgent.match('Gecko/...');
} }
export function isWebKit() { function isWebKit() {
return !!navigator.userAgent.match('AppleWebKit/...') && return !!navigator.userAgent.match('AppleWebKit/...') &&
!navigator.userAgent.match('Chrome/...'); !navigator.userAgent.match('Chrome/...');
} }
export function isBlink() { function isBlink() {
return !!navigator.userAgent.match('Chrome/...'); return !!navigator.userAgent.match('Chrome/...');
} }
window.isTouchDevice = isTouchDevice;
window.dragThreshold = dragThreshold;
window._supportsCursorURIs = _supportsCursorURIs;
window.supportsCursorURIs = supportsCursorURIs;
window._hasScrollbarGutter = _hasScrollbarGutter;
window.hasScrollbarGutter = hasScrollbarGutter;
window.isMac = isMac;
window.isWindows = isWindows;
window.isIOS = isIOS;
window.isAndroid = isAndroid;
window.isChromeOS = isChromeOS;
window.isSafari = isSafari;
window.isFirefox = isFirefox;
window.isChromium = isChromium;
window.isOpera = isOpera;
window.isEdge = isEdge;
window.isGecko = isGecko;
window.isWebKit = isWebKit;
window.isBlink = isBlink;
...@@ -4,11 +4,10 @@ ...@@ -4,11 +4,10 @@
* Licensed under MPL 2.0 or any later version (see LICENSE.txt) * Licensed under MPL 2.0 or any later version (see LICENSE.txt)
*/ */
import { supportsCursorURIs, isTouchDevice } from './browser.js';
const useFallback = !supportsCursorURIs || isTouchDevice; const useFallback = !supportsCursorURIs || isTouchDevice;
export default class Cursor { class Cursor {
constructor() { constructor() {
this._target = null; this._target = null;
...@@ -245,3 +244,5 @@ export default class Cursor { ...@@ -245,3 +244,5 @@ export default class Cursor {
document.documentElement.contains(document.captureElement); document.documentElement.contains(document.captureElement);
} }
} }
window.useFallback = useFallback;
window.Cursor = Cursor;
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* HTML element utility functions * HTML element utility functions
*/ */
export function clientToElement(x, y, elem) { function clientToElement(x, y, elem) {
const bounds = elem.getBoundingClientRect(); const bounds = elem.getBoundingClientRect();
let pos = { x: 0, y: 0 }; let pos = { x: 0, y: 0 };
// Clip to target bounds // Clip to target bounds
...@@ -30,3 +30,4 @@ export function clientToElement(x, y, elem) { ...@@ -30,3 +30,4 @@ export function clientToElement(x, y, elem) {
} }
return pos; return pos;
} }
window.clientToElement = clientToElement;
...@@ -10,11 +10,11 @@ ...@@ -10,11 +10,11 @@
* Cross-browser event and position routines * Cross-browser event and position routines
*/ */
export function getPointerEvent(e) { function getPointerEvent(e) {
return e.changedTouches ? e.changedTouches[0] : e.touches ? e.touches[0] : e; return e.changedTouches ? e.changedTouches[0] : e.touches ? e.touches[0] : e;
} }
export function stopEvent(e) { function stopEvent(e) {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
} }
...@@ -60,7 +60,7 @@ function _capturedElemChanged() { ...@@ -60,7 +60,7 @@ function _capturedElemChanged() {
const _captureObserver = new MutationObserver(_capturedElemChanged); const _captureObserver = new MutationObserver(_capturedElemChanged);
export function setCapture(target) { function setCapture(target) {
if (target.setCapture) { if (target.setCapture) {
target.setCapture(); target.setCapture();
...@@ -107,7 +107,7 @@ export function setCapture(target) { ...@@ -107,7 +107,7 @@ export function setCapture(target) {
} }
} }
export function releaseCapture() { function releaseCapture() {
if (document.releaseCapture) { if (document.releaseCapture) {
document.releaseCapture(); document.releaseCapture();
...@@ -136,3 +136,12 @@ export function releaseCapture() { ...@@ -136,3 +136,12 @@ export function releaseCapture() {
window.removeEventListener('mouseup', _captureProxy); window.removeEventListener('mouseup', _captureProxy);
} }
} }
window.getPointerEvent = getPointerEvent;
window.stopEvent = stopEvent;
window._captureRecursion = _captureRecursion;
window._elementForUnflushedEvents = _elementForUnflushedEvents;
window._captureProxy = _captureProxy;
window._capturedElemChanged = _capturedElemChanged;
window._captureObserver = _captureObserver;
window.setCapture = setCapture;
window.releaseCapture = releaseCapture;
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* See README.md for usage and integration instructions. * See README.md for usage and integration instructions.
*/ */
export default class EventTargetMixin { class EventTargetMixin {
constructor() { constructor() {
this._listeners = new Map(); this._listeners = new Map();
} }
...@@ -33,3 +33,4 @@ export default class EventTargetMixin { ...@@ -33,3 +33,4 @@ export default class EventTargetMixin {
return !event.defaultPrevented; return !event.defaultPrevented;
} }
} }
window.EventTargetMixin = EventTargetMixin;
...@@ -6,10 +6,12 @@ ...@@ -6,10 +6,12 @@
* See README.md for usage and integration instructions. * See README.md for usage and integration instructions.
*/ */
export function toUnsigned32bit(toConvert) { function toUnsigned32bit(toConvert) {
return toConvert >>> 0; return toConvert >>> 0;
} }
export function toSigned32bit(toConvert) { function toSigned32bit(toConvert) {
return toConvert | 0; return toConvert | 0;
} }
window.toUnsigned32bit = toUnsigned32bit;
window.toSigned32bit = toSigned32bit;
...@@ -17,7 +17,7 @@ let Info = () => {}; ...@@ -17,7 +17,7 @@ let Info = () => {};
let Warn = () => {}; let Warn = () => {};
let Error = () => {}; let Error = () => {};
export function initLogging(level) { function initLogging(level) {
if (typeof level === 'undefined') { if (typeof level === 'undefined') {
level = _logLevel; level = _logLevel;
} else { } else {
...@@ -46,11 +46,18 @@ export function initLogging(level) { ...@@ -46,11 +46,18 @@ export function initLogging(level) {
} }
} }
export function getLogging() { function getLogging() {
return _logLevel; return _logLevel;
} }
export { Debug, Info, Warn, Error }; window.Log = { Debug, Info, Warn, Error };
// Initialize logging level // Initialize logging level
initLogging(); initLogging();
window._logLevel = _logLevel;
window.Debug = Debug;
window.Info = Info;
window.Warn = Warn;
window.Error = Error;
window.initLogging = initLogging;
window.getLogging = getLogging;
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* Performs MD5 hashing on a string of binary characters, returns an array of bytes * Performs MD5 hashing on a string of binary characters, returns an array of bytes
*/ */
export function MD5(d) { function MD5(d) {
let r = M(V(Y(X(d), 8 * d.length))); let r = M(V(Y(X(d), 8 * d.length)));
return r; return r;
} }
...@@ -76,4 +76,14 @@ function add(d, g) { ...@@ -76,4 +76,14 @@ function add(d, g) {
function rol(d, g) { function rol(d, g) {
return d << g | d >>> 32 - g; return d << g | d >>> 32 - g;
} }window.MD5 = MD5;
\ No newline at end of file window.X = X;
window.V = V;
window.Y = Y;
window.cmn = cmn;
window.ff = ff;
window.gg = gg;
window.hh = hh;
window.ii = ii;
window.add = add;
window.rol = rol;
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
*/ */
// Decode from UTF-8 // Decode from UTF-8
export function decodeUTF8(utf8string, allowLatin1=false) { function decodeUTF8(utf8string, allowLatin1=false) {
try { try {
return decodeURIComponent(escape(utf8string)); return decodeURIComponent(escape(utf8string));
} catch (e) { } catch (e) {
...@@ -23,6 +23,8 @@ export function decodeUTF8(utf8string, allowLatin1=false) { ...@@ -23,6 +23,8 @@ export function decodeUTF8(utf8string, allowLatin1=false) {
} }
// Encode to UTF-8 // Encode to UTF-8
export function encodeUTF8(DOMString) { function encodeUTF8(DOMString) {
return unescape(encodeURIComponent(DOMString)); return unescape(encodeURIComponent(DOMString));
} }
window.decodeUTF8 = decodeUTF8;
window.encodeUTF8 = encodeUTF8;
...@@ -48,4 +48,4 @@ const adler32 = (adler, buf, len, pos) => { ...@@ -48,4 +48,4 @@ const adler32 = (adler, buf, len, pos) => {
}; };
export default adler32; window.adler32;
...@@ -19,36 +19,64 @@ ...@@ -19,36 +19,64 @@
// misrepresented as being the original software. // misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution. // 3. This notice may not be removed or altered from any source distribution.
export const Z_NO_FLUSH = 0; const Z_NO_FLUSH = 0;
export const Z_PARTIAL_FLUSH = 1; const Z_PARTIAL_FLUSH = 1;
export const Z_SYNC_FLUSH = 2; const Z_SYNC_FLUSH = 2;
export const Z_FULL_FLUSH = 3; const Z_FULL_FLUSH = 3;
export const Z_FINISH = 4; const Z_FINISH = 4;
export const Z_BLOCK = 5; const Z_BLOCK = 5;
export const Z_TREES = 6; const Z_TREES = 6;
export const Z_OK = 0; const Z_OK = 0;
export const Z_STREAM_END = 1; const Z_STREAM_END = 1;
export const Z_NEED_DICT = 2; const Z_NEED_DICT = 2;
export const Z_ERRNO = -1; const Z_ERRNO = -1;
export const Z_STREAM_ERROR = -2; const Z_STREAM_ERROR = -2;
export const Z_DATA_ERROR = -3; const Z_DATA_ERROR = -3;
export const Z_MEM_ERROR = -4; const Z_MEM_ERROR = -4;
export const Z_BUF_ERROR = -5; const Z_BUF_ERROR = -5;
export const Z_NO_COMPRESSION = 0; const Z_NO_COMPRESSION = 0;
export const Z_BEST_SPEED = 1; const Z_BEST_SPEED = 1;
export const Z_BEST_COMPRESSION = 9; const Z_BEST_COMPRESSION = 9;
export const Z_DEFAULT_COMPRESSION = -1; const Z_DEFAULT_COMPRESSION = -1;
export const Z_FILTERED = 1; const Z_FILTERED = 1;
export const Z_HUFFMAN_ONLY = 2; const Z_HUFFMAN_ONLY = 2;
export const Z_RLE = 3; const Z_RLE = 3;
export const Z_FIXED = 4; const Z_FIXED = 4;
export const Z_DEFAULT_STRATEGY = 0; const Z_DEFAULT_STRATEGY = 0;
export const Z_BINARY = 0; const Z_BINARY = 0;
export const Z_TEXT = 1; const Z_TEXT = 1;
export const Z_UNKNOWN = 2; const Z_UNKNOWN = 2;
export const Z_DEFLATED = 8; const Z_DEFLATED = 8;
window.Z_NO_FLUSH = Z_NO_FLUSH;
window.Z_PARTIAL_FLUSH = Z_PARTIAL_FLUSH;
window.Z_SYNC_FLUSH = Z_SYNC_FLUSH;
window.Z_FULL_FLUSH = Z_FULL_FLUSH;
window.Z_FINISH = Z_FINISH;
window.Z_BLOCK = Z_BLOCK;
window.Z_TREES = Z_TREES;
window.Z_OK = Z_OK;
window.Z_STREAM_END = Z_STREAM_END;
window.Z_NEED_DICT = Z_NEED_DICT;
window.Z_ERRNO = Z_ERRNO;
window.Z_STREAM_ERROR = Z_STREAM_ERROR;
window.Z_DATA_ERROR = Z_DATA_ERROR;
window.Z_MEM_ERROR = Z_MEM_ERROR;
window.Z_BUF_ERROR = Z_BUF_ERROR;
window.Z_NO_COMPRESSION = Z_NO_COMPRESSION;
window.Z_BEST_SPEED = Z_BEST_SPEED;
window.Z_BEST_COMPRESSION = Z_BEST_COMPRESSION;
window.Z_DEFAULT_COMPRESSION = Z_DEFAULT_COMPRESSION;
window.Z_FILTERED = Z_FILTERED;
window.Z_HUFFMAN_ONLY = Z_HUFFMAN_ONLY;
window.Z_RLE = Z_RLE;
window.Z_FIXED = Z_FIXED;
window.Z_DEFAULT_STRATEGY = Z_DEFAULT_STRATEGY;
window.Z_BINARY = Z_BINARY;
window.Z_TEXT = Z_TEXT;
window.Z_UNKNOWN = Z_UNKNOWN;
window.Z_DEFLATED = Z_DEFLATED;
...@@ -56,4 +56,6 @@ const crc32 = (crc, buf, len, pos) => { ...@@ -56,4 +56,6 @@ const crc32 = (crc, buf, len, pos) => {
}; };
export default crc32; window.crc32;
window.makeTable = makeTable;
window.crcTable = crcTable;
...@@ -19,23 +19,10 @@ ...@@ -19,23 +19,10 @@
// misrepresented as being the original software. // misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution. // 3. This notice may not be removed or altered from any source distribution.
import { _tr_init, _tr_stored_block, _tr_flush_block, _tr_tally, _tr_align } from './trees.js';
import adler32 from './adler32.js';
import crc32 from './crc32.js';
import msg from './messages.js';
/* Public constants ==========================================================*/ /* Public constants ==========================================================*/
/* ===========================================================================*/ /* ===========================================================================*/
import {
Z_NO_FLUSH, Z_PARTIAL_FLUSH, Z_FULL_FLUSH, Z_FINISH, Z_BLOCK,
Z_OK, Z_STREAM_END, Z_STREAM_ERROR, Z_DATA_ERROR, Z_BUF_ERROR,
Z_DEFAULT_COMPRESSION,
Z_FILTERED, Z_HUFFMAN_ONLY, Z_RLE, Z_FIXED, Z_DEFAULT_STRATEGY,
Z_UNKNOWN,
Z_DEFLATED
} from './constants.js';
/*============================================================================*/ /*============================================================================*/
...@@ -2027,4 +2014,65 @@ const deflateSetDictionary = (strm, dictionary) => { ...@@ -2027,4 +2014,65 @@ const deflateSetDictionary = (strm, dictionary) => {
}; };
export { deflateInit, deflateInit2, deflateReset, deflateResetKeep, deflateSetHeader, deflate, deflateEnd, deflateSetDictionary }; window.deflateInit = deflateInit;
window.deflateInit2 = deflateInit2;
window.deflateReset = deflateReset;
window.deflateResetKeep = deflateResetKeep;
window.deflateSetHeader = deflateSetHeader;
window.deflate = deflate;
window.deflateEnd = deflateEnd;
window.deflateSetDictionary = deflateSetDictionary;
window.MAX_MEM_LEVEL = MAX_MEM_LEVEL;
window.MAX_WBITS = MAX_WBITS;
window.DEF_MEM_LEVEL = DEF_MEM_LEVEL;
window.LENGTH_CODES = LENGTH_CODES;
window.LITERALS = LITERALS;
window.L_CODES = L_CODES;
window.D_CODES = D_CODES;
window.BL_CODES = BL_CODES;
window.HEAP_SIZE = HEAP_SIZE;
window.MAX_BITS = MAX_BITS;
window.MIN_MATCH = MIN_MATCH;
window.MAX_MATCH = MAX_MATCH;
window.MIN_LOOKAHEAD = MIN_LOOKAHEAD;
window.PRESET_DICT = PRESET_DICT;
window.INIT_STATE = INIT_STATE;
window.GZIP_STATE = GZIP_STATE;
window.EXTRA_STATE = EXTRA_STATE;
window.NAME_STATE = NAME_STATE;
window.COMMENT_STATE = COMMENT_STATE;
window.HCRC_STATE = HCRC_STATE;
window.BUSY_STATE = BUSY_STATE;
window.FINISH_STATE = FINISH_STATE;
window.BS_NEED_MORE = BS_NEED_MORE;
window.BS_BLOCK_DONE = BS_BLOCK_DONE;
window.BS_FINISH_STARTED = BS_FINISH_STARTED;
window.BS_FINISH_DONE = BS_FINISH_DONE;
window.OS_CODE = OS_CODE;
window.err = err;
window.rank = rank;
window.zero = zero;
window.slide_hash = slide_hash;
window.HASH_ZLIB = HASH_ZLIB;
window.flush_pending = flush_pending;
window.flush_block_only = flush_block_only;
window.put_byte = put_byte;
window.putShortMSB = putShortMSB;
window.read_buf = read_buf;
window.longest_match = longest_match;
window.fill_window = fill_window;
window.deflate_stored = deflate_stored;
window.deflate_fast = deflate_fast;
window.deflate_slow = deflate_slow;
window.deflate_rle = deflate_rle;
window.deflate_huff = deflate_huff;
window.Config = Config;
window.configuration_table = configuration_table;
window.lm_init = lm_init;
window.DeflateState = DeflateState;
window.deflateStateCheck = deflateStateCheck;
window.deflateResetKeep = deflateResetKeep;
window.deflateSetHeader = deflateSetHeader;
window.deflateInit2 = deflateInit2;
window.deflateEnd = deflateEnd;
window.deflateSetDictionary = deflateSetDictionary;
...@@ -20,8 +20,6 @@ ...@@ -20,8 +20,6 @@
// 3. This notice may not be removed or altered from any source distribution. // 3. This notice may not be removed or altered from any source distribution.
// See state defs from inflate.js // See state defs from inflate.js
const BAD = 16209; /* got a data error -- remain here until reset */
const TYPE = 16191; /* i: waiting for type bits, including last-flag bit */
/* /*
Decode literal, length, and distance codes and write out the resulting Decode literal, length, and distance codes and write out the resulting
...@@ -58,7 +56,7 @@ const TYPE = 16191; /* i: waiting for type bits, including last-flag bit */ ...@@ -58,7 +56,7 @@ const TYPE = 16191; /* i: waiting for type bits, including last-flag bit */
requires strm.avail_out >= 258 for each loop to avoid checking for requires strm.avail_out >= 258 for each loop to avoid checking for
output space. output space.
*/ */
export default function inflate_fast(strm, start) { function inflate_fast(strm, start) {
let _in; /* local strm.input */ let _in; /* local strm.input */
let last; /* have enough input while in < last */ let last; /* have enough input while in < last */
let _out; /* local strm.output */ let _out; /* local strm.output */
...@@ -342,3 +340,5 @@ export default function inflate_fast(strm, start) { ...@@ -342,3 +340,5 @@ export default function inflate_fast(strm, start) {
state.bits = bits; state.bits = bits;
return; return;
}; };
window.inflate_fast = inflate_fast;
...@@ -19,10 +19,6 @@ ...@@ -19,10 +19,6 @@
// misrepresented as being the original software. // misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution. // 3. This notice may not be removed or altered from any source distribution.
import adler32 from './adler32.js';
import crc32 from './crc32.js';
import inflate_fast from './inffast.js';
import inflate_table from './inftrees.js';
const CODES = 0; const CODES = 0;
const LENS = 1; const LENS = 1;
...@@ -31,12 +27,6 @@ const DISTS = 2; ...@@ -31,12 +27,6 @@ const DISTS = 2;
/* Public constants ==========================================================*/ /* Public constants ==========================================================*/
/* ===========================================================================*/ /* ===========================================================================*/
import {
Z_FINISH, Z_BLOCK, Z_TREES,
Z_OK, Z_STREAM_END, Z_NEED_DICT, Z_STREAM_ERROR, Z_DATA_ERROR, Z_MEM_ERROR, Z_BUF_ERROR,
Z_DEFLATED
} from './constants.js';
/* STATES ====================================================================*/ /* STATES ====================================================================*/
/* ===========================================================================*/ /* ===========================================================================*/
...@@ -83,7 +73,6 @@ const ENOUGH_LENS = 852; ...@@ -83,7 +73,6 @@ const ENOUGH_LENS = 852;
const ENOUGH_DISTS = 592; const ENOUGH_DISTS = 592;
//const ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); //const ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS);
const MAX_WBITS = 15;
/* 32K LZ77 window */ /* 32K LZ77 window */
const DEF_WBITS = MAX_WBITS; const DEF_WBITS = MAX_WBITS;
...@@ -1548,9 +1537,66 @@ const inflateSetDictionary = (strm, dictionary) => { ...@@ -1548,9 +1537,66 @@ const inflateSetDictionary = (strm, dictionary) => {
}; };
export { inflateReset, inflateReset2, inflateResetKeep, inflateInit, inflateInit2, inflate, inflateEnd, inflateGetHeader, inflateSetDictionary }; window.inflateReset = inflateReset;
export const inflateInfo = 'pako inflate (from Nodeca project)'; window.inflateReset2 = inflateReset2;
window.inflateResetKeep = inflateResetKeep;
window.inflateInit = inflateInit;
window.inflateInit2 = inflateInit2;
window.inflate = inflate;
window.inflateEnd = inflateEnd;
window.inflateGetHeader = inflateGetHeader;
window.inflateSetDictionary = inflateSetDictionary;
const inflateInfo = 'pako inflate (from Nodeca project)';
/* Not implemented /* Not implemented
export { inflateCodesUsed, inflateCopy, inflateGetDictionary, inflateMark, inflatePrime, inflateSync, inflateSyncPoint, inflateUndermine, inflateValidate }; window. inflateCodesUsed, inflateCopy, inflateGetDictionary, inflateMark, inflatePrime, inflateSync, inflateSyncPoint, inflateUndermine, inflateValidate = {;
*/ */
window.CODES = CODES;
window.LENS = LENS;
window.DISTS = DISTS;
window.HEAD = HEAD;
window.FLAGS = FLAGS;
window.TIME = TIME;
window.OS = OS;
window.EXLEN = EXLEN;
window.EXTRA = EXTRA;
window.NAME = NAME;
window.COMMENT = COMMENT;
window.HCRC = HCRC;
window.DICTID = DICTID;
window.TYPE = TYPE;
window.TYPEDO = TYPEDO;
window.STORED = STORED;
window.COPY_ = COPY_;
window.TABLE = TABLE;
window.LENLENS = LENLENS;
window.CODELENS = CODELENS;
window.LEN_ = LEN_;
window.LENEXT = LENEXT;
window.DISTEXT = DISTEXT;
window.MATCH = MATCH;
window.LIT = LIT;
window.CHECK = CHECK;
window.LENGTH = LENGTH;
window.DONE = DONE;
window.BAD = BAD;
window.MEM = MEM;
window.SYNC = SYNC;
window.ENOUGH_LENS = ENOUGH_LENS;
window.ENOUGH_DISTS = ENOUGH_DISTS;
window.MAX_WBITS = MAX_WBITS;
window.DEF_WBITS = DEF_WBITS;
window.zswap32 = zswap32;
window.InflateState = InflateState;
window.inflateStateCheck = inflateStateCheck;
window.inflateResetKeep = inflateResetKeep;
window.inflateReset2 = inflateReset2;
window.inflateInit2 = inflateInit2;
window.virgin = virgin;
window.lenfix = lenfix;
window.fixedtables = fixedtables;
window.updatewindow = updatewindow;
window.inflateEnd = inflateEnd;
window.inflateGetHeader = inflateGetHeader;
window.inflateSetDictionary = inflateSetDictionary;
window.inflateInfo = inflateInfo;
...@@ -20,14 +20,8 @@ ...@@ -20,14 +20,8 @@
// 3. This notice may not be removed or altered from any source distribution. // 3. This notice may not be removed or altered from any source distribution.
const MAXBITS = 15; const MAXBITS = 15;
const ENOUGH_LENS = 852;
const ENOUGH_DISTS = 592;
//const ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); //const ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS);
const CODES = 0;
const LENS = 1;
const DISTS = 2;
const lbase = new Uint16Array([ /* Length codes 257..285 base */ const lbase = new Uint16Array([ /* Length codes 257..285 base */
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 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 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
...@@ -337,4 +331,14 @@ const inflate_table = (type, lens, lens_index, codes, table, table_index, work, ...@@ -337,4 +331,14 @@ const inflate_table = (type, lens, lens_index, codes, table, table_index, work,
}; };
export default inflate_table; window.inflate_table;
window.MAXBITS = MAXBITS;
window.ENOUGH_LENS = ENOUGH_LENS;
window.ENOUGH_DISTS = ENOUGH_DISTS;
window.CODES = CODES;
window.LENS = LENS;
window.DISTS = DISTS;
window.lbase = lbase;
window.lext = lext;
window.dbase = dbase;
window.dext = dext;
'use strict'; 'use strict';
export default { window.msg = {
2: 'need dictionary', /* Z_NEED_DICT 2 */ 2: 'need dictionary', /* Z_NEED_DICT 2 */
1: 'stream end', /* Z_STREAM_END 1 */ 1: 'stream end', /* Z_STREAM_END 1 */
0: '', /* Z_OK 0 */ 0: '', /* Z_OK 0 */
......
...@@ -28,19 +28,14 @@ ...@@ -28,19 +28,14 @@
//const Z_FILTERED = 1; //const Z_FILTERED = 1;
//const Z_HUFFMAN_ONLY = 2; //const Z_HUFFMAN_ONLY = 2;
//const Z_RLE = 3; //const Z_RLE = 3;
const Z_FIXED = 4;
//const Z_DEFAULT_STRATEGY = 0; //const Z_DEFAULT_STRATEGY = 0;
/* Possible values of the data_type field (though see inflate()) */ /* Possible values of the data_type field (though see inflate()) */
const Z_BINARY = 0;
const Z_TEXT = 1;
//const Z_ASCII = 1; // = Z_TEXT //const Z_ASCII = 1; // = Z_TEXT
const Z_UNKNOWN = 2;
/*============================================================================*/ /*============================================================================*/
function zero(buf) { let len = buf.length; while (--len >= 0) { buf[len] = 0; } }
// From zutil.h // From zutil.h
...@@ -48,9 +43,6 @@ const STORED_BLOCK = 0; ...@@ -48,9 +43,6 @@ const STORED_BLOCK = 0;
const STATIC_TREES = 1; const STATIC_TREES = 1;
const DYN_TREES = 2; const DYN_TREES = 2;
/* The three kinds of block type */ /* The three kinds of block type */
const MIN_MATCH = 3;
const MAX_MATCH = 258;
/* The minimum and maximum match lengths */ /* The minimum and maximum match lengths */
// From deflate.h // From deflate.h
...@@ -58,25 +50,16 @@ const MAX_MATCH = 258; ...@@ -58,25 +50,16 @@ const MAX_MATCH = 258;
* Internal compression state. * Internal compression state.
*/ */
const LENGTH_CODES = 29;
/* number of length codes, not counting the special END_BLOCK code */
const LITERALS = 256;
/* number of literal bytes 0..255 */ /* number of literal bytes 0..255 */
const L_CODES = LITERALS + 1 + LENGTH_CODES;
/* number of Literal or Length codes, including the END_BLOCK code */ /* number of Literal or Length codes, including the END_BLOCK code */
const D_CODES = 30;
/* number of distance codes */ /* number of distance codes */
const BL_CODES = 19;
/* number of codes used to transfer the bit lengths */ /* number of codes used to transfer the bit lengths */
const HEAP_SIZE = 2 * L_CODES + 1;
/* maximum heap size */ /* maximum heap size */
const MAX_BITS = 15;
/* All codes must not exceed MAX_BITS bits */ /* All codes must not exceed MAX_BITS bits */
const Buf_size = 16; const Buf_size = 16;
...@@ -1172,4 +1155,69 @@ const _tr_tally = (s, dist, lc) => { ...@@ -1172,4 +1155,69 @@ const _tr_tally = (s, dist, lc) => {
return (s.sym_next === s.sym_end); return (s.sym_next === s.sym_end);
}; };
export { _tr_init, _tr_stored_block, _tr_flush_block, _tr_tally, _tr_align }; window._tr_init = _tr_init;
window._tr_stored_block = _tr_stored_block;
window._tr_flush_block = _tr_flush_block;
window._tr_tally = _tr_tally;
window._tr_align = _tr_align;
window.zero = zero;
window.STORED_BLOCK = STORED_BLOCK;
window.STATIC_TREES = STATIC_TREES;
window.DYN_TREES = DYN_TREES;
window.MIN_MATCH = MIN_MATCH;
window.MAX_MATCH = MAX_MATCH;
window.LENGTH_CODES = LENGTH_CODES;
window.LITERALS = LITERALS;
window.L_CODES = L_CODES;
window.D_CODES = D_CODES;
window.BL_CODES = BL_CODES;
window.HEAP_SIZE = HEAP_SIZE;
window.MAX_BITS = MAX_BITS;
window.Buf_size = Buf_size;
window.MAX_BL_BITS = MAX_BL_BITS;
window.END_BLOCK = END_BLOCK;
window.REP_3_6 = REP_3_6;
window.REPZ_3_10 = REPZ_3_10;
window.REPZ_11_138 = REPZ_11_138;
window.extra_lbits = extra_lbits;
window.extra_dbits = extra_dbits;
window.extra_blbits = extra_blbits;
window.bl_order = bl_order;
window.DIST_CODE_LEN = DIST_CODE_LEN;
window.static_ltree = static_ltree;
window.static_dtree = static_dtree;
window._dist_code = _dist_code;
window._length_code = _length_code;
window.base_length = base_length;
window.base_dist = base_dist;
window.StaticTreeDesc = StaticTreeDesc;
window.static_l_desc = static_l_desc;
window.static_d_desc = static_d_desc;
window.static_bl_desc = static_bl_desc;
window.TreeDesc = TreeDesc;
window.d_code = d_code;
window.put_short = put_short;
window.send_bits = send_bits;
window.send_code = send_code;
window.bi_reverse = bi_reverse;
window.bi_flush = bi_flush;
window.gen_bitlen = gen_bitlen;
window.gen_codes = gen_codes;
window.tr_static_init = tr_static_init;
window.init_block = init_block;
window.bi_windup = bi_windup;
window.smaller = smaller;
window.pqdownheap = pqdownheap;
window.compress_block = compress_block;
window.build_tree = build_tree;
window.scan_tree = scan_tree;
window.send_tree = send_tree;
window.build_bl_tree = build_bl_tree;
window.send_all_trees = send_all_trees;
window.detect_data_type = detect_data_type;
window.static_init_done = static_init_done;
window._tr_init = _tr_init;
window._tr_stored_block = _tr_stored_block;
window._tr_align = _tr_align;
window._tr_flush_block = _tr_flush_block;
window._tr_tally = _tr_tally;
...@@ -44,4 +44,4 @@ function ZStream() { ...@@ -44,4 +44,4 @@ function ZStream() {
this.adler = 0; this.adler = 0;
} }
export default ZStream; window.ZStream;
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
* read binary data off of the receive queue. * read binary data off of the receive queue.
*/ */
import * as Log from './util/logging.js';
// this has performance issues in some versions Chromium, and // this has performance issues in some versions Chromium, and
// doesn't gain a tremendous amount of performance increase in Firefox // doesn't gain a tremendous amount of performance increase in Firefox
...@@ -47,7 +46,7 @@ const rawChannelProps = [ ...@@ -47,7 +46,7 @@ const rawChannelProps = [
"readyState", "readyState",
]; ];
export default class Websock { class Websock {
constructor() { constructor() {
this._websocket = null; // WebSocket or RTCDataChannel object this._websocket = null; // WebSocket or RTCDataChannel object
...@@ -351,3 +350,8 @@ export default class Websock { ...@@ -351,3 +350,8 @@ export default class Websock {
} }
} }
} }
window.MAX_RQ_GROW_SIZE = MAX_RQ_GROW_SIZE;
window.DataChannel = DataChannel;
window.ReadyStates = ReadyStates;
window.rawChannelProps = rawChannelProps;
window.Websock = Websock;
...@@ -8,10 +8,47 @@ ...@@ -8,10 +8,47 @@
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"> <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
<link rel="stylesheet" href="/novnc.css"> <link rel="stylesheet" href="/novnc.css">
<script type="module"> <script src="/novnc/util/strings.js"></script>
import RFB from '/novnc/rfb.js'; <script src="/novnc/util/logging.js"></script>
window.RFB = RFB; <script src="/novnc/util/browser.js"></script>
</script> <script src="/novnc/util/element.js"></script>
<script src="/novnc/util/events.js"></script>
<script src="/novnc/util/eventtarget.js"></script>
<script src="/novnc/util/int.js"></script>
<script src="/novnc/util/cursor.js"></script>
<script src="/novnc/util/md5.js"></script>
<script src="/novnc/websock.js"></script>
<script src="/novnc/des.js"></script>
<script src="/novnc/input/keysym.js"></script>
<script src="/novnc/input/keysymdef.js"></script>
<script src="/novnc/input/xtscancodes.js"></script>
<script src="/novnc/input/util.js"></script>
<script src="/novnc/input/keyboard.js"></script>
<script src="/novnc/input/gesturehandler.js"></script>
<script src="/novnc/display.js"></script>
<script src="/novnc/inflator.js"></script>
<script src="/novnc/deflator.js"></script>
<script src="/novnc/encodings.js"></script>
<script src="/novnc/ra2.js"></script>
<script src="/novnc/decoders/raw.js"></script>
<script src="/novnc/decoders/copyrect.js"></script>
<script src="/novnc/decoders/rre.js"></script>
<script src="/novnc/decoders/hextile.js"></script>
<script src="/novnc/decoders/tight.js"></script>
<script src="/novnc/decoders/tightpng.js"></script>
<script src="/novnc/decoders/zrle.js"></script>
<script src="/novnc/decoders/jpeg.js"></script>
<script src="/novnc/vendor/pako/lib/zlib/adler32.js"></script>
<script src="/novnc/vendor/pako/lib/zlib/constants.js"></script>
<script src="/novnc/vendor/pako/lib/zlib/crc32.js"></script>
<script src="/novnc/vendor/pako/lib/zlib/deflate.js"></script>
<script src="/novnc/vendor/pako/lib/zlib/inffast.js"></script>
<script src="/novnc/vendor/pako/lib/zlib/inflate.js"></script>
<script src="/novnc/vendor/pako/lib/zlib/inftrees.js"></script>
<script src="/novnc/vendor/pako/lib/zlib/messages.js"></script>
<script src="/novnc/vendor/pako/lib/zlib/trees.js"></script>
<script src="/novnc/vendor/pako/lib/zlib/zstream.js"></script>
<script src="/novnc/rfb.js"></script>
<style> <style>
.navbar-brand { .navbar-brand {
font-weight: bold; font-weight: bold;
...@@ -222,7 +259,9 @@ function connect() { ...@@ -222,7 +259,9 @@ function connect() {
}); });
rfb.addEventListener('connect', () => { rfb.addEventListener('connect', () => {
console.log('VNC connected'); console.log('[VNC-DEBUG] VNC connected event fired');
console.log('[VNC-DEBUG] Connection state:', rfb._rfbConnectionState);
console.log('[VNC-DEBUG] Init state:', rfb._rfbInitState);
connected = true; connected = true;
document.getElementById('connectBtn').disabled = true; document.getElementById('connectBtn').disabled = true;
document.getElementById('disconnectBtn').disabled = false; document.getElementById('disconnectBtn').disabled = false;
...@@ -230,7 +269,9 @@ function connect() { ...@@ -230,7 +269,9 @@ function connect() {
}); });
rfb.addEventListener('disconnect', () => { rfb.addEventListener('disconnect', () => {
console.log('VNC disconnected'); console.log('[VNC-DEBUG] VNC disconnected event fired');
console.log('[VNC-DEBUG] Connection state:', rfb._rfbConnectionState);
console.log('[VNC-DEBUG] Init state:', rfb._rfbInitState);
connected = false; connected = false;
document.getElementById('connectBtn').disabled = false; document.getElementById('connectBtn').disabled = false;
document.getElementById('disconnectBtn').disabled = true; document.getElementById('disconnectBtn').disabled = true;
......
File added
...@@ -279,7 +279,7 @@ static void *websocket_terminal_handler(void *arg) { ...@@ -279,7 +279,7 @@ static void *websocket_terminal_handler(void *arg) {
char *output = terminal_get_output(session, &output_len); char *output = terminal_get_output(session, &output_len);
if (output && output_len > 0) { if (output && output_len > 0) {
printf("WebSocket sending %zu bytes of output for request_id: %s\n", output_len, ws_conn->request_id); printf("WebSocket sending %zu bytes of output for request_id: %s\n", output_len, ws_conn->request_id);
ws_send_frame(ws_conn->ws_conn, WS_OPCODE_BINARY, output, output_len); ws_send_binary_frame(ws_conn->ws_conn, output, output_len);
free(output); free(output);
} }
...@@ -352,6 +352,55 @@ static void *websocket_vnc_handler(void *arg) { ...@@ -352,6 +352,55 @@ static void *websocket_vnc_handler(void *arg) {
} }
pthread_mutex_unlock(&vncs_mutex); pthread_mutex_unlock(&vncs_mutex);
// Wait for VNC connection to be established and RFB handshake to complete
// Add a delay to allow the RFB client to complete the handshake before sending VNC data
sleep(2);
// Wait for the VNC connection to establish (up to 20 seconds)
int wait_count = 0;
const int max_wait = 400; // 20 seconds at 50ms intervals
bool connection_ready = false;
while (ws_conn->active && ws_conn->ws_conn->state == WS_STATE_OPEN && wait_count < max_wait && !connection_ready) {
// Check if VNC session is still active
if (!vnc_is_running(session)) {
printf("WebSocket VNC session ended during connection establishment for request_id: %s\n", ws_conn->request_id);
ws_send_frame(ws_conn->ws_conn, WS_OPCODE_TEXT, "{\"error\":\"VNC session failed to start\"}", 39);
break;
}
// Check for initial output from VNC server
size_t output_len = 0;
char *output = vnc_get_output(session, &output_len);
if (output && output_len > 0) {
printf("WebSocket VNC connection established, received %zu bytes of initial data for request_id: %s\n", output_len, ws_conn->request_id);
if (global_config->debug_vnc) {
printf("VNC initial data (first 20 bytes): ");
for (size_t i = 0; i < output_len && i < 20; i++) {
printf("%02x ", (unsigned char)output[i]);
}
printf("\n");
}
// Send initial data to WebSocket
ws_send_binary_frame(ws_conn->ws_conn, output, output_len);
free(output);
connection_ready = true;
break;
} else if (output) {
free(output);
}
usleep(50000); // 50ms delay
wait_count++;
}
if (!connection_ready) {
printf("WebSocket VNC connection timed out waiting for establishment for request_id: %s\n", ws_conn->request_id);
ws_send_frame(ws_conn->ws_conn, WS_OPCODE_TEXT, "{\"error\":\"VNC connection timeout\"}", 33);
remove_websocket_vnc_connection(ws_conn->request_id);
return NULL;
}
while (ws_conn->active && ws_conn->ws_conn->state == WS_STATE_OPEN) { while (ws_conn->active && ws_conn->ws_conn->state == WS_STATE_OPEN) {
// Check if VNC session is still active // Check if VNC session is still active
if (!vnc_is_running(session)) { if (!vnc_is_running(session)) {
...@@ -373,7 +422,7 @@ static void *websocket_vnc_handler(void *arg) { ...@@ -373,7 +422,7 @@ static void *websocket_vnc_handler(void *arg) {
} }
printf("\n"); printf("\n");
} }
ws_send_frame(ws_conn->ws_conn, WS_OPCODE_BINARY, output, output_len); ws_send_binary_frame(ws_conn->ws_conn, output, output_len);
free(output); free(output);
} }
...@@ -384,7 +433,7 @@ static void *websocket_vnc_handler(void *arg) { ...@@ -384,7 +433,7 @@ static void *websocket_vnc_handler(void *arg) {
if (ws_receive_frame(ws_conn->ws_conn, &opcode, &data, &len)) { if (ws_receive_frame(ws_conn->ws_conn, &opcode, &data, &len)) {
if (opcode == WS_OPCODE_BINARY && len > 0) { if (opcode == WS_OPCODE_BINARY && len > 0) {
// VNC input data // VNC input data - this is raw binary data from noVNC
printf("WebSocket received %zu bytes of VNC input for request_id: %s\n", len, ws_conn->request_id); printf("WebSocket received %zu bytes of VNC input for request_id: %s\n", len, ws_conn->request_id);
if (global_config->debug_vnc) { if (global_config->debug_vnc) {
printf("VNC input data (first 20 bytes): "); printf("VNC input data (first 20 bytes): ");
......
File added
...@@ -1722,12 +1722,7 @@ void *status_thread(void *arg) { ...@@ -1722,12 +1722,7 @@ void *status_thread(void *arg) {
// WebSocket server functions // WebSocket server functions
int websocket_start_server(const wssshd_config_t *config, wssshd_state_t *state) { int websocket_start_server(const wssshd_config_t *config, wssshd_state_t *state) {
printf("Starting WebSocket server on %s:%d", config->host, config->port); printf("Starting WebSocket server on %s:%d (TLS-only)\n", config->host, config->port);
if (config->websocket_tls_only) {
printf(" (TLS-only)\n");
} else {
printf(" (TLS and non-TLS)\n");
}
// Create TCP socket // Create TCP socket
server_sock = socket(AF_INET, SOCK_STREAM, 0); server_sock = socket(AF_INET, SOCK_STREAM, 0);
...@@ -1764,58 +1759,53 @@ int websocket_start_server(const wssshd_config_t *config, wssshd_state_t *state) ...@@ -1764,58 +1759,53 @@ int websocket_start_server(const wssshd_config_t *config, wssshd_state_t *state)
return -1; return -1;
} }
// Initialize SSL only if TLS is enabled // Initialize SSL context (always required for wssshc client connections)
if (config->websocket_tls_only) { printf("Creating SSL context...\n");
printf("Creating SSL context...\n"); ssl_ctx = ssl_create_context();
ssl_ctx = ssl_create_context(); if (!ssl_ctx) {
if (!ssl_ctx) { fprintf(stderr, "Failed to create SSL context\n");
fprintf(stderr, "Failed to create SSL context\n"); close(server_sock);
close(server_sock); return -1;
return -1; }
} printf("SSL context created successfully\n");
printf("SSL context created successfully\n");
// Load or generate certificates
// Load or generate certificates char cert_file[256], key_file[256];
char cert_file[256], key_file[256]; // Try system location first, then fall back to local directory
// Try system location first, then fall back to local directory snprintf(cert_file, sizeof(cert_file), "/etc/wssshd/cert.pem");
snprintf(cert_file, sizeof(cert_file), "/etc/wssshd/cert.pem"); snprintf(key_file, sizeof(key_file), "/etc/wssshd/key.pem");
snprintf(key_file, sizeof(key_file), "/etc/wssshd/key.pem");
// If system location doesn't exist and we can't write there, use local directory
// If system location doesn't exist and we can't write there, use local directory if (access(cert_file, F_OK) != 0) {
if (access(cert_file, F_OK) != 0) { char local_dir[256];
char local_dir[256]; snprintf(local_dir, sizeof(local_dir), "./certs");
snprintf(local_dir, sizeof(local_dir), "./certs"); mkdir(local_dir, 0755); // Create directory if it doesn't exist
mkdir(local_dir, 0755); // Create directory if it doesn't exist snprintf(cert_file, sizeof(cert_file), "./certs/cert.pem");
snprintf(cert_file, sizeof(cert_file), "./certs/cert.pem"); snprintf(key_file, sizeof(key_file), "./certs/key.pem");
snprintf(key_file, sizeof(key_file), "./certs/key.pem"); }
}
printf("Using certificates: %s, %s\n", cert_file, key_file);
if (access(cert_file, F_OK) != 0 || access(key_file, F_OK) != 0) { printf("Using certificates: %s, %s\n", cert_file, key_file);
printf("Generating self-signed certificate...\n");
if (ssl_generate_self_signed_cert(cert_file, key_file) != 0) {
fprintf(stderr, "Failed to generate certificate\n");
SSL_CTX_free(ssl_ctx);
close(server_sock);
return -1;
}
printf("Certificate generated successfully\n");
}
printf("Loading certificates...\n"); if (access(cert_file, F_OK) != 0 || access(key_file, F_OK) != 0) {
if (ssl_load_certificates(ssl_ctx, cert_file, key_file) != 0) { printf("Generating self-signed certificate...\n");
fprintf(stderr, "Failed to load certificates\n"); if (ssl_generate_self_signed_cert(cert_file, key_file) != 0) {
fprintf(stderr, "Failed to generate certificate\n");
SSL_CTX_free(ssl_ctx); SSL_CTX_free(ssl_ctx);
close(server_sock); close(server_sock);
return -1; return -1;
} }
printf("Certificates loaded successfully\n"); printf("Certificate generated successfully\n");
} else {
printf("WebSocket TLS disabled - allowing non-TLS connections\n");
ssl_ctx = NULL;
} }
printf("Loading certificates...\n");
if (ssl_load_certificates(ssl_ctx, cert_file, key_file) != 0) {
fprintf(stderr, "Failed to load certificates\n");
SSL_CTX_free(ssl_ctx);
close(server_sock);
return -1;
}
printf("Certificates loaded successfully\n");
printf("WebSocket server listening on %s:%d\n", config->host, config->port); printf("WebSocket server listening on %s:%d\n", config->host, config->port);
server_running = 1; server_running = 1;
...@@ -1838,68 +1828,29 @@ int websocket_start_server(const wssshd_config_t *config, wssshd_state_t *state) ...@@ -1838,68 +1828,29 @@ int websocket_start_server(const wssshd_config_t *config, wssshd_state_t *state)
continue; continue;
} }
ws_connection_t *conn = NULL; // TLS-only mode: always use SSL
SSL *ssl = NULL; SSL *ssl = SSL_new(ssl_ctx);
if (!ssl) {
if (config->websocket_tls_only) { close(client_sock);
// TLS-only mode: always use SSL continue;
ssl = SSL_new(ssl_ctx); }
if (!ssl) {
close(client_sock);
continue;
}
SSL_set_fd(ssl, client_sock); SSL_set_fd(ssl, client_sock);
if (SSL_accept(ssl) <= 0) { if (SSL_accept(ssl) <= 0) {
ERR_print_errors_fp(stderr); ERR_print_errors_fp(stderr);
SSL_free(ssl); SSL_free(ssl);
close(client_sock); close(client_sock);
continue; continue;
} }
// Create WebSocket connection with SSL // Create WebSocket connection with SSL
conn = ws_connection_create(ssl, client_sock); ws_connection_t *conn = ws_connection_create(ssl, client_sock);
if (!conn) { if (!conn) {
// ws_connection_create failed, clean up SSL and socket // ws_connection_create failed, clean up SSL and socket
SSL_free(ssl); SSL_free(ssl);
close(client_sock); close(client_sock);
continue; continue;
}
} else {
// Mixed mode: try TLS first, fall back to plain WebSocket
ssl = SSL_new(ssl_ctx);
if (ssl) {
SSL_set_fd(ssl, client_sock);
if (SSL_accept(ssl) > 0) {
// TLS handshake successful
conn = ws_connection_create(ssl, client_sock);
if (!conn) {
SSL_free(ssl);
close(client_sock);
continue;
}
} else {
// TLS handshake failed, try plain WebSocket
SSL_free(ssl);
ssl = NULL;
// Create plain WebSocket connection
conn = ws_connection_create_plain(client_sock);
if (!conn) {
close(client_sock);
continue;
}
}
} else {
// SSL context creation failed, use plain WebSocket
conn = ws_connection_create(NULL, client_sock);
if (!conn) {
close(client_sock);
continue;
}
}
} }
// Start client handler thread // Start client handler thread
......
...@@ -532,6 +532,64 @@ bool ws_receive_frame(ws_connection_t *conn, uint8_t *opcode, void **data, size_ ...@@ -532,6 +532,64 @@ bool ws_receive_frame(ws_connection_t *conn, uint8_t *opcode, void **data, size_
return true; return true;
} }
// Send raw WebSocket binary frame (for VNC connections)
bool ws_send_binary_frame(ws_connection_t *conn, const void *data, size_t len) {
if (!conn || conn->state != WS_STATE_OPEN) {
return false;
}
// Simple WebSocket binary frame construction for VNC
// No masking (server-to-client), no complex retry logic
size_t header_len = 2;
if (len >= 126) {
if (len < 65536) {
header_len = 4;
} else {
header_len = 10;
}
}
size_t frame_len = header_len + len;
uint8_t *frame = malloc(frame_len);
if (!frame) {
return false;
}
// Frame header - binary frame, no masking
frame[0] = 0x82; // FIN + binary opcode
if (len < 126) {
frame[1] = len & 0x7F; // No mask bit
} else if (len < 65536) {
frame[1] = 126 & 0x7F; // No mask bit
frame[2] = (len >> 8) & 0xFF;
frame[3] = len & 0xFF;
} else {
frame[1] = 127 & 0x7F; // No mask bit
frame[2] = frame[3] = frame[4] = frame[5] = 0;
frame[6] = (len >> 24) & 0xFF;
frame[7] = (len >> 16) & 0xFF;
frame[8] = (len >> 8) & 0xFF;
frame[9] = len & 0xFF;
}
// Copy data
if (len > 0) {
memcpy(frame + header_len, data, len);
}
// Send frame
int total_written = 0;
if (conn->ssl) {
total_written = SSL_write(conn->ssl, frame, frame_len);
} else {
total_written = write(conn->sock_fd, frame, frame_len);
}
free(frame);
return total_written == (int)frame_len;
}
// Check if WebSocket connection is healthy // Check if WebSocket connection is healthy
bool ws_connection_is_healthy(ws_connection_t *conn) { bool ws_connection_is_healthy(ws_connection_t *conn) {
// Simple health check - just verify connection is open and has SSL context or socket // Simple health check - just verify connection is open and has SSL context or socket
......
...@@ -72,6 +72,7 @@ bool ws_perform_handshake(ws_connection_t *conn); ...@@ -72,6 +72,7 @@ bool ws_perform_handshake(ws_connection_t *conn);
// Frame operations // Frame operations
bool ws_send_frame(ws_connection_t *conn, uint8_t opcode, const void *data, size_t len); bool ws_send_frame(ws_connection_t *conn, uint8_t opcode, const void *data, size_t len);
bool ws_send_binary_frame(ws_connection_t *conn, const void *data, size_t len);
bool ws_receive_frame(ws_connection_t *conn, uint8_t *opcode, void **data, size_t *len); bool ws_receive_frame(ws_connection_t *conn, uint8_t *opcode, void **data, size_t *len);
// Connection health monitoring // Connection health monitoring
......
File added
...@@ -681,8 +681,8 @@ int run_pipe_mode(wsssh_config_t *config, const char *client_id, const char *wss ...@@ -681,8 +681,8 @@ int run_pipe_mode(wsssh_config_t *config, const char *client_id, const char *wss
char *best_tunnel_control = select_best_transport(expanded_tunnel_control); char *best_tunnel_control = select_best_transport(expanded_tunnel_control);
snprintf(tunnel_request_msg, sizeof(tunnel_request_msg), snprintf(tunnel_request_msg, sizeof(tunnel_request_msg),
"{\"type\":\"tunnel_request\",\"client_id\":\"%s\",\"request_id\":\"%s\",\"tunnel\":\"%s\",\"tunnel_control\":\"%s\",\"service\":\"ssh\",\"version\":\"%s\"}", "{\"type\":\"tunnel_request\",\"client_id\":\"%s\",\"request_id\":\"%s\",\"tunnel\":\"%s\",\"tunnel_control\":\"%s\",\"service\":\"%s\",\"version\":\"%s\"}",
client_id, request_id, best_tunnel ? best_tunnel : expanded_tunnel, best_tunnel_control ? best_tunnel_control : expanded_tunnel_control, WSSSH_VERSION); client_id, request_id, best_tunnel ? best_tunnel : expanded_tunnel, best_tunnel_control ? best_tunnel_control : expanded_tunnel_control, config->service ? config->service : "ssh", WSSSH_VERSION);
if (!send_websocket_frame(ws_ssl, tunnel_request_msg)) { if (!send_websocket_frame(ws_ssl, tunnel_request_msg)) {
free(expanded_tunnel); free(expanded_tunnel);
...@@ -1211,10 +1211,10 @@ void *handle_connection(void *arg) { ...@@ -1211,10 +1211,10 @@ void *handle_connection(void *arg) {
char *best_tunnel_control = select_best_transport(expanded_tunnel_control); char *best_tunnel_control = select_best_transport(expanded_tunnel_control);
snprintf(tunnel_request_msg, sizeof(tunnel_request_msg), snprintf(tunnel_request_msg, sizeof(tunnel_request_msg),
"{\"type\":\"tunnel_request\",\"client_id\":\"%s\",\"request_id\":\"%s\",\"tunnel\":\"%s\",\"tunnel_control\":\"%s\",\"service\":\"ssh\",\"version\":\"%s\"}", "{\"type\":\"tunnel_request\",\"client_id\":\"%s\",\"request_id\":\"%s\",\"tunnel\":\"%s\",\"tunnel_control\":\"%s\",\"service\":\"%s\",\"version\":\"%s\"}",
client_id, new_tunnel->request_id, client_id, new_tunnel->request_id,
best_tunnel ? best_tunnel : expanded_tunnel, best_tunnel ? best_tunnel : expanded_tunnel,
best_tunnel_control ? best_tunnel_control : expanded_tunnel_control, WSSSH_VERSION); best_tunnel_control ? best_tunnel_control : expanded_tunnel_control, config->service ? config->service : "ssh", WSSSH_VERSION);
if (!send_websocket_frame(ws_ssl, tunnel_request_msg)) { if (!send_websocket_frame(ws_ssl, tunnel_request_msg)) {
free(expanded_tunnel); free(expanded_tunnel);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment