├── debian ├── docs ├── source │ └── format ├── rules ├── control ├── copyright └── changelog ├── vncpatches ├── series └── tls-auth-pluging.patch ├── LibVNCServer-0.9.14.tar.gz ├── .gitignore ├── .clang-format ├── TODO ├── vncterm.pod ├── README ├── Makefile ├── genfont2.c ├── genfont.c ├── vncterm.h └── vncterm.c /debian/docs: -------------------------------------------------------------------------------- 1 | debian/SOURCE 2 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /vncpatches/series: -------------------------------------------------------------------------------- 1 | tls-auth-pluging.patch 2 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | %: 4 | dh $@ 5 | -------------------------------------------------------------------------------- /LibVNCServer-0.9.14.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proxmox/vncterm/HEAD/LibVNCServer-0.9.14.tar.gz -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | vncterm 3 | libvncserver-LibVNCServer-0.9.11/ 4 | # wchardata.c is copied from unifont (/usr/share/unifont/wchardata.c) 5 | wchardata.c 6 | *.deb 7 | *.buildinfo 8 | *.changes 9 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | ColumnLimit: 100 3 | IndentWidth: 4 4 | AlignAfterOpenBracket: BlockIndent 5 | BinPackParameters: false # TODO: evaluate using OnePerLine (needs clang 20+) for a balanance? 6 | AlwaysBreakBeforeMultilineStrings: true 7 | InsertBraces: true # needs clang-format 15 or newer. 8 | AlignTrailingComments: 9 | Kind: Never 10 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | 2 | TODO: 3 | better command line parser - print usage infos 4 | implement some kind of regression testing 5 | cleanup the TLS patch and try to merge into upstream 6 | scroll/history support? 7 | more ESC sequences (see xterm source ctlseqs.txt) 8 | disp_ctrl 9 | add arabic font glyphs 10 | add japanese font glyphs 11 | add other font glyphs (maybe http://openlab.jp/efont/unicode/)? 12 | 13 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: vncterm 2 | Section: admin 3 | Priority: optional 4 | Maintainer: Proxmox Support Team 5 | Build-Depends: cmake, 6 | debhelper-compat (= 12), 7 | libglib2.0-dev, 8 | libgnutls28-dev, 9 | libjpeg62-turbo-dev, 10 | libnsl-dev, 11 | libpng-dev, 12 | quilt, 13 | unifont, 14 | zlib1g-dev, 15 | Standards-Version: 4.6.1 16 | 17 | Package: vncterm 18 | Architecture: any 19 | Section: x11 20 | Priority: optional 21 | Depends: libgnutls-deb0-28 | libgnutls30, 22 | libjpeg62-turbo, 23 | libpng16-16, 24 | zlib1g (>= 1:1.2.1), 25 | ${misc:Depends}, 26 | ${shlibs:Depends}, 27 | Description: VNC Terminal Emulator 28 | With vncterm you can start commands and export its standard input and 29 | output to any VNC client (simulating a xterm Terminal). 30 | -------------------------------------------------------------------------------- /vncterm.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | vncterm - VNC Terminal Emulator 4 | 5 | =head1 SYNOPSIS 6 | 7 | vncterm [VNCOPTS] [-c command [ARGS]] 8 | 9 | =head1 DESCRIPTION 10 | 11 | Executes redirecting stdin from a vncviewer and stdout & stderr 12 | to the VNC clients. Implements a 'xterm' compatible terminal. 13 | 14 | =head1 SEE ALSO 15 | 16 | x11vnc(1). 17 | 18 | =head1 AUTHOR 19 | 20 | Dietmar Maurer 21 | 22 | Many thanks to Proxmox Server Solutions (www.proxmox.com) for sponsoring 23 | this work. 24 | 25 | =head1 COPYRIGHT AND DISCLAIMER 26 | 27 | Copyright (C) 2007 Proxmox Server Solutions GmbH 28 | 29 | Copyright: vncterm is under GNU GPL, the GNU General Public License. 30 | 31 | This program is free software; you can redistribute it and/or modify 32 | it under the terms of the GNU General Public License as published by 33 | the Free Software Foundation; either version 2 of the License, or 34 | (at your option) any later version. 35 | 36 | This program is distributed in the hope that it will be useful, 37 | but WITHOUT ANY WARRANTY; without even the implied warranty of 38 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 39 | GNU General Public License for more details. 40 | 41 | You should have received a copy of the GNU General Public License along 42 | with this program; if not, write to the Free Software Foundation, Inc., 43 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 44 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Simple VNC terminal emulator (inspired by VncCommand) 2 | 3 | We try to simulate a 'xterm', because we want to support: 4 | 5 | - unicode 6 | - mouse 7 | 8 | and xterm seems to be the best solution for that. 9 | 10 | some code is inspired by: qemu, VncCommand, xterm, linux kernel, ... 11 | 12 | libvncserver 13 | ============ 14 | 15 | LibVNCServer does not suppurt TLS currently. So we staically link to a 16 | patched versions on that library instead of using the debian shared 17 | libraries. 18 | 19 | VNC Java Applet (for use in web based applications): 20 | ==================================================== 21 | 22 | We use patched VncViewer files (TigerVNC 1.1.0 sources): 23 | 24 | http://www.tigervnc.com/ 25 | 26 | We sign the applet to make clipboard working. 27 | 28 | Note: Newer tigervnc 1.2.0 always use a toplevel window, which 29 | make it basically useless as applet. 30 | 31 | http://sourceforge.net/tracker/?func=detail&aid=3488166&group_id=254363&atid=1126849 32 | 33 | I put patches for 1.2.0 to newtigerpatches subdir. 34 | 35 | Note: javascript-events.patch requires JSObject, which is inside plugin.jar 36 | previously provided by sun-java-jdk. For wheezy/openjdk, we need to install 37 | package "icedtea-netx-common" which contains that file. 38 | 39 | TODO: 40 | ===== 41 | 42 | Useful sources for terminal emulation: 43 | 44 | xterm source: ctlseqs.txt 45 | http://vt100.net/docs/vt220-rm 46 | man 5 terminfo 47 | infocmp xterm 48 | man console_codes 49 | 50 | Fonts: 51 | 52 | we currently use fonts provided by the debian console-data package: 53 | see /usr/share/consolefonts/* 54 | 55 | Only 8x16 fonts are supported 56 | 57 | TODO contains a list of unimplemented things. 58 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Source: git://git.proxmox.com/git/vncterm.git 3 | 4 | Files: 5 | * 6 | Copyright: 2007 - 2023, Proxmox Support Team 7 | License: AGPL-3.0-or-later 8 | This program is free software: you can redistribute it and/or modify it under 9 | the terms of the GNU Affero General Public License as published by the Free 10 | Software Foundation, either version 3 of the License, or (at your option) any 11 | later version. 12 | . 13 | This program is distributed in the hope that it will be useful, but WITHOUT 14 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more 16 | details. 17 | . 18 | You should have received a copy of the GNU Affero General Public License along 19 | with this program. If not, see . 20 | Comment: 21 | The authors take the provided option to use the LibVNC as GPL 2 *or later*, 22 | and use it as GPL 3, allowing combination with our AGPLv3 code. 23 | 24 | Files: 25 | LibVNCServer-*.tar* 26 | Copyright: 2001-2003, Johannes E. Schindelin 27 | 2001 - 2022, The LibVNC Authors 28 | License: GPL-2+ 29 | This program is free software; you can redistribute it and/or modify 30 | it under the terms of the GNU General Public License as published by 31 | the Free Software Foundation; either version 2 of the License, or 32 | (at your option) any later version. 33 | . 34 | This program is distributed in the hope that it will be useful, 35 | but WITHOUT ANY WARRANTY; without even the implied warranty of 36 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 37 | GNU General Public License for more details. 38 | . 39 | You should have received a copy of the GNU General Public License 40 | along with this package; if not, see <https://www.gnu.org/licenses/>. 41 | Comment: 42 | On Debian systems, the full text of the GNU General Public License 43 | version 2 can be found in the file '/usr/share/common-licenses/GPL-2'. 44 | -------------------------------------------------------------------------------- /vncpatches/tls-auth-pluging.patch: -------------------------------------------------------------------------------- 1 | Index: libvncserver-LibVNCServer-0.9.13/libvncserver/auth.c 2 | =================================================================== 3 | --- libvncserver-LibVNCServer-0.9.13.orig/libvncserver/auth.c 4 | +++ libvncserver-LibVNCServer-0.9.13/libvncserver/auth.c 5 | @@ -301,7 +301,8 @@ rfbAuthNewClient(rfbClientPtr cl) 6 | 7 | if (!cl->screen->authPasswdData || cl->reverseConnection) { 8 | /* chk if this condition is valid or not. */ 9 | - securityType = rfbSecTypeNone; 10 | + /* we disable anonymous auth */ 11 | + // securityType = rfbSecTypeNone; 12 | } else if (cl->screen->authPasswdData) { 13 | securityType = rfbSecTypeVncAuth; 14 | } 15 | Index: libvncserver-LibVNCServer-0.9.13/libvncserver/sockets.c 16 | =================================================================== 17 | --- libvncserver-LibVNCServer-0.9.13.orig/libvncserver/sockets.c 18 | +++ libvncserver-LibVNCServer-0.9.13/libvncserver/sockets.c 19 | @@ -638,7 +638,11 @@ rfbReadExactTimeout(rfbClientPtr cl, cha 20 | n = read(sock, buf, len); 21 | } 22 | #else 23 | - n = read(sock, buf, len); 24 | + if (cl->sock_read_fn) { 25 | + n = cl->sock_read_fn(cl, buf, len); 26 | + } else { 27 | + n = read(sock, buf, len); 28 | + } 29 | #endif 30 | 31 | if (n > 0) { 32 | @@ -826,7 +830,11 @@ rfbWriteExact(rfbClientPtr cl, 33 | n = rfbssl_write(cl, buf, len); 34 | else 35 | #endif 36 | + if (cl->sock_write_fn) { 37 | + n = cl->sock_write_fn(cl, buf, len); 38 | + } else { 39 | n = write(sock, buf, len); 40 | + } 41 | 42 | if (n > 0) { 43 | 44 | Index: libvncserver-LibVNCServer-0.9.13/rfb/rfb.h 45 | =================================================================== 46 | --- libvncserver-LibVNCServer-0.9.13.orig/rfb/rfb.h 47 | +++ libvncserver-LibVNCServer-0.9.13/rfb/rfb.h 48 | @@ -411,6 +411,9 @@ typedef struct _rfbStatList { 49 | typedef struct _rfbSslCtx rfbSslCtx; 50 | typedef struct _wsCtx wsCtx; 51 | 52 | +typedef ssize_t (*sock_read_fn_t)(struct _rfbClientRec *cl, void *buf, size_t count); 53 | +typedef ssize_t (*sock_write_fn_t)(struct _rfbClientRec *cl, const void *buf, size_t count); 54 | + 55 | typedef struct _rfbClientRec { 56 | 57 | /** back pointer to the screen */ 58 | @@ -431,6 +434,10 @@ typedef struct _rfbClientRec { 59 | void* clientData; 60 | ClientGoneHookPtr clientGoneHook; 61 | 62 | + /* use to hook up TLS read/write */ 63 | + sock_read_fn_t sock_read_fn; 64 | + sock_read_fn_t sock_write_fn; 65 | + 66 | rfbSocket sock; 67 | char *host; 68 | 69 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include /usr/share/dpkg/pkg-info.mk 2 | include /usr/share/dpkg/architecture.mk 3 | 4 | PACKAGE=vncterm 5 | BUILDDIR ?= $(PACKAGE)-$(DEB_VERSION_UPSTREAM) 6 | 7 | VNCVER=0.9.14 8 | VNCREL=LibVNCServer-$(VNCVER) 9 | VNCDIR=libvncserver-$(VNCREL) 10 | VNCSRC=$(VNCREL).tar.gz 11 | VNCLIB=$(VNCDIR)/libvncserver.a 12 | 13 | DSC = $(PACKAGE)_$(DEB_VERSION).dsc 14 | 15 | DEB=$(PACKAGE)_$(DEB_VERSION)_$(DEB_HOST_ARCH).deb 16 | DBG_DEB=$(PACKAGE)-dbgsym_$(DEB_VERSION)_$(DEB_HOST_ARCH).deb 17 | 18 | CPPFLAGS += -O2 -g -Wall -Wno-deprecated-declarations -D_GNU_SOURCE -I $(VNCDIR) 19 | 20 | VNC_LIBS := -lnsl -lpthread -lz -ljpeg -lutil -lgnutls -lpng 21 | 22 | all: vncterm 23 | 24 | font.data: genfont2 25 | ./genfont2 -o font.data.tmp -i /usr/share/unifont/unifont.hex 26 | mv font.data.tmp font.data 27 | 28 | genfont2: genfont2.c 29 | gcc -g -O2 -o $@ genfont2.c -Wall -Wextra -D_GNU_SOURCE -lz 30 | 31 | .PHONY: vnc 32 | vnc: $(VNCLIB) 33 | $(VNCLIB): $(VNCSRC) 34 | rm -rf $(VNCDIR) 35 | tar xf $(VNCSRC) 36 | ln -s ../vncpatches $(VNCDIR)/patches 37 | cd $(VNCDIR); quilt push -a 38 | cd $(VNCDIR); cmake -D WITH_GNUTLS=OFF -D WITH_OPENSSL=OFF -D WITH_WEBSOCKETS=OFF -D WITH_SYSTEMD=OFF -D WITH_TIGHTVNC_FILETRANSFER=OFF -D WITH_GCRYPT=OFF -D WITH_LZO=OFF -D BUILD_SHARED_LIBS=OFF .; cmake --build . 39 | 40 | vncterm: vncterm.c wchardata.c $(VNCLIB) 41 | $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $^ $(VNC_LIBS) 42 | 43 | wchardata.c: 44 | cp /usr/share/unifont/$@ $@ 45 | 46 | .PHONY: install 47 | install: vncterm vncterm.1 font.data 48 | mkdir -p $(DESTDIR)/usr/share/$(PACKAGE) 49 | install -m 0644 font.data $(DESTDIR)/usr/share/$(PACKAGE) 50 | mkdir -p $(DESTDIR)/usr/share/man/man1 51 | install -m 0644 vncterm.1 $(DESTDIR)/usr/share/man/man1 52 | mkdir -p $(DESTDIR)/usr/bin 53 | install -m 0755 vncterm $(DESTDIR)/usr/bin 54 | 55 | .PHONY: dinstall 56 | dinstall: $(DEB) 57 | dpkg -i $(DEB) 58 | 59 | vncterm.1: vncterm.pod 60 | rm -f $@ 61 | pod2man -n $< -s 1 -r $(DEB_VERSION_UPSTREAM) <$< >$@ 62 | 63 | $(BUILDDIR): 64 | rm -rf $@ $@.tmp 65 | rsync -a . $@.tmp 66 | echo "git clone git://git.proxmox.com/git/vncterm.git\\ngit checkout $$(git rev-parse HEAD)" > $@.tmp/debian/SOURCE 67 | mv $@.tmp $@ 68 | 69 | .PHONY: deb 70 | deb: $(DEB) 71 | $(DEB) $(DBG_DEB) &: $(BUILDDIR) 72 | cd $(BUILDDIR); dpkg-buildpackage -rfakeroot -b -us -uc 73 | lintian $(DEB) 74 | 75 | .PHONY: dsc 76 | dsc: $(DSC) 77 | rm -rf $(BUILDDIR) $(DSC) 78 | $(MAKE) $(DSC) 79 | lintian $(DSC) 80 | 81 | $(DSC): $(BUILDDIR) 82 | cd $(BUILDDIR); dpkg-buildpackage -S -us -uc -d 83 | 84 | sbuild: $(DSC) 85 | sbuild $< 86 | 87 | .PHONY: upload 88 | upload: UPLOAD_DIST ?= $(DEB_DISTRIBUTION) 89 | upload: $(DEB) 90 | tar cf - $(DEB) $(DBG_DEB) | ssh -X repoman@repo.proxmox.com -- upload --product pve --dist $(UPLOAD_DIST) 91 | 92 | .PHONY: clean 93 | clean: 94 | rm -f *.dsc *.deb $(PACKAGE)*.tar* *.changes *.build *.buildinfo 95 | rm -f vncterm vncterm.1 genfont genfont2 *~ *.tmp wchardata.c font.data 96 | rm -rf $(VNCDIR) $(PACKAGE)-[0-9]*/ 97 | 98 | .PHONY: distclean 99 | distclean: clean 100 | -------------------------------------------------------------------------------- /genfont2.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (C) 2017 Proxmox Server Solutions GmbH 4 | 5 | Copyright: vncterm is under GNU GPL, the GNU General Public License. 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; version 2 dated June, 1991. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 19 | 02111-1307, USA. 20 | 21 | Author: Dominik Csapak 22 | 23 | This tool converts the unifont.hex file format into 24 | a binary format used in vncterm to render glyphs. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #define NUMCODEPOINTS 0xFFFF 34 | #define GLYPHLINES 16 35 | #define INDEXLENGTH 4 36 | 37 | /* parses strings like 00F0 to the integer */ 38 | long parsehex(char *val, size_t length) { 39 | unsigned int value = 0; 40 | 41 | for (size_t i = 0; i < length; i++) { 42 | value *= 16; 43 | if (val[i] >= '0' && val[i] <= '9') { 44 | value += (val[i] - '0'); 45 | } else if (val[i] >= 'A' && val[i] <= 'F') { 46 | value += (val[i] - 'A' + 10); 47 | } else if (val[i] >= 'a' && val[i] <= 'f') { 48 | value += (val[i] - 'a' + 10); 49 | } else { 50 | return -1; 51 | } 52 | } 53 | 54 | return value; 55 | } 56 | 57 | void usage(char **argv) { 58 | printf("Usage: %s [OPTION]...\n", argv[0]); 59 | printf("Converts font data from hex format into binary format used by vncterm.\n"); 60 | 61 | printf("\n"); 62 | printf(" -o, --output file for output, if omitted, write to STDOUT\n"); 63 | printf(" -i, --input file for input, if omitted read from STDIN\n"); 64 | printf(" -h, --help display this help\n"); 65 | 66 | printf("\nThe input has to be formatted in the hex format of unifont.\n"); 67 | } 68 | 69 | int main(int argc, char **argv) { 70 | FILE *fd; 71 | FILE *outfd; 72 | char *line = NULL; 73 | char *tmp = NULL; 74 | char *fontfile = NULL; 75 | char *outfile = NULL; 76 | size_t linesize = 0; 77 | uint8_t emptyglyph[GLYPHLINES * 2] = {0}; 78 | uint8_t glyph[GLYPHLINES * 2] = {0}; 79 | int nextcodepoint = 0; 80 | int codepoint = 0; 81 | int c; 82 | 83 | static struct option long_options[] = { 84 | {"help", no_argument, 0, 'h'}, 85 | {"output", required_argument, 0, 'o'}, 86 | {"input", required_argument, 0, 'i'}, 87 | {0, 0, 0, 0} 88 | }; 89 | int option_index = 0; 90 | 91 | while ((c = getopt_long(argc, argv, "hi:o:", long_options, &option_index)) != -1) { 92 | switch (c) { 93 | case 'h': 94 | usage(argv); 95 | exit(0); 96 | break; 97 | case 'o': 98 | outfile = optarg; 99 | break; 100 | case 'i': 101 | fontfile = optarg; 102 | break; 103 | default: 104 | usage(argv); 105 | exit(1); 106 | } 107 | } 108 | 109 | if (fontfile != NULL) { 110 | fd = fopen(fontfile, "r"); 111 | if (fd == NULL) { 112 | fprintf(stderr, "Error opening '%s'\n", fontfile); 113 | perror(NULL); 114 | exit(2); 115 | } 116 | } else { 117 | fd = stdin; 118 | } 119 | 120 | if (outfile != NULL) { 121 | outfd = fopen(outfile, "w"); 122 | if (outfd == NULL) { 123 | fprintf(stderr, "Error opening '%s'\n", outfile); 124 | perror(NULL); 125 | exit(2); 126 | } 127 | } else { 128 | outfd = stdout; 129 | } 130 | 131 | while (getline(&line, &linesize, fd) != -1) { 132 | codepoint = parsehex(line, INDEXLENGTH); 133 | if (codepoint == -1) { 134 | fprintf(stderr, "Cannot parse codepoint index: '%s'\n", line); 135 | free(line); 136 | exit(4); 137 | } 138 | 139 | /* fill in missing codepoints with empty glyphs */ 140 | while (nextcodepoint++ < codepoint) { 141 | fwrite(emptyglyph, sizeof(emptyglyph), 1, outfd); 142 | } 143 | 144 | tmp = line + INDEXLENGTH + 1; 145 | size_t i = 0; 146 | 147 | /* parse until end of line */ 148 | while (*(tmp + i * 2) != '\n' && i < sizeof(glyph)) { 149 | int value = parsehex(tmp + i * 2, 2); 150 | 151 | if (value == -1) { 152 | fprintf( 153 | stderr, "Cannot parse glyph from line: '%s' at position %ld ('%s')\n", line, 154 | i * 2, tmp + i * 2 155 | ); 156 | free(line); 157 | exit(4); 158 | } 159 | 160 | glyph[i++] = (uint8_t)value; 161 | } 162 | 163 | /* if we have a 1width glyph, fill the rest with zeroes */ 164 | while (i < sizeof(glyph)) { 165 | glyph[i++] = 0; 166 | } 167 | 168 | fwrite(glyph, sizeof(glyph), 1, outfd); 169 | } 170 | 171 | if (errno) { 172 | perror("Cannot not read line from file"); 173 | } 174 | 175 | while (nextcodepoint++ <= NUMCODEPOINTS) { 176 | fwrite(emptyglyph, sizeof(emptyglyph), 1, outfd); 177 | } 178 | 179 | free(line); 180 | exit(0); 181 | } 182 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | vncterm (1.9.1) trixie; urgency=medium 2 | 3 | * osc: remove buffer that was only used for debug information. 4 | 5 | -- Proxmox Support Team Wed, 17 Sep 2025 17:07:44 +0200 6 | 7 | vncterm (1.9.0) trixie; urgency=medium 8 | 9 | * re-build for Debian Trixie based releases. 10 | 11 | -- Proxmox Support Team Tue, 17 Jun 2025 11:22:12 +0200 12 | 13 | vncterm (1.8.0) bookworm; urgency=medium 14 | 15 | * re-build for Debian 12 Bookworm based releases 16 | 17 | * update libvncserver to 0.9.14 18 | 19 | -- Proxmox Support Team Mon, 29 May 2023 17:10:38 +0200 20 | 21 | vncterm (1.7-1) bullseye; urgency=medium 22 | 23 | * rebuild for Debian 11 Bullseye based releases 24 | 25 | -- Proxmox Support Team Sun, 16 May 2021 19:06:09 +0200 26 | 27 | vncterm (1.6-2) pve pmg; urgency=medium 28 | 29 | * import libvncserver 0.9.13 upstream release 30 | 31 | -- Proxmox Support Team Wed, 15 Jul 2020 07:05:12 +0200 32 | 33 | vncterm (1.6-1) pve pmg; urgency=medium 34 | 35 | * rebuild for Debian Buster 36 | 37 | -- Proxmox Support Team Wed, 22 May 2019 19:42:26 +0200 38 | 39 | vncterm (1.5-3) unstable; urgency=medium 40 | 41 | * depend on libjpeg62-turbo instead of libjpeg62 42 | 43 | -- Proxmox Support Team Wed, 15 Nov 2017 12:46:19 +0100 44 | 45 | vncterm (1.5-2) unstable; urgency=medium 46 | 47 | * replaced pure plack cursor with a more visible pointer with white edges 48 | 49 | -- Proxmox Support Team Wed, 07 Jun 2017 13:53:17 +0200 50 | 51 | vncterm (1.5-1) unstable; urgency=medium 52 | 53 | * use unifont to support more glyphs (range 0x0000..0xFFFF) 54 | 55 | * support wide characters 56 | 57 | * support combining glyphs 58 | 59 | -- Proxmox Support Team Wed, 24 May 2017 13:12:55 +0200 60 | 61 | vncterm (1.4-2) unstable; urgency=medium 62 | 63 | * fix bound checking on cursor move 64 | 65 | -- Proxmox Support Team Sat, 06 May 2017 08:09:29 +0200 66 | 67 | vncterm (1.4-1) unstable; urgency=medium 68 | 69 | * recompile for Debian Stretch / PVE 5 70 | 71 | -- Proxmox Support Team Fri, 10 Mar 2017 10:19:48 +0100 72 | 73 | vncterm (1.3-2) unstable; urgency=medium 74 | 75 | * bound check utf8 characters 76 | 77 | * detect empty first parameter of escape codes 78 | 79 | -- Proxmox Support Team Fri, 10 Mar 2017 10:11:00 +0100 80 | 81 | vncterm (1.3-1) unstable; urgency=low 82 | 83 | * update cipher suites to GnuTLS's NORMAL 84 | 85 | * allow building with GnuTLS 3.5 86 | 87 | * update LibVNCServer to 0.9.11 88 | 89 | -- Proxmox Support Team Fri, 3 Feb 2017 08:52:31 +0100 90 | 91 | vncterm (1.2-1) unstable; urgency=low 92 | 93 | * recompile for Debian Jessie / PVE 4 94 | 95 | -- Proxmox Support Team Fri, 27 Feb 2015 20:00:46 +0100 96 | 97 | vncterm (1.1-8) unstable; urgency=low 98 | 99 | * update applet signature (new code signing cert) 100 | 101 | -- Proxmox Support Team Fri, 25 Jul 2014 06:57:46 +0200 102 | 103 | vncterm (1.1-7) unstable; urgency=low 104 | 105 | * new option -notls (for novnc) 106 | 107 | -- Proxmox Support Team Mon, 23 Jun 2014 13:30:50 +0200 108 | 109 | vncterm (1.1-6) unstable; urgency=low 110 | 111 | * set Caller-Allowable-Codebase and remove Trusted-Library. This avoids 112 | security popups with latest Java 7u45. Also see: 113 | 114 | http://stackoverflow.com/questions/19393826/java-applet-manifest-allow-all-caller-allowable-codebase 115 | 116 | -- Proxmox Support Team Fri, 08 Nov 2013 11:27:08 +0100 117 | 118 | vncterm (1.1-5) unstable; urgency=low 119 | 120 | * use Comodo code sign cert for applet signature 121 | 122 | -- Proxmox Support Team Fri, 08 Nov 2013 08:35:10 +0100 123 | 124 | vncterm (1.1-4) unstable; urgency=low 125 | 126 | * Allow to add intermediate certificates to /etc/pve/local/pve-ssl.pem 127 | (users previously used apache option SSLCertificateChainFile for that). 128 | 129 | -- Proxmox Support Team Mon, 03 Jun 2013 08:26:59 +0200 130 | 131 | vncterm (1.1-3) unstable; urgency=low 132 | 133 | * re-enable javascript-events.patch (used for migrate/reload) 134 | 135 | -- Proxmox Support Team Fri, 10 May 2013 07:56:35 +0200 136 | 137 | vncterm (1.1-2) unstable; urgency=low 138 | 139 | * recompile VnCViewer.jar with openJDK 140 | 141 | * set trusted-lib to avoid security popup 142 | 143 | * disable unused javascript-events.patch 144 | 145 | -- Proxmox Support Team Sat, 20 Apr 2013 16:05:41 +0200 146 | 147 | vncterm (1.1-1) unstable; urgency=low 148 | 149 | * recompile for wheezy 150 | 151 | -- Proxmox Support Team Fri, 15 Mar 2013 08:26:04 +0100 152 | 153 | vncterm (1.0-3) unstable; urgency=low 154 | 155 | * fix focus traversal 156 | 157 | -- Proxmox Support Team Tue, 21 Aug 2012 09:14:04 +0200 158 | 159 | vncterm (1.0-2) unstable; urgency=low 160 | 161 | * do not create a new process group (with setsid), so 162 | we can kill the whole pve task if used inside fork_worker(). 163 | 164 | -- Proxmox Support Team Tue, 13 Sep 2011 10:49:32 +0200 165 | 166 | vncterm (1.0-1) unstable; urgency=low 167 | 168 | * use TigerVNC java sources 169 | 170 | * implement TLS encryption and vencrypt password auth 171 | 172 | * removed support for -passwdfile option 173 | 174 | -- root Mon, 24 Jan 2011 10:46:25 +0100 175 | 176 | vncterm (0.9-2) unstable; urgency=low 177 | 178 | * compiled for debian etch 179 | 180 | * use memmove instead of rfbDoCopyRect (avoid SIGSEGV when 181 | called with negative dy) 182 | 183 | -- Proxmox Support Team Wed, 22 Apr 2009 10:02:08 +0200 184 | 185 | vncterm (0.9-1) stable; urgency=low 186 | 187 | * initial import 188 | 189 | -- Proxmox Support Team Thu, 27 Dec 2007 05:57:17 +0100 190 | 191 | -------------------------------------------------------------------------------- /genfont.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (C) 2007 Proxmox Server Solutions GmbH 4 | 5 | Copyright: vzdump is under GNU GPL, the GNU General Public License. 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; version 2 dated June, 1991. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 19 | 02111-1307, USA. 20 | 21 | Author: Dietmar Maurer 22 | 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include /* read compressed console fonts */ 30 | 31 | /* map unicode to font */ 32 | static unsigned short vt_fontmap[65536]; 33 | 34 | /* font glyph storage */ 35 | static unsigned char *vt_font_data = NULL; 36 | static int vt_font_size = 0; 37 | static int vt_font_maxsize = 0; 38 | 39 | /* PSF stuff */ 40 | 41 | #define PSF_MAGIC1 0x36 42 | #define PSF_MAGIC2 0x04 43 | 44 | #define PSF_MODE256NOSFM 0 45 | #define PSF_MODE512NOSFM 1 46 | #define PSF_MODE256SFM 2 47 | #define PSF_MODE512SFM 3 48 | 49 | #define PSF_SEPARATOR 0xFFFF 50 | 51 | struct psf_header { 52 | unsigned char magic1, magic2; /* Magic number */ 53 | unsigned char mode; /* PSF font mode */ 54 | unsigned char charheight; /* Character size */ 55 | }; 56 | 57 | #define PSF_MAGIC_OK(x) ((x).magic1 == PSF_MAGIC1 && (x).magic2 == PSF_MAGIC2) 58 | #define PSF_MODE_VALID(x) ((x) <= PSF_MODE512SFM) 59 | #define PSF_MODE_HAS512(x) (((x) == 1) || ((x) == 3)) 60 | #define PSF_MODE_HASSFM(x) (((x) == 2) || ((x) == 3)) 61 | 62 | typedef unsigned short unicode; 63 | 64 | static int font_add_glyph(const char *data) { 65 | 66 | if (vt_font_size >= vt_font_maxsize) { 67 | vt_font_maxsize += 256; 68 | vt_font_data = realloc(vt_font_data, vt_font_maxsize * 16); 69 | } 70 | 71 | memcpy(vt_font_data + vt_font_size * 16, data, 16); 72 | 73 | vt_font_size += 1; 74 | 75 | return vt_font_size - 1; 76 | } 77 | 78 | static int load_psf_font(const char *filename, int is_default) { 79 | struct psf_header psfhdr; 80 | 81 | gzFile f = gzopen(filename, "rb"); 82 | if (f == NULL) { 83 | fprintf(stderr, "unable to read file %s\n", filename); 84 | exit(-1); 85 | } 86 | 87 | // read psf header 88 | if (gzread(f, &psfhdr, sizeof(struct psf_header)) != sizeof(struct psf_header)) { 89 | fprintf(stderr, "unable to read psf font header (%s)\n", filename); 90 | gzclose(f); 91 | return -1; 92 | } 93 | 94 | if (!PSF_MAGIC_OK(psfhdr) || !PSF_MODE_VALID(psfhdr.mode) || !PSF_MODE_HASSFM(psfhdr.mode) || 95 | (psfhdr.charheight != 16)) { 96 | fprintf(stderr, "no valid 8*16 psf font (%s)\n", filename); 97 | gzclose(f); 98 | return -1; 99 | } 100 | 101 | int charcount = ((PSF_MODE_HAS512(psfhdr.mode)) ? 512 : 256); 102 | 103 | int size = 16 * charcount; 104 | 105 | char *chardata = (char *)malloc(size); 106 | 107 | if (size != gzread(f, chardata, size)) { 108 | fprintf(stderr, "unable to read font character data (%s)\n", filename); 109 | gzclose(f); 110 | return -1; 111 | } 112 | 113 | unicode unichar; 114 | int glyph; 115 | 116 | for (glyph = 0; glyph < charcount; glyph++) { 117 | int fi = 0; 118 | while (gzread(f, &unichar, sizeof(unicode)) == sizeof(unicode) && (unichar != PSF_SEPARATOR) 119 | ) { 120 | if (!vt_fontmap[unichar]) { 121 | if (!fi) { 122 | fi = font_add_glyph(chardata + glyph * 16); 123 | } 124 | vt_fontmap[unichar] = fi; 125 | } 126 | } 127 | 128 | if (is_default && fi && glyph < 256) { 129 | vt_fontmap[0xf000 + glyph] = fi; 130 | } 131 | } 132 | 133 | free(chardata); 134 | gzclose(f); 135 | 136 | return 0; 137 | } 138 | 139 | void print_glyphs() { 140 | int i, j; 141 | 142 | printf("static int vt_font_size = %d;\n\n", vt_font_size); 143 | 144 | printf("static unsigned char vt_font_data[] = {\n"); 145 | for (i = 0; i < vt_font_size; i++) { 146 | printf("\t/* %d 0x%02x */\n", i, i); 147 | for (j = 0; j < 16; j++) { 148 | unsigned char d = vt_font_data[i * 16 + j]; 149 | printf("\t0x%02X, /* ", d); 150 | int k; 151 | for (k = 128; k > 0; k = k >> 1) { 152 | printf("%c", (d & k) ? '1' : '0'); 153 | } 154 | printf(" */\n"); 155 | } 156 | printf("\n"); 157 | } 158 | printf("};\n\n"); 159 | 160 | printf("static unsigned short vt_fontmap[65536] = {\n"); 161 | for (i = 0; i < 0x0ffff; i++) { 162 | printf("\t/* 0x%04X => */ %d,\n", i, vt_fontmap[i]); 163 | } 164 | printf("};\n\n"); 165 | } 166 | 167 | int main(int argc, char **argv) { 168 | char empty[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 169 | glob_t globbuf; 170 | 171 | font_add_glyph(empty); 172 | 173 | /* font load order is only important if glyphs are redefined */ 174 | load_psf_font("/usr/share/consolefonts/default8x16.psf.gz", 1); /* vga default */ 175 | load_psf_font("/usr/share/consolefonts/lat1u-16.psf.gz", 0); /* Latin-1 */ 176 | load_psf_font("/usr/share/consolefonts/lat2u-16.psf.gz", 0); /* Latin-2 */ 177 | load_psf_font("/usr/share/consolefonts/lat4u-16.psf.gz", 0); /* Baltic */ 178 | 179 | load_psf_font("/usr/share/consolefonts/iso07.f16.psf.gz", 0); /* Greek */ 180 | load_psf_font("/usr/share/consolefonts/Goha-16.psf.gz", 0); /* Ethiopic */ 181 | 182 | /* fixme: Arabic, Japanese letters ? */ 183 | 184 | if (0) { 185 | glob("/usr/share/consolefonts/*", GLOB_ERR, NULL, &globbuf); 186 | 187 | int i; 188 | for (i = 0; i < globbuf.gl_pathc; i++) { 189 | int pc = vt_font_size; 190 | load_psf_font(globbuf.gl_pathv[i], 0); 191 | if (vt_font_size > pc) { 192 | printf("TEST: %s %d\n", globbuf.gl_pathv[i], vt_font_size - pc); 193 | } 194 | } 195 | } else { 196 | 197 | print_glyphs(); 198 | } 199 | 200 | exit(0); 201 | } 202 | -------------------------------------------------------------------------------- /vncterm.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define IBUFSIZE 1024 4 | #define MAX_ESC_PARAMS 16 5 | 6 | typedef unsigned short unicode; 7 | 8 | typedef struct TextAttributes { 9 | unsigned int fgcol : 4; 10 | unsigned int bgcol : 4; 11 | unsigned int bold : 1; 12 | unsigned int uline : 1; 13 | unsigned int blink : 1; 14 | unsigned int invers : 1; 15 | unsigned int unvisible : 1; 16 | } TextAttributes; 17 | 18 | typedef struct TextCell { 19 | unicode ch; 20 | unicode combiningglyph; 21 | short width; 22 | TextAttributes attrib; 23 | } TextCell; 24 | 25 | typedef struct vncTerm { 26 | int maxx; 27 | int maxy; 28 | 29 | int width; 30 | int height; 31 | 32 | int total_height; 33 | int scroll_height; 34 | int y_base; 35 | int y_displ; 36 | int altbuf : 1; 37 | 38 | unsigned int utf8 : 1; // utf8 mode 39 | long utf_char; // used by utf8 parser 40 | int utf_count; // used by utf8 parser 41 | 42 | TextAttributes default_attrib; 43 | 44 | TextCell *cells; 45 | TextCell *altcells; 46 | 47 | rfbScreenInfoPtr screen; 48 | 49 | // cursor 50 | TextAttributes cur_attrib; 51 | TextAttributes cur_attrib_saved; 52 | unsigned int tty_state; // 0 - normal, 1 - ESC, 2 - CSI 53 | int cx; // cursor x position 54 | int cy; // cursor y position 55 | int cx_saved; // saved cursor x position 56 | int cy_saved; // saved cursor y position 57 | unsigned int esc_buf[MAX_ESC_PARAMS]; 58 | unsigned int esc_count; 59 | unsigned int esc_ques; 60 | unsigned int esc_has_par; 61 | unsigned int region_top; 62 | unsigned int region_bottom; 63 | 64 | unsigned int charset : 1; // G0 or G1 65 | unsigned int charset_saved : 1; // G0 or G1 66 | unsigned int g0enc : 2; 67 | unsigned int g0enc_saved : 2; 68 | unsigned int g1enc : 2; 69 | unsigned int g1enc_saved : 2; 70 | unsigned int cur_enc : 2; 71 | unsigned int cur_enc_saved : 2; 72 | 73 | // input buffer 74 | char ibuf[IBUFSIZE]; 75 | int ibuf_count; 76 | 77 | unicode *selection; 78 | int selection_len; 79 | 80 | unsigned int mark_active : 1; 81 | 82 | unsigned int report_mouse : 1; 83 | 84 | } vncTerm; 85 | 86 | /* Unicode translations copied from kernel source consolemap.c */ 87 | 88 | #define LAT1_MAP 0 89 | #define GRAF_MAP 1 90 | #define IBMPC_MAP 2 91 | #define USER_MAP 3 92 | 93 | static unsigned short translations[][256] = { 94 | /* 8-bit Latin-1 mapped to Unicode -- trivial mapping */ 95 | {0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 96 | 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 97 | 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 98 | 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 99 | 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 100 | 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 101 | 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 102 | 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 103 | 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 104 | 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 105 | 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 106 | 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 107 | 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 108 | 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 109 | 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 110 | 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 111 | 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 112 | 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 113 | 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 114 | 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 115 | 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 116 | 0x00fc, 0x00fd, 0x00fe, 0x00ff}, 117 | /* VT100 graphics mapped to Unicode */ 118 | {0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 119 | 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 120 | 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 121 | 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x2192, 0x2190, 0x2191, 0x2193, 0x002f, 122 | 0x2588, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 123 | 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 124 | 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 125 | 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x00a0, 126 | 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, 0x2591, 0x240b, 0x2518, 0x2510, 127 | 0x250c, 0x2514, 0x253c, 0x23ba, 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c, 128 | 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 129 | 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 130 | 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 131 | 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 132 | 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 133 | 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 134 | 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 135 | 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 136 | 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 137 | 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 138 | 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 139 | 0x00fc, 0x00fd, 0x00fe, 0x00ff}, 140 | /* IBM Codepage 437 mapped to Unicode */ 141 | {0x0000, 0x263a, 0x263b, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, 0x25d8, 0x25cb, 0x25d9, 0x2642, 142 | 0x2640, 0x266a, 0x266b, 0x263c, 0x25b6, 0x25c0, 0x2195, 0x203c, 0x00b6, 0x00a7, 0x25ac, 0x21a8, 143 | 0x2191, 0x2193, 0x2192, 0x2190, 0x221f, 0x2194, 0x25b2, 0x25bc, 0x0020, 0x0021, 0x0022, 0x0023, 144 | 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 145 | 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 146 | 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 147 | 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 148 | 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 149 | 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 150 | 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 151 | 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302, 0x00c7, 0x00fc, 0x00e9, 0x00e2, 152 | 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, 153 | 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00a2, 154 | 0x00a3, 0x00a5, 0x20a7, 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, 155 | 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, 0x2591, 0x2592, 0x2593, 0x2502, 156 | 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510, 157 | 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, 0x255a, 0x2554, 0x2569, 0x2566, 158 | 0x2560, 0x2550, 0x256c, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, 159 | 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, 0x03b1, 0x00df, 0x0393, 0x03c0, 160 | 0x03a3, 0x03c3, 0x00b5, 0x03c4, 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229, 161 | 0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248, 0x00b0, 0x2219, 0x00b7, 0x221a, 162 | 0x207f, 0x00b2, 0x25a0, 0x00a0}, 163 | /* User mapping -- default to codes for direct font mapping */ 164 | {0xf000, 0xf001, 0xf002, 0xf003, 0xf004, 0xf005, 0xf006, 0xf007, 0xf008, 0xf009, 0xf00a, 0xf00b, 165 | 0xf00c, 0xf00d, 0xf00e, 0xf00f, 0xf010, 0xf011, 0xf012, 0xf013, 0xf014, 0xf015, 0xf016, 0xf017, 166 | 0xf018, 0xf019, 0xf01a, 0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f, 0xf020, 0xf021, 0xf022, 0xf023, 167 | 0xf024, 0xf025, 0xf026, 0xf027, 0xf028, 0xf029, 0xf02a, 0xf02b, 0xf02c, 0xf02d, 0xf02e, 0xf02f, 168 | 0xf030, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037, 0xf038, 0xf039, 0xf03a, 0xf03b, 169 | 0xf03c, 0xf03d, 0xf03e, 0xf03f, 0xf040, 0xf041, 0xf042, 0xf043, 0xf044, 0xf045, 0xf046, 0xf047, 170 | 0xf048, 0xf049, 0xf04a, 0xf04b, 0xf04c, 0xf04d, 0xf04e, 0xf04f, 0xf050, 0xf051, 0xf052, 0xf053, 171 | 0xf054, 0xf055, 0xf056, 0xf057, 0xf058, 0xf059, 0xf05a, 0xf05b, 0xf05c, 0xf05d, 0xf05e, 0xf05f, 172 | 0xf060, 0xf061, 0xf062, 0xf063, 0xf064, 0xf065, 0xf066, 0xf067, 0xf068, 0xf069, 0xf06a, 0xf06b, 173 | 0xf06c, 0xf06d, 0xf06e, 0xf06f, 0xf070, 0xf071, 0xf072, 0xf073, 0xf074, 0xf075, 0xf076, 0xf077, 174 | 0xf078, 0xf079, 0xf07a, 0xf07b, 0xf07c, 0xf07d, 0xf07e, 0xf07f, 0xf080, 0xf081, 0xf082, 0xf083, 175 | 0xf084, 0xf085, 0xf086, 0xf087, 0xf088, 0xf089, 0xf08a, 0xf08b, 0xf08c, 0xf08d, 0xf08e, 0xf08f, 176 | 0xf090, 0xf091, 0xf092, 0xf093, 0xf094, 0xf095, 0xf096, 0xf097, 0xf098, 0xf099, 0xf09a, 0xf09b, 177 | 0xf09c, 0xf09d, 0xf09e, 0xf09f, 0xf0a0, 0xf0a1, 0xf0a2, 0xf0a3, 0xf0a4, 0xf0a5, 0xf0a6, 0xf0a7, 178 | 0xf0a8, 0xf0a9, 0xf0aa, 0xf0ab, 0xf0ac, 0xf0ad, 0xf0ae, 0xf0af, 0xf0b0, 0xf0b1, 0xf0b2, 0xf0b3, 179 | 0xf0b4, 0xf0b5, 0xf0b6, 0xf0b7, 0xf0b8, 0xf0b9, 0xf0ba, 0xf0bb, 0xf0bc, 0xf0bd, 0xf0be, 0xf0bf, 180 | 0xf0c0, 0xf0c1, 0xf0c2, 0xf0c3, 0xf0c4, 0xf0c5, 0xf0c6, 0xf0c7, 0xf0c8, 0xf0c9, 0xf0ca, 0xf0cb, 181 | 0xf0cc, 0xf0cd, 0xf0ce, 0xf0cf, 0xf0d0, 0xf0d1, 0xf0d2, 0xf0d3, 0xf0d4, 0xf0d5, 0xf0d6, 0xf0d7, 182 | 0xf0d8, 0xf0d9, 0xf0da, 0xf0db, 0xf0dc, 0xf0dd, 0xf0de, 0xf0df, 0xf0e0, 0xf0e1, 0xf0e2, 0xf0e3, 183 | 0xf0e4, 0xf0e5, 0xf0e6, 0xf0e7, 0xf0e8, 0xf0e9, 0xf0ea, 0xf0eb, 0xf0ec, 0xf0ed, 0xf0ee, 0xf0ef, 184 | 0xf0f0, 0xf0f1, 0xf0f2, 0xf0f3, 0xf0f4, 0xf0f5, 0xf0f6, 0xf0f7, 0xf0f8, 0xf0f9, 0xf0fa, 0xf0fb, 185 | 0xf0fc, 0xf0fd, 0xf0fe, 0xf0ff} 186 | }; 187 | -------------------------------------------------------------------------------- /vncterm.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (C) 2007-2011 Proxmox Server Solutions GmbH 4 | 5 | Copyright: vzdump is under GNU GPL, the GNU General Public License. 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; version 2 dated June, 1991. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 19 | 02111-1307, USA. 20 | 21 | Author: Dietmar Maurer 22 | 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include /* for openpty and forkpty */ 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #include "vncterm.h" 45 | 46 | #include 47 | #include 48 | 49 | /* define this for debugging */ 50 | // #define DEBUG 51 | 52 | char *auth_path = "/"; 53 | char *auth_perm = "Sys.Console"; 54 | 55 | uint16_t screen_width = 744; 56 | uint16_t screen_height = 400; 57 | 58 | int use_x509 = 1; 59 | 60 | extern int wcwidth(wchar_t wc); 61 | unsigned char *fontdata; 62 | 63 | #define FONTFILE "/usr/share/vncterm/font.data" 64 | #define GLYPHLINES 16 65 | 66 | static char *urlencode(char *buf, const char *value) { 67 | static const char *hexchar = "0123456789abcdef"; 68 | char *p = buf; 69 | int i; 70 | int l = strlen(value); 71 | for (i = 0; i < l; i++) { 72 | char c = value[i]; 73 | if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9')) { 74 | *p++ = c; 75 | } else if (c == 32) { 76 | *p++ = '+'; 77 | } else { 78 | *p++ = '%'; 79 | *p++ = hexchar[c >> 4]; 80 | *p++ = hexchar[c & 15]; 81 | } 82 | } 83 | *p = 0; 84 | 85 | return p; 86 | } 87 | 88 | static int pve_auth_verify(const char *clientip, const char *username, const char *passwd) { 89 | struct sockaddr_in server; 90 | 91 | int sfd = socket(AF_INET, SOCK_STREAM, 0); 92 | if (sfd == -1) { 93 | perror("pve_auth_verify: socket failed"); 94 | return -1; 95 | } 96 | 97 | struct hostent *he; 98 | if ((he = gethostbyname("localhost")) == NULL) { 99 | fprintf(stderr, "pve_auth_verify: error resolving hostname\n"); 100 | goto err; 101 | } 102 | 103 | memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length); 104 | server.sin_family = AF_INET; 105 | server.sin_port = htons(85); 106 | 107 | if (connect(sfd, (struct sockaddr *)&server, sizeof(server))) { 108 | perror("pve_auth_verify: error connecting to server"); 109 | goto err; 110 | } 111 | 112 | char buf[8192]; 113 | char form[8192]; 114 | 115 | char *p = form; 116 | p = urlencode(p, "username"); 117 | *p++ = '='; 118 | p = urlencode(p, username); 119 | 120 | *p++ = '&'; 121 | p = urlencode(p, "password"); 122 | *p++ = '='; 123 | p = urlencode(p, passwd); 124 | 125 | *p++ = '&'; 126 | p = urlencode(p, "path"); 127 | *p++ = '='; 128 | p = urlencode(p, auth_path); 129 | 130 | *p++ = '&'; 131 | p = urlencode(p, "privs"); 132 | *p++ = '='; 133 | p = urlencode(p, auth_perm); 134 | 135 | sprintf( 136 | buf, 137 | "POST /api2/json/access/ticket HTTP/1.1\n" 138 | "Host: localhost:85\n" 139 | "Connection: close\n" 140 | "PVEClientIP: %s\n" 141 | "Content-Type: application/x-www-form-urlencoded\n" 142 | "Content-Length: %zd\n\n%s\n", 143 | clientip, strlen(form), form 144 | ); 145 | ssize_t len = strlen(buf); 146 | ssize_t sb = send(sfd, buf, len, 0); 147 | if (sb < 0) { 148 | perror("pve_auth_verify: send failed"); 149 | goto err; 150 | } 151 | if (sb != len) { 152 | fprintf(stderr, "pve_auth_verify: partial send error\n"); 153 | goto err; 154 | } 155 | 156 | len = recv(sfd, buf, sizeof(buf) - 1, 0); 157 | if (len < 0) { 158 | perror("pve_auth_verify: recv failed"); 159 | goto err; 160 | } 161 | 162 | buf[len] = 0; 163 | 164 | // printf("DATA:%s\n", buf); 165 | 166 | shutdown(sfd, SHUT_RDWR); 167 | 168 | return strncmp(buf, "HTTP/1.1 200 OK", 15); 169 | 170 | err: 171 | shutdown(sfd, SHUT_RDWR); 172 | return -1; 173 | } 174 | 175 | #ifdef DEBUG 176 | static void vnc_debug_gnutls_log(int level, const char *str) { 177 | fprintf(stderr, "%d %s", level, str); 178 | } 179 | #endif 180 | 181 | #define DH_BITS 2048 182 | static gnutls_dh_params_t dh_params; 183 | 184 | typedef struct { 185 | gnutls_session_t session; 186 | } tls_client_t; 187 | 188 | static ssize_t vnc_tls_push(gnutls_transport_ptr_t transport, const void *data, size_t len) { 189 | rfbClientPtr cl = (rfbClientPtr)transport; 190 | int n; 191 | 192 | retry: 193 | n = send(cl->sock, data, len, 0); 194 | if (n < 0) { 195 | if (errno == EINTR) { 196 | goto retry; 197 | } 198 | return -1; 199 | } 200 | return n; 201 | } 202 | 203 | static ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport, void *data, size_t len) { 204 | rfbClientPtr cl = (rfbClientPtr)transport; 205 | int n; 206 | 207 | retry: 208 | n = recv(cl->sock, data, len, 0); 209 | if (n < 0) { 210 | if (errno == EINTR) { 211 | goto retry; 212 | } 213 | return -1; 214 | } 215 | return n; 216 | } 217 | 218 | ssize_t vnc_tls_read(rfbClientPtr cl, void *buf, size_t count) { 219 | tls_client_t *sd = (tls_client_t *)cl->clientData; 220 | 221 | int ret = gnutls_read(sd->session, buf, count); 222 | if (ret < 0) { 223 | if (ret == GNUTLS_E_AGAIN) { 224 | errno = EAGAIN; 225 | } else { 226 | errno = EIO; 227 | } 228 | ret = -1; 229 | } 230 | 231 | return ret; 232 | } 233 | ssize_t vnc_tls_write(rfbClientPtr cl, void *buf, size_t count) { 234 | tls_client_t *sd = (tls_client_t *)cl->clientData; 235 | 236 | int ret = gnutls_write(sd->session, buf, count); 237 | if (ret < 0) { 238 | if (ret == GNUTLS_E_AGAIN) { 239 | errno = EAGAIN; 240 | } else { 241 | errno = EIO; 242 | } 243 | ret = -1; 244 | } 245 | 246 | return ret; 247 | } 248 | 249 | static gnutls_anon_server_credentials tls_initialize_anon_cred(void) { 250 | gnutls_anon_server_credentials anon_cred; 251 | int ret; 252 | 253 | if ((ret = gnutls_anon_allocate_server_credentials(&anon_cred)) < 0) { 254 | rfbLog("can't allocate credentials: %s\n", gnutls_strerror(ret)); 255 | return NULL; 256 | } 257 | 258 | #if GNUTLS_VERSION_NUMBER >= 0x030506 259 | gnutls_anon_set_server_known_dh_params(anon_cred, GNUTLS_SEC_PARAM_MEDIUM); 260 | #else 261 | gnutls_anon_set_server_dh_params(anon_cred, dh_params); 262 | #endif 263 | 264 | return anon_cred; 265 | } 266 | 267 | static gnutls_certificate_credentials_t tls_initialize_x509_cred(void) { 268 | gnutls_certificate_credentials_t x509_cred; 269 | int ret; 270 | 271 | /* Paths to x509 certs/keys */ 272 | char *x509cacert = "/etc/pve/pve-root-ca.pem"; 273 | char *x509cert = "/etc/pve/local/pve-ssl.pem"; 274 | char *x509key = "/etc/pve/local/pve-ssl.key"; 275 | 276 | if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0) { 277 | rfbLog("can't allocate credentials: %s\n", gnutls_strerror(ret)); 278 | return NULL; 279 | } 280 | 281 | if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred, x509cacert, GNUTLS_X509_FMT_PEM)) < 282 | 0) { 283 | rfbLog("can't load CA certificate: %s\n", gnutls_strerror(ret)); 284 | gnutls_certificate_free_credentials(x509_cred); 285 | return NULL; 286 | } 287 | 288 | if ((ret = 289 | gnutls_certificate_set_x509_key_file(x509_cred, x509cert, x509key, GNUTLS_X509_FMT_PEM) 290 | ) < 0) { 291 | rfbLog("can't load certificate & key: %s\n", gnutls_strerror(ret)); 292 | gnutls_certificate_free_credentials(x509_cred); 293 | return NULL; 294 | } 295 | #if GNUTLS_VERSION_NUMBER >= 0x030506 296 | /* only available since GnuTLS 3.5.6, on previous versions see 297 | * gnutls_certificate_set_dh_params(). */ 298 | gnutls_certificate_set_known_dh_params(x509_cred, GNUTLS_SEC_PARAM_MEDIUM); 299 | #else 300 | gnutls_certificate_set_dh_params(x509_cred, dh_params); 301 | #endif 302 | 303 | return x509_cred; 304 | } 305 | 306 | /* rfb tls security handler */ 307 | 308 | #define rfbSecTypeVencrypt 19 309 | #define rfbVencryptTlsPlain 259 310 | #define rfbVencryptX509Plain 262 311 | 312 | void rfbEncodeU32(char *buf, uint32_t value) { 313 | buf[0] = (value >> 24) & 0xFF; 314 | buf[1] = (value >> 16) & 0xFF; 315 | buf[2] = (value >> 8) & 0xFF; 316 | buf[3] = value & 0xFF; 317 | } 318 | 319 | uint32_t rfbDecodeU32(char *data, size_t offset) { 320 | return ( 321 | (data[offset] << 24) | (data[offset + 1] << 16) | (data[offset + 2] << 8) | data[offset + 3] 322 | ); 323 | } 324 | 325 | static void vencrypt_subauth_plain(rfbClientPtr cl) { 326 | const char *err = NULL; 327 | char buf[4096]; 328 | int n; 329 | 330 | char clientip[INET6_ADDRSTRLEN]; 331 | clientip[0] = 0; 332 | struct sockaddr_in client; 333 | socklen_t addrlen = sizeof(client); 334 | if (getpeername(cl->sock, &client, &addrlen) == 0) { 335 | inet_ntop(client.sin_family, &client.sin_addr, clientip, sizeof(clientip)); 336 | } 337 | 338 | if ((n = rfbReadExact(cl, buf, 8)) <= 0) { 339 | err = n ? "read failed" : "client gone"; 340 | goto err; 341 | } 342 | 343 | uint32_t ulen = rfbDecodeU32(buf, 0); 344 | uint32_t pwlen = rfbDecodeU32(buf, 4); 345 | 346 | if (!ulen) { 347 | err = "No User name."; 348 | goto err; 349 | } 350 | if (ulen >= 255) { 351 | err = "User name too long."; 352 | goto err; 353 | } 354 | if (!pwlen) { 355 | err = "Password too short"; 356 | goto err; 357 | } 358 | if (pwlen >= 511) { 359 | err = "Password too long."; 360 | goto err; 361 | } 362 | 363 | if ((n = rfbReadExact(cl, buf, ulen)) <= 0) { 364 | err = n ? "read failed" : "client gone"; 365 | goto err; 366 | } 367 | buf[ulen] = 0; 368 | char *username = buf; 369 | char *passwd = buf + ulen + 1; 370 | if ((n = rfbReadExact(cl, passwd, pwlen)) <= 0) { 371 | err = n ? "read failed" : "client gone"; 372 | goto err; 373 | } 374 | passwd[pwlen] = 0; 375 | 376 | rfbLog("VencryptPlain: username: %s pw: %s\n", username, passwd); 377 | 378 | if (pve_auth_verify(clientip, username, passwd) == 0) { 379 | rfbEncodeU32(buf, 0); /* Accept auth completion */ 380 | rfbWriteExact(cl, buf, 4); 381 | cl->state = RFB_INITIALISATION; 382 | return; 383 | } 384 | 385 | err = "Authentication failed"; 386 | err: 387 | rfbLog("VencryptPlain: %s\n", err ? err : "no reason specified"); 388 | if (err) { 389 | rfbEncodeU32(buf, 1); /* Reject auth */ 390 | rfbWriteExact(cl, buf, 4); 391 | if (cl->protocolMinorVersion >= 8) { 392 | int elen = strlen(err); 393 | rfbEncodeU32(buf, elen); 394 | rfbWriteExact(cl, buf, 4); 395 | rfbWriteExact(cl, err, elen); 396 | } 397 | } 398 | rfbCloseClient(cl); 399 | return; 400 | } 401 | 402 | static void rfbVncAuthVencrypt(rfbClientPtr cl) { 403 | int ret; 404 | 405 | /* Send VeNCrypt version 0.2 */ 406 | char buf[256]; 407 | buf[0] = 0; 408 | buf[1] = 2; 409 | 410 | if (rfbWriteExact(cl, buf, 2) < 0) { 411 | rfbLogPerror("rfbVncAuthVencrypt: write"); 412 | rfbCloseClient(cl); 413 | return; 414 | } 415 | 416 | int n = rfbReadExact(cl, buf, 2); 417 | if (n <= 0) { 418 | if (n == 0) { 419 | rfbLog("rfbVncAuthVencrypt: client gone\n"); 420 | } else { 421 | rfbLogPerror("rfbVncAuthVencrypt: read"); 422 | } 423 | rfbCloseClient(cl); 424 | return; 425 | } 426 | 427 | if (buf[0] != 0 || buf[1] != 2) { 428 | rfbLog("Unsupported VeNCrypt protocol %d.%d\n", (int)buf[0], (int)buf[1]); 429 | buf[0] = 1; /* Reject version */ 430 | rfbWriteExact(cl, buf, 1); 431 | rfbCloseClient(cl); 432 | return; 433 | } 434 | 435 | /* Sending allowed auth */ 436 | int req_auth = use_x509 ? rfbVencryptX509Plain : rfbVencryptTlsPlain; 437 | 438 | buf[0] = 0; /* Accept version */ 439 | buf[1] = 1; /* number of sub auths */ 440 | rfbEncodeU32(buf + 2, req_auth); 441 | if (rfbWriteExact(cl, buf, 6) < 0) { 442 | rfbLogPerror("rfbVncAuthVencrypt: write"); 443 | rfbCloseClient(cl); 444 | return; 445 | } 446 | 447 | n = rfbReadExact(cl, buf, 4); 448 | if (n <= 0) { 449 | if (n == 0) { 450 | rfbLog("rfbVncAuthVencrypt: client gone\n"); 451 | } else { 452 | rfbLogPerror("rfbVncAuthVencrypt: read"); 453 | } 454 | rfbCloseClient(cl); 455 | return; 456 | } 457 | 458 | int auth = rfbDecodeU32(buf, 0); 459 | if (auth != req_auth) { 460 | buf[0] = 1; /* Reject auth*/ 461 | rfbWriteExact(cl, buf, 1); 462 | rfbCloseClient(cl); 463 | return; 464 | } 465 | 466 | buf[0] = 1; /* Accept auth */ 467 | if (rfbWriteExact(cl, buf, 1) < 0) { 468 | rfbLogPerror("rfbVncAuthVencrypt: write"); 469 | rfbCloseClient(cl); 470 | return; 471 | } 472 | 473 | tls_client_t *sd = calloc(1, sizeof(tls_client_t)); 474 | 475 | if (sd->session == NULL) { 476 | if (gnutls_init(&sd->session, GNUTLS_SERVER) < 0) { 477 | rfbLog("gnutls_init failed\n"); 478 | rfbCloseClient(cl); 479 | return; 480 | } 481 | 482 | if ((ret = gnutls_set_default_priority(sd->session)) < 0) { 483 | rfbLog("gnutls_set_default_priority failed: %s\n", gnutls_strerror(ret)); 484 | sd->session = NULL; 485 | rfbCloseClient(cl); 486 | return; 487 | } 488 | 489 | static const char *priority_str_x509 = "NORMAL"; 490 | static const char *priority_str_anon = "NORMAL:+ANON-ECDH:+ANON-DH"; 491 | if ((ret = gnutls_priority_set_direct( 492 | sd->session, use_x509 ? priority_str_x509 : priority_str_anon, NULL 493 | )) < 0) { 494 | rfbLog("gnutls_priority_set_direct failed: %s\n", gnutls_strerror(ret)); 495 | sd->session = NULL; 496 | rfbCloseClient(cl); 497 | return; 498 | } 499 | 500 | if (use_x509) { 501 | gnutls_certificate_server_credentials x509_cred; 502 | 503 | if (!(x509_cred = tls_initialize_x509_cred())) { 504 | sd->session = NULL; 505 | rfbCloseClient(cl); 506 | return; 507 | } 508 | 509 | if (gnutls_credentials_set(sd->session, GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) { 510 | sd->session = NULL; 511 | gnutls_certificate_free_credentials(x509_cred); 512 | rfbCloseClient(cl); 513 | return; 514 | } 515 | 516 | } else { 517 | gnutls_anon_server_credentials anon_cred; 518 | 519 | if (!(anon_cred = tls_initialize_anon_cred())) { 520 | sd->session = NULL; 521 | rfbCloseClient(cl); 522 | return; 523 | } 524 | 525 | if ((ret = gnutls_credentials_set(sd->session, GNUTLS_CRD_ANON, anon_cred)) < 0) { 526 | rfbLog("gnutls_credentials_set failed: %s\n", gnutls_strerror(ret)); 527 | gnutls_anon_free_server_credentials(anon_cred); 528 | sd->session = NULL; 529 | rfbCloseClient(cl); 530 | return; 531 | } 532 | } 533 | 534 | gnutls_transport_set_ptr(sd->session, (gnutls_transport_ptr_t)cl); 535 | gnutls_transport_set_push_function(sd->session, vnc_tls_push); 536 | gnutls_transport_set_pull_function(sd->session, vnc_tls_pull); 537 | } 538 | 539 | retry: 540 | if ((ret = gnutls_handshake(sd->session)) < 0) { 541 | if (!gnutls_error_is_fatal(ret)) { 542 | usleep(100000); 543 | goto retry; 544 | } 545 | rfbLog("rfbVncAuthVencrypt: handshake failed\n"); 546 | rfbCloseClient(cl); 547 | return; 548 | } 549 | 550 | /* set up TLS read/write hooks */ 551 | cl->clientData = sd; 552 | cl->sock_read_fn = &vnc_tls_read; 553 | cl->sock_write_fn = &vnc_tls_write; 554 | 555 | vencrypt_subauth_plain(cl); 556 | } 557 | 558 | static rfbSecurityHandler VncSecurityHandlerVencrypt = { 559 | rfbSecTypeVencrypt, rfbVncAuthVencrypt, NULL 560 | }; 561 | 562 | #define TERM "xterm" 563 | 564 | #define TERMIDCODE "[?1;2c" // vt100 ID 565 | 566 | #define CHECK_ARGC(argc, argv, i) \ 567 | if (i >= argc - 1) { \ 568 | fprintf(stderr, "ERROR: not enough arguments for: %s\n", argv[i]); \ 569 | print_usage(NULL); \ 570 | exit(1); \ 571 | } 572 | 573 | /* these colours are from linux kernel drivers/char/vt.c */ 574 | 575 | static int idle_timeout = 1; 576 | 577 | unsigned char color_table[] = {0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15}; 578 | 579 | /* the default colour table, for VGA+ colour systems */ 580 | int default_red[] = {0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 581 | 0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff}; 582 | int default_grn[] = {0x00, 0x00, 0xaa, 0x55, 0x00, 0x00, 0xaa, 0xaa, 583 | 0x55, 0x55, 0xff, 0xff, 0x55, 0x55, 0xff, 0xff}; 584 | int default_blu[] = {0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 585 | 0x55, 0x55, 0x55, 0x55, 0xff, 0xff, 0xff, 0xff}; 586 | 587 | static void print_usage(const char *msg) { 588 | if (msg) { 589 | fprintf(stderr, "ERROR: %s\n", msg); 590 | } 591 | fprintf(stderr, "USAGE: vncterm [vncopts] [-c command [args]]\n"); 592 | } 593 | 594 | /* Convert UCS2 to UTF8 sequence, trailing zero */ 595 | static int ucs2_to_utf8(unicode c, char *out) { 596 | if (c < 0x80) { 597 | out[0] = c; // 0******* 598 | out[1] = 0; 599 | return 1; 600 | } else if (c < 0x800) { 601 | out[0] = 0xc0 | (c >> 6); // 110***** 10****** 602 | out[1] = 0x80 | (c & 0x3f); 603 | out[2] = 0; 604 | return 2; 605 | } else { 606 | out[0] = 0xe0 | (c >> 12); // 1110**** 10****** 10****** 607 | out[1] = 0x80 | ((c >> 6) & 0x3f); 608 | out[2] = 0x80 | (c & 0x3f); 609 | out[3] = 0; 610 | return 3; 611 | } 612 | 613 | return 0; 614 | } 615 | 616 | static void 617 | rfb_draw_char(rfbScreenInfoPtr rfbScreen, int x, int y, unicode c, rfbPixel col, short width) { 618 | int i, j; 619 | unsigned char *data = fontdata + c * (GLYPHLINES * 2); 620 | unsigned char d = *data; 621 | int rowstride = rfbScreen->paddedWidthInBytes; 622 | char *colour = (char *)&col; 623 | 624 | for (j = 0; j < GLYPHLINES; j++) { 625 | for (i = 0; i < 8 * width; i++) { 626 | if ((i & 7) == 0) { 627 | d = *data; 628 | data++; 629 | } 630 | if (d & 0x80) { 631 | *(rfbScreen->frameBuffer + (y + j) * rowstride + (x + i)) = *colour; 632 | } 633 | d <<= 1; 634 | } 635 | } 636 | } 637 | 638 | static void draw_char_at( 639 | vncTerm *vt, 640 | int x, 641 | int y, 642 | unicode ch, 643 | TextAttributes attrib, 644 | short width, 645 | unicode combiningglyph 646 | ) { 647 | if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { 648 | return; 649 | } 650 | 651 | // non printable character 652 | if (width < 1) { 653 | return; 654 | } 655 | 656 | int rx = x * 8; 657 | int ry = y * 16; 658 | int rxe = x * 8 + 8 * width; 659 | int rye = y * 16 + 16; 660 | 661 | int fg, bg; 662 | 663 | if (attrib.invers) { 664 | bg = attrib.fgcol; 665 | fg = attrib.bgcol; 666 | } else { 667 | bg = attrib.bgcol; 668 | fg = attrib.fgcol; 669 | } 670 | 671 | rfbFillRect(vt->screen, rx, ry, rxe, rye, bg); 672 | 673 | if (attrib.bold) { 674 | fg += 8; 675 | } 676 | 677 | // unsuported attributes = (attrib.blink || attrib.unvisible) 678 | 679 | rfb_draw_char(vt->screen, rx, ry, ch, fg, width); 680 | 681 | if (combiningglyph) { 682 | rfb_draw_char(vt->screen, rx, ry, combiningglyph, fg, 1); 683 | } 684 | 685 | if (attrib.uline) { 686 | rfbDrawLine(vt->screen, rx, ry + 14, rxe, ry + 14, fg); 687 | } 688 | 689 | rfbMarkRectAsModified(vt->screen, rx, ry, rxe, rye); 690 | } 691 | 692 | static void vncterm_update_xy(vncTerm *vt, int x, int y) { 693 | if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { 694 | return; 695 | } 696 | 697 | int y1 = (vt->y_base + y) % vt->total_height; 698 | int y2 = y1 - vt->y_displ; 699 | if (y2 < 0) { 700 | y2 += vt->total_height; 701 | } 702 | if (y2 < vt->height) { 703 | TextCell *c = &vt->cells[y1 * vt->width + x]; 704 | draw_char_at(vt, x, y2, c->ch, c->attrib, c->width, c->combiningglyph); 705 | } 706 | } 707 | 708 | static void vncterm_clear_xy(vncTerm *vt, int x, int y) { 709 | if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { 710 | return; 711 | } 712 | 713 | int y1 = (vt->y_base + y) % vt->total_height; 714 | int y2 = y1 - vt->y_displ; 715 | if (y2 < 0) { 716 | y2 += vt->total_height; 717 | } 718 | if (y2 < vt->height) { 719 | TextCell *c = &vt->cells[y1 * vt->width + x]; 720 | c->ch = ' '; 721 | c->attrib = vt->default_attrib; 722 | c->attrib.fgcol = vt->cur_attrib.fgcol; 723 | c->attrib.bgcol = vt->cur_attrib.bgcol; 724 | c->width = 1; 725 | c->combiningglyph = 0; 726 | 727 | draw_char_at(vt, x, y, c->ch, c->attrib, c->width, c->combiningglyph); 728 | } 729 | } 730 | 731 | static void vncterm_show_cursor(vncTerm *vt, int show) { 732 | int x = vt->cx; 733 | if (x >= vt->width) { 734 | x = vt->width - 1; 735 | } 736 | 737 | int y1 = (vt->y_base + vt->cy) % vt->total_height; 738 | int y = y1 - vt->y_displ; 739 | if (y < 0) { 740 | y += vt->total_height; 741 | } 742 | 743 | if (y < vt->height) { 744 | 745 | TextCell *c = &vt->cells[y1 * vt->width + x]; 746 | 747 | if (show) { 748 | TextAttributes attrib = vt->default_attrib; 749 | attrib.invers = !(attrib.invers); /* invert fg and bg */ 750 | draw_char_at(vt, x, y, c->ch, attrib, c->width, c->combiningglyph); 751 | } else { 752 | draw_char_at(vt, x, y, c->ch, c->attrib, c->width, c->combiningglyph); 753 | } 754 | } 755 | } 756 | 757 | static void vncterm_refresh(vncTerm *vt) { 758 | int x, y, y1; 759 | 760 | rfbFillRect(vt->screen, 0, 0, vt->maxx, vt->maxy, vt->default_attrib.bgcol); 761 | 762 | y1 = vt->y_displ; 763 | for (y = 0; y < vt->height; y++) { 764 | TextCell *c = vt->cells + y1 * vt->width; 765 | for (x = 0; x < vt->width; x++) { 766 | draw_char_at(vt, x, y, c->ch, c->attrib, c->width, c->combiningglyph); 767 | c += c->width; 768 | } 769 | if (++y1 == vt->total_height) { 770 | y1 = 0; 771 | } 772 | } 773 | rfbMarkRectAsModified(vt->screen, 0, 0, vt->maxx, vt->maxy); 774 | 775 | vncterm_show_cursor(vt, 1); 776 | } 777 | 778 | static void vncterm_scroll_down(vncTerm *vt, int top, int bottom, int lines) { 779 | if ((top + lines) >= bottom) { 780 | lines = bottom - top - 1; 781 | } 782 | 783 | if (top < 0 || bottom > vt->height || top >= bottom || lines < 1) { 784 | return; 785 | } 786 | 787 | int h = lines * 16; 788 | int y0 = top * 16; 789 | int y1 = y0 + h; 790 | int y2 = bottom * 16; 791 | int rowstride = vt->screen->paddedWidthInBytes; 792 | int rows = (bottom - top - lines) * 16; 793 | 794 | char *in = vt->screen->frameBuffer + y0 * rowstride; 795 | char *out = vt->screen->frameBuffer + y1 * rowstride; 796 | memmove(out, in, rowstride * rows); 797 | 798 | memset(vt->screen->frameBuffer + y0 * rowstride, 0, h * rowstride); 799 | rfbMarkRectAsModified(vt->screen, 0, y0, vt->screen->width, y2); 800 | 801 | int i; 802 | for (i = bottom - top - lines - 1; i >= 0; i--) { 803 | int src = ((vt->y_base + top + i) % vt->total_height) * vt->width; 804 | int dst = ((vt->y_base + top + lines + i) % vt->total_height) * vt->width; 805 | 806 | memmove(vt->cells + dst, vt->cells + src, vt->width * sizeof(TextCell)); 807 | } 808 | 809 | for (i = 0; i < lines; i++) { 810 | int j; 811 | TextCell *c = vt->cells + ((vt->y_base + top + i) % vt->total_height) * vt->width; 812 | for (j = 0; j < vt->width; j++) { 813 | c->attrib = vt->default_attrib; 814 | c->ch = ' '; 815 | c->width = 1; 816 | c->combiningglyph = 0; 817 | c++; 818 | } 819 | } 820 | } 821 | 822 | static void vncterm_scroll_up(vncTerm *vt, int top, int bottom, int lines, int moveattr) { 823 | if ((top + lines) >= bottom) { 824 | lines = bottom - top - 1; 825 | } 826 | 827 | if (top < 0 || bottom > vt->height || top >= bottom || lines < 1) { 828 | return; 829 | } 830 | 831 | int h = lines * 16; 832 | int y0 = top * 16; 833 | int y1 = (top + lines) * 16; 834 | int y2 = bottom * 16; 835 | int rowstride = vt->screen->paddedWidthInBytes; 836 | int rows = (bottom - top - lines) * 16; 837 | 838 | char *in = vt->screen->frameBuffer + y1 * rowstride; 839 | char *out = vt->screen->frameBuffer + y0 * rowstride; 840 | memmove(out, in, rowstride * rows); 841 | 842 | memset(vt->screen->frameBuffer + (y2 - h) * rowstride, 0, h * rowstride); 843 | 844 | rfbMarkRectAsModified(vt->screen, 0, y0, vt->screen->width, y2); 845 | 846 | if (!moveattr) { 847 | return; 848 | } 849 | 850 | // move attributes 851 | 852 | int i; 853 | for (i = 0; i < (bottom - top - lines); i++) { 854 | int dst = ((vt->y_base + top + i) % vt->total_height) * vt->width; 855 | int src = ((vt->y_base + top + lines + i) % vt->total_height) * vt->width; 856 | 857 | memmove(vt->cells + dst, vt->cells + src, vt->width * sizeof(TextCell)); 858 | } 859 | 860 | for (i = 1; i <= lines; i++) { 861 | int j; 862 | TextCell *c = vt->cells + ((vt->y_base + bottom - i) % vt->total_height) * vt->width; 863 | for (j = 0; j < vt->width; j++) { 864 | c->attrib = vt->default_attrib; 865 | c->ch = ' '; 866 | c->width = 1; 867 | c->combiningglyph = 0; 868 | c++; 869 | } 870 | } 871 | } 872 | 873 | static void vncterm_virtual_scroll(vncTerm *vt, int lines) { 874 | if (vt->altbuf || lines == 0) { 875 | return; 876 | } 877 | 878 | if (lines < 0) { 879 | lines = -lines; 880 | int i = vt->scroll_height; 881 | if (i > vt->total_height - vt->height) { 882 | i = vt->total_height - vt->height; 883 | } 884 | int y1 = vt->y_base - i; 885 | if (y1 < 0) { 886 | y1 += vt->total_height; 887 | } 888 | for (i = 0; i < lines; i++) { 889 | if (vt->y_displ == y1) { 890 | break; 891 | } 892 | if (--vt->y_displ < 0) { 893 | vt->y_displ = vt->total_height - 1; 894 | } 895 | } 896 | } else { 897 | int i; 898 | for (i = 0; i < lines; i++) { 899 | if (vt->y_displ == vt->y_base) { 900 | break; 901 | } 902 | if (++vt->y_displ == vt->total_height) { 903 | vt->y_displ = 0; 904 | } 905 | } 906 | } 907 | 908 | vncterm_refresh(vt); 909 | } 910 | static void vncterm_respond_esc(vncTerm *vt, const char *esc) { 911 | int len = strlen(esc); 912 | int i; 913 | 914 | if (vt->ibuf_count < (IBUFSIZE - 1 - len)) { 915 | vt->ibuf[vt->ibuf_count++] = 27; 916 | for (i = 0; i < len; i++) { 917 | vt->ibuf[vt->ibuf_count++] = esc[i]; 918 | } 919 | } 920 | } 921 | 922 | static void vncterm_put_lf(vncTerm *vt) { 923 | if (vt->cy + 1 == vt->region_bottom) { 924 | 925 | if (vt->altbuf || vt->region_top != 0 || vt->region_bottom != vt->height) { 926 | vncterm_scroll_up(vt, vt->region_top, vt->region_bottom, 1, 1); 927 | return; 928 | } 929 | 930 | if (vt->y_displ == vt->y_base) { 931 | vncterm_scroll_up(vt, vt->region_top, vt->region_bottom, 1, 0); 932 | } 933 | 934 | if (vt->y_displ == vt->y_base) { 935 | if (++vt->y_displ == vt->total_height) { 936 | vt->y_displ = 0; 937 | } 938 | } 939 | 940 | if (++vt->y_base == vt->total_height) { 941 | vt->y_base = 0; 942 | } 943 | 944 | if (vt->scroll_height < vt->total_height) { 945 | vt->scroll_height++; 946 | } 947 | 948 | int y1 = (vt->y_base + vt->height - 1) % vt->total_height; 949 | TextCell *c = &vt->cells[y1 * vt->width]; 950 | int x; 951 | for (x = 0; x < vt->width; x++) { 952 | c->ch = ' '; 953 | c->width = 1; 954 | c->combiningglyph = 0; 955 | c->attrib = vt->default_attrib; 956 | c++; 957 | } 958 | 959 | // fprintf (stderr, "BASE: %d DISPLAY %d\n", vt->y_base, vt->y_displ); 960 | 961 | } else if (vt->cy < vt->height - 1) { 962 | vt->cy += 1; 963 | } 964 | } 965 | 966 | static void vncterm_csi_m(vncTerm *vt) { 967 | int i; 968 | 969 | for (i = 0; i < vt->esc_count; i++) { 970 | switch (vt->esc_buf[i]) { 971 | case 0: /* reset all console attributes to default */ 972 | vt->cur_attrib = vt->default_attrib; 973 | break; 974 | case 1: 975 | vt->cur_attrib.bold = 1; 976 | break; 977 | case 4: 978 | vt->cur_attrib.uline = 1; 979 | break; 980 | case 5: 981 | vt->cur_attrib.blink = 1; 982 | break; 983 | case 7: 984 | vt->cur_attrib.invers = 1; 985 | break; 986 | case 8: 987 | vt->cur_attrib.unvisible = 1; 988 | break; 989 | case 10: 990 | vt->cur_enc = LAT1_MAP; 991 | // fixme: dispaly controls = 0 ? 992 | // fixme: toggle meta = 0 ? 993 | break; 994 | case 11: 995 | vt->cur_enc = IBMPC_MAP; 996 | // fixme: dispaly controls = 1 ? 997 | // fixme: toggle meta = 0 ? 998 | break; 999 | case 12: 1000 | vt->cur_enc = IBMPC_MAP; 1001 | // fixme: dispaly controls = 1 ? 1002 | // fixme: toggle meta = 1 ? 1003 | break; 1004 | case 22: 1005 | vt->cur_attrib.bold = 0; 1006 | break; 1007 | case 24: 1008 | vt->cur_attrib.uline = 0; 1009 | break; 1010 | case 25: 1011 | vt->cur_attrib.blink = 0; 1012 | break; 1013 | case 27: 1014 | vt->cur_attrib.invers = 0; 1015 | break; 1016 | case 28: 1017 | vt->cur_attrib.unvisible = 0; 1018 | break; 1019 | case 30: 1020 | case 31: 1021 | case 32: 1022 | case 33: 1023 | case 34: 1024 | case 35: 1025 | case 36: 1026 | case 37: 1027 | /* set foreground color */ 1028 | vt->cur_attrib.fgcol = color_table[vt->esc_buf[i] - 30]; 1029 | break; 1030 | case 38: 1031 | /* reset color to default, enable underline */ 1032 | vt->cur_attrib.fgcol = vt->default_attrib.fgcol; 1033 | vt->cur_attrib.uline = 1; 1034 | break; 1035 | case 39: 1036 | /* reset color to default, disable underline */ 1037 | vt->cur_attrib.fgcol = vt->default_attrib.fgcol; 1038 | vt->cur_attrib.uline = 0; 1039 | break; 1040 | case 40: 1041 | case 41: 1042 | case 42: 1043 | case 43: 1044 | case 44: 1045 | case 45: 1046 | case 46: 1047 | case 47: 1048 | /* set background color */ 1049 | vt->cur_attrib.bgcol = color_table[vt->esc_buf[i] - 40]; 1050 | break; 1051 | case 49: 1052 | /* reset background color */ 1053 | vt->cur_attrib.bgcol = vt->default_attrib.bgcol; 1054 | break; 1055 | default: 1056 | fprintf(stderr, "unhandled ESC[%d m code\n", vt->esc_buf[i]); 1057 | // fixme: implement 1058 | } 1059 | } 1060 | } 1061 | 1062 | static void vncterm_save_cursor(vncTerm *vt) { 1063 | vt->cx_saved = vt->cx; 1064 | vt->cy_saved = vt->cy; 1065 | vt->cur_attrib_saved = vt->cur_attrib; 1066 | vt->charset_saved = vt->charset; 1067 | vt->g0enc_saved = vt->g0enc; 1068 | vt->g1enc_saved = vt->g1enc; 1069 | vt->cur_enc_saved = vt->cur_enc; 1070 | } 1071 | 1072 | static void vncterm_restore_cursor(vncTerm *vt) { 1073 | vt->cx = vt->cx_saved; 1074 | vt->cy = vt->cy_saved; 1075 | vt->cur_attrib = vt->cur_attrib_saved; 1076 | vt->charset = vt->charset_saved; 1077 | vt->g0enc = vt->g0enc_saved; 1078 | vt->g1enc = vt->g1enc_saved; 1079 | vt->cur_enc = vt->cur_enc_saved; 1080 | } 1081 | 1082 | static void vncterm_set_alternate_buffer(vncTerm *vt, int on_off) { 1083 | int x, y; 1084 | 1085 | vt->y_displ = vt->y_base; 1086 | 1087 | if (on_off) { 1088 | 1089 | if (vt->altbuf) { 1090 | return; 1091 | } 1092 | 1093 | vt->altbuf = 1; 1094 | 1095 | /* alternate buffer & cursor */ 1096 | 1097 | vncterm_save_cursor(vt); 1098 | /* save screen to altcels */ 1099 | for (y = 0; y < vt->height; y++) { 1100 | int y1 = (vt->y_base + y) % vt->total_height; 1101 | for (x = 0; x < vt->width; x++) { 1102 | vt->altcells[y * vt->width + x] = vt->cells[y1 * vt->width + x]; 1103 | } 1104 | } 1105 | 1106 | /* clear screen */ 1107 | for (y = 0; y <= vt->height; y++) { 1108 | for (x = 0; x < vt->width; x++) { 1109 | vncterm_clear_xy(vt, x, y); 1110 | } 1111 | } 1112 | 1113 | } else { 1114 | 1115 | if (vt->altbuf == 0) { 1116 | return; 1117 | } 1118 | 1119 | vt->altbuf = 0; 1120 | 1121 | /* restore saved data */ 1122 | for (y = 0; y < vt->height; y++) { 1123 | int y1 = (vt->y_base + y) % vt->total_height; 1124 | for (x = 0; x < vt->width; x++) { 1125 | vt->cells[y1 * vt->width + x] = vt->altcells[y * vt->width + x]; 1126 | } 1127 | } 1128 | 1129 | vncterm_restore_cursor(vt); 1130 | } 1131 | 1132 | vncterm_refresh(vt); 1133 | } 1134 | 1135 | static void vncterm_set_mode(vncTerm *vt, int on_off) { 1136 | int i; 1137 | 1138 | for (i = 0; i <= vt->esc_count; i++) { 1139 | if (vt->esc_ques) { /* DEC private modes set/reset */ 1140 | switch (vt->esc_buf[i]) { 1141 | case 10: /* X11 mouse reporting on/off */ 1142 | case 1000: 1143 | vt->report_mouse = on_off; 1144 | break; 1145 | case 1049: /* start/end special app mode (smcup/rmcup) */ 1146 | vncterm_set_alternate_buffer(vt, on_off); 1147 | break; 1148 | case 25: /* Cursor on/off */ 1149 | case 9: /* X10 mouse reporting on/off */ 1150 | case 6: /* Origin relative/absolute */ 1151 | case 1: /* Cursor keys in appl mode*/ 1152 | case 5: /* Inverted screen on/off */ 1153 | case 7: /* Autowrap on/off */ 1154 | case 8: /* Autorepeat on/off */ 1155 | break; 1156 | } 1157 | } else { /* ANSI modes set/reset */ 1158 | /* fixme: implement me */ 1159 | } 1160 | } 1161 | } 1162 | 1163 | static void vncterm_gotoxy(vncTerm *vt, int x, int y) { 1164 | /* verify all boundaries */ 1165 | 1166 | if (x < 0) { 1167 | x = 0; 1168 | } else if (x >= vt->width) { 1169 | x = vt->width - 1; 1170 | } 1171 | 1172 | vt->cx = x; 1173 | 1174 | if (y < 0) { 1175 | y = 0; 1176 | } else if (y >= vt->height) { 1177 | y = vt->height - 1; 1178 | } 1179 | 1180 | vt->cy = y; 1181 | } 1182 | 1183 | enum { 1184 | ESnormal, 1185 | ESesc, 1186 | ESsquare, 1187 | ESgetpars, 1188 | ESgotpars, 1189 | ESfunckey, 1190 | EShash, 1191 | ESsetG0, 1192 | ESsetG1, 1193 | ESpercent, 1194 | ESignore, 1195 | ESnonstd, 1196 | ESpalette, 1197 | ESidquery, 1198 | ESosc1, 1199 | ESosc2 1200 | }; 1201 | 1202 | static void vncterm_putchar(vncTerm *vt, unicode ch) { 1203 | int x, y, i, c; 1204 | 1205 | #ifdef DEBUG 1206 | if (!vt->tty_state) { 1207 | fprintf( 1208 | stderr, "CHAR:%2d: %4x '%c' (cur_enc %d) %d %d\n", vt->tty_state, ch, ch, vt->cur_enc, 1209 | vt->cx, vt->cy 1210 | ); 1211 | } 1212 | #endif 1213 | 1214 | switch (vt->tty_state) { 1215 | case ESesc: 1216 | vt->tty_state = ESnormal; 1217 | switch (ch) { 1218 | case '[': 1219 | vt->tty_state = ESsquare; 1220 | break; 1221 | case ']': 1222 | vt->tty_state = ESnonstd; 1223 | break; 1224 | case '%': 1225 | vt->tty_state = ESpercent; 1226 | break; 1227 | case '7': 1228 | vncterm_save_cursor(vt); 1229 | break; 1230 | case '8': 1231 | vncterm_restore_cursor(vt); 1232 | break; 1233 | case '(': 1234 | vt->tty_state = ESsetG0; // SET G0 1235 | break; 1236 | case ')': 1237 | vt->tty_state = ESsetG1; // SET G1 1238 | break; 1239 | case 'M': 1240 | /* cursor up (ri) */ 1241 | if (vt->cy == vt->region_top) { 1242 | vncterm_scroll_down(vt, vt->region_top, vt->region_bottom, 1); 1243 | } else if (vt->cy > 0) { 1244 | vt->cy--; 1245 | } 1246 | break; 1247 | case '>': 1248 | /* numeric keypad - ignored */ 1249 | break; 1250 | case '=': 1251 | /* appl. keypad - ignored */ 1252 | break; 1253 | default: 1254 | #ifdef DEBUG 1255 | fprintf(stderr, "got unhandled ESC%c %d\n", ch, ch); 1256 | #endif 1257 | break; 1258 | } 1259 | break; 1260 | case ESnonstd: /* Operating System Controls */ 1261 | vt->tty_state = ESnormal; 1262 | 1263 | switch (ch) { 1264 | case 'P': /* palette escape sequence */ 1265 | for (i = 0; i < MAX_ESC_PARAMS; i++) { 1266 | vt->esc_buf[i] = 0; 1267 | } 1268 | 1269 | vt->esc_count = 0; 1270 | vt->tty_state = ESpalette; 1271 | break; 1272 | case 'R': /* reset palette */ 1273 | // fixme: reset_palette(vc); 1274 | break; 1275 | case '0': 1276 | case '1': 1277 | case '2': 1278 | case '4': 1279 | vt->tty_state = ESosc1; 1280 | break; 1281 | default: 1282 | #ifdef DEBUG 1283 | fprintf(stderr, "unhandled OSC %c\n", ch); 1284 | #endif 1285 | vt->tty_state = ESnormal; 1286 | break; 1287 | } 1288 | break; 1289 | case ESosc1: 1290 | vt->tty_state = ESnormal; 1291 | if (ch == ';') { 1292 | vt->tty_state = ESosc2; 1293 | } else { 1294 | #ifdef DEBUG 1295 | fprintf(stderr, "got illegal OSC sequence\n"); 1296 | #endif 1297 | } 1298 | break; 1299 | case ESosc2: 1300 | if (ch == 0x9c || ch == 7) { 1301 | vt->tty_state = ESnormal; 1302 | } 1303 | break; 1304 | case ESpalette: 1305 | if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f')) { 1306 | vt->esc_buf[vt->esc_count++] = (ch > '9' ? (ch & 0xDF) - 'A' + 10 : ch - '0'); 1307 | if (vt->esc_count == 7) { 1308 | // fixme: this does not work - please test 1309 | rfbColourMap *cmap = &vt->screen->colourMap; 1310 | 1311 | int i = color_table[vt->esc_buf[0]] * 3, j = 1; 1312 | cmap->data.bytes[i] = 16 * vt->esc_buf[j++]; 1313 | cmap->data.bytes[i++] += vt->esc_buf[j++]; 1314 | cmap->data.bytes[i] = 16 * vt->esc_buf[j++]; 1315 | cmap->data.bytes[i++] += vt->esc_buf[j++]; 1316 | cmap->data.bytes[i] = 16 * vt->esc_buf[j++]; 1317 | cmap->data.bytes[i] += vt->esc_buf[j]; 1318 | 1319 | // set_palette(vc); ? 1320 | 1321 | vt->tty_state = ESnormal; 1322 | } 1323 | } else { 1324 | vt->tty_state = ESnormal; 1325 | } 1326 | break; 1327 | case ESsquare: 1328 | for (i = 0; i < MAX_ESC_PARAMS; i++) { 1329 | vt->esc_buf[i] = 0; 1330 | } 1331 | 1332 | vt->esc_count = 0; 1333 | vt->esc_has_par = 0; 1334 | vt->tty_state = ESgetpars; 1335 | 1336 | if (ch == '>') { 1337 | vt->tty_state = ESidquery; 1338 | break; 1339 | } 1340 | 1341 | if ((vt->esc_ques = (ch == '?'))) { 1342 | break; 1343 | } 1344 | case ESgetpars: 1345 | if (ch >= '0' && ch <= '9') { 1346 | vt->esc_has_par = 1; 1347 | if (vt->esc_count < MAX_ESC_PARAMS) { 1348 | vt->esc_buf[vt->esc_count] = vt->esc_buf[vt->esc_count] * 10 + ch - '0'; 1349 | } 1350 | break; 1351 | } else if (ch == ';') { 1352 | vt->esc_has_par = 1; 1353 | vt->esc_count++; 1354 | break; 1355 | } else { 1356 | if (vt->esc_has_par) { 1357 | vt->esc_count++; 1358 | } 1359 | vt->tty_state = ESgotpars; 1360 | } 1361 | case ESgotpars: 1362 | 1363 | vt->tty_state = ESnormal; 1364 | 1365 | #ifdef DEBUG 1366 | char *qes = vt->esc_ques ? "?" : ""; 1367 | if (vt->esc_count == 0) { 1368 | fprintf(stderr, "ESC[%s%c\n", qes, ch); 1369 | } else if (vt->esc_count == 1) { 1370 | fprintf(stderr, "ESC[%s%d%c\n", qes, vt->esc_buf[0], ch); 1371 | } else { 1372 | int i; 1373 | fprintf(stderr, "ESC[%s%d", qes, vt->esc_buf[0]); 1374 | for (i = 1; i < vt->esc_count; i++) { 1375 | fprintf(stderr, ";%d", vt->esc_buf[i]); 1376 | } 1377 | fprintf(stderr, "%c\n", ch); 1378 | } 1379 | #endif 1380 | 1381 | switch (ch) { 1382 | case 'h': 1383 | vncterm_set_mode(vt, 1); 1384 | break; 1385 | case 'l': 1386 | vncterm_set_mode(vt, 0); 1387 | break; 1388 | case 'm': 1389 | if (!vt->esc_count) { 1390 | vt->esc_count++; // default parameter 0 1391 | } 1392 | vncterm_csi_m(vt); 1393 | break; 1394 | case 'n': 1395 | /* report cursor position */ 1396 | /* TODO: send ESC[row;colR */ 1397 | break; 1398 | case 'A': 1399 | /* move cursor up */ 1400 | if (vt->esc_buf[0] == 0) { 1401 | vt->esc_buf[0] = 1; 1402 | } 1403 | vncterm_gotoxy(vt, vt->cx, vt->cy - vt->esc_buf[0]); 1404 | break; 1405 | case 'B': 1406 | case 'e': 1407 | /* move cursor down */ 1408 | if (vt->esc_buf[0] == 0) { 1409 | vt->esc_buf[0] = 1; 1410 | } 1411 | vncterm_gotoxy(vt, vt->cx, vt->cy + vt->esc_buf[0]); 1412 | break; 1413 | case 'C': 1414 | case 'a': 1415 | /* move cursor right */ 1416 | if (vt->esc_buf[0] == 0) { 1417 | vt->esc_buf[0] = 1; 1418 | } 1419 | vncterm_gotoxy(vt, vt->cx + vt->esc_buf[0], vt->cy); 1420 | break; 1421 | case 'D': 1422 | /* move cursor left */ 1423 | if (vt->esc_buf[0] == 0) { 1424 | vt->esc_buf[0] = 1; 1425 | } 1426 | vncterm_gotoxy(vt, vt->cx - vt->esc_buf[0], vt->cy); 1427 | break; 1428 | case 'G': 1429 | case '`': 1430 | /* move cursor to column */ 1431 | vncterm_gotoxy(vt, vt->esc_buf[0] - 1, vt->cy); 1432 | break; 1433 | case 'd': 1434 | /* move cursor to row */ 1435 | vncterm_gotoxy(vt, vt->cx, vt->esc_buf[0] - 1); 1436 | break; 1437 | case 'f': 1438 | case 'H': 1439 | /* move cursor to row, column */ 1440 | vncterm_gotoxy(vt, vt->esc_buf[1] - 1, vt->esc_buf[0] - 1); 1441 | break; 1442 | case 'J': 1443 | switch (vt->esc_buf[0]) { 1444 | case 0: 1445 | /* clear to end of screen */ 1446 | for (y = vt->cy; y < vt->height; y++) { 1447 | for (x = 0; x < vt->width; x++) { 1448 | if (y == vt->cy && x < vt->cx) { 1449 | continue; 1450 | } 1451 | vncterm_clear_xy(vt, x, y); 1452 | } 1453 | } 1454 | break; 1455 | case 1: 1456 | /* clear from beginning of screen */ 1457 | for (y = 0; y <= vt->cy; y++) { 1458 | for (x = 0; x < vt->width; x++) { 1459 | if (y == vt->cy && x > vt->cx) { 1460 | break; 1461 | } 1462 | vncterm_clear_xy(vt, x, y); 1463 | } 1464 | } 1465 | break; 1466 | case 2: 1467 | /* clear entire screen */ 1468 | for (y = 0; y <= vt->height; y++) { 1469 | for (x = 0; x < vt->width; x++) { 1470 | vncterm_clear_xy(vt, x, y); 1471 | } 1472 | } 1473 | break; 1474 | } 1475 | break; 1476 | case 'K': 1477 | switch (vt->esc_buf[0]) { 1478 | case 0: 1479 | /* clear to eol */ 1480 | for (x = vt->cx; x < vt->width; x++) { 1481 | vncterm_clear_xy(vt, x, vt->cy); 1482 | } 1483 | break; 1484 | case 1: 1485 | /* clear from beginning of line */ 1486 | for (x = 0; x <= vt->cx; x++) { 1487 | vncterm_clear_xy(vt, x, vt->cy); 1488 | } 1489 | break; 1490 | case 2: 1491 | /* clear entire line */ 1492 | for (x = 0; x < vt->width; x++) { 1493 | vncterm_clear_xy(vt, x, vt->cy); 1494 | } 1495 | break; 1496 | } 1497 | break; 1498 | case 'L': 1499 | /* insert line */ 1500 | c = vt->esc_buf[0]; 1501 | 1502 | if (c > vt->height - vt->cy) { 1503 | c = vt->height - vt->cy; 1504 | } else if (!c) { 1505 | c = 1; 1506 | } 1507 | 1508 | vncterm_scroll_down(vt, vt->cy, vt->region_bottom, c); 1509 | break; 1510 | case 'M': 1511 | /* delete line */ 1512 | c = vt->esc_buf[0]; 1513 | 1514 | if (c > vt->height - vt->cy) { 1515 | c = vt->height - vt->cy; 1516 | } else if (!c) { 1517 | c = 1; 1518 | } 1519 | 1520 | vncterm_scroll_up(vt, vt->cy, vt->region_bottom, c, 1); 1521 | break; 1522 | case 'T': 1523 | /* scroll down */ 1524 | c = vt->esc_buf[0]; 1525 | if (!c) { 1526 | c = 1; 1527 | } 1528 | vncterm_scroll_down(vt, vt->region_top, vt->region_bottom, c); 1529 | break; 1530 | case 'S': 1531 | /* scroll up */ 1532 | c = vt->esc_buf[0]; 1533 | if (!c) { 1534 | c = 1; 1535 | } 1536 | vncterm_scroll_up(vt, vt->region_top, vt->region_bottom, c, 1); 1537 | break; 1538 | case 'P': 1539 | /* delete c character */ 1540 | c = vt->esc_buf[0]; 1541 | 1542 | if (c > vt->width - vt->cx) { 1543 | c = vt->width - vt->cx; 1544 | } else if (!c) { 1545 | c = 1; 1546 | } 1547 | 1548 | for (x = vt->cx; x < vt->width - c; x++) { 1549 | int y1 = (vt->y_base + vt->cy) % vt->total_height; 1550 | TextCell *dst = &vt->cells[y1 * vt->width + x]; 1551 | TextCell *src = dst + c; 1552 | *dst = *src; 1553 | vncterm_update_xy(vt, x + c, vt->cy); 1554 | src->ch = ' '; 1555 | src->width = 1; 1556 | src->combiningglyph = 0; 1557 | src->attrib = vt->default_attrib; 1558 | vncterm_update_xy(vt, x, vt->cy); 1559 | } 1560 | break; 1561 | case 's': 1562 | /* save cursor position */ 1563 | vncterm_save_cursor(vt); 1564 | break; 1565 | case 'u': 1566 | /* restore cursor position */ 1567 | vncterm_restore_cursor(vt); 1568 | break; 1569 | case 'X': 1570 | /* erase c characters */ 1571 | c = vt->esc_buf[0]; 1572 | if (!c) { 1573 | c = 1; 1574 | } 1575 | 1576 | if (c > (vt->width - vt->cx)) { 1577 | c = vt->width - vt->cx; 1578 | } 1579 | 1580 | for (i = 0; i < c; i++) { 1581 | vncterm_clear_xy(vt, vt->cx + i, vt->cy); 1582 | } 1583 | break; 1584 | case '@': 1585 | /* insert c character */ 1586 | c = vt->esc_buf[0]; 1587 | if (c > (vt->width - vt->cx)) { 1588 | c = vt->width - vt->cx; 1589 | } 1590 | if (!c) { 1591 | c = 1; 1592 | } 1593 | 1594 | for (x = vt->width - c; x >= vt->cx; x--) { 1595 | int y1 = (vt->y_base + vt->cy) % vt->total_height; 1596 | TextCell *src = &vt->cells[y1 * vt->width + x]; 1597 | TextCell *dst = src + c; 1598 | *dst = *src; 1599 | vncterm_update_xy(vt, x + c, vt->cy); 1600 | src->ch = ' '; 1601 | src->width = 1; 1602 | src->combiningglyph = 0; 1603 | src->attrib = vt->cur_attrib; 1604 | vncterm_update_xy(vt, x, vt->cy); 1605 | } 1606 | 1607 | break; 1608 | case 'r': 1609 | /* set region */ 1610 | if (!vt->esc_buf[0]) { 1611 | vt->esc_buf[0]++; 1612 | } 1613 | if (!vt->esc_buf[1]) { 1614 | vt->esc_buf[1] = vt->height; 1615 | } 1616 | /* Minimum allowed region is 2 lines */ 1617 | if (vt->esc_buf[0] < vt->esc_buf[1] && vt->esc_buf[1] <= vt->height) { 1618 | vt->region_top = vt->esc_buf[0] - 1; 1619 | vt->region_bottom = vt->esc_buf[1]; 1620 | vt->cx = 0; 1621 | vt->cy = vt->region_top; 1622 | #ifdef DEBUG 1623 | fprintf(stderr, "set region %d %d\n", vt->region_top, vt->region_bottom); 1624 | #endif 1625 | } 1626 | 1627 | break; 1628 | default: 1629 | #ifdef DEBUG 1630 | if (vt->esc_count == 0) { 1631 | fprintf(stderr, "unhandled escape ESC[%s%c\n", qes, ch); 1632 | } else if (vt->esc_count == 1) { 1633 | fprintf(stderr, "unhandled escape ESC[%s%d%c\n", qes, vt->esc_buf[0], ch); 1634 | } else { 1635 | int i; 1636 | fprintf(stderr, "unhandled escape ESC[%s%d", qes, vt->esc_buf[0]); 1637 | for (i = 1; i < vt->esc_count; i++) { 1638 | fprintf(stderr, ";%d", vt->esc_buf[i]); 1639 | } 1640 | fprintf(stderr, "%c\n", ch); 1641 | } 1642 | #endif 1643 | break; 1644 | } 1645 | vt->esc_ques = 0; 1646 | break; 1647 | case ESsetG0: // Set G0 1648 | vt->tty_state = ESnormal; 1649 | 1650 | if (ch == '0') { 1651 | vt->g0enc = GRAF_MAP; 1652 | } else if (ch == 'B') { 1653 | vt->g0enc = LAT1_MAP; 1654 | } else if (ch == 'U') { 1655 | vt->g0enc = IBMPC_MAP; 1656 | } else if (ch == 'K') { 1657 | vt->g0enc = USER_MAP; 1658 | } 1659 | 1660 | if (vt->charset == 0) { 1661 | vt->cur_enc = vt->g0enc; 1662 | } 1663 | 1664 | break; 1665 | case ESsetG1: // Set G1 1666 | vt->tty_state = ESnormal; 1667 | 1668 | if (ch == '0') { 1669 | vt->g1enc = GRAF_MAP; 1670 | } else if (ch == 'B') { 1671 | vt->g1enc = LAT1_MAP; 1672 | } else if (ch == 'U') { 1673 | vt->g1enc = IBMPC_MAP; 1674 | } else if (ch == 'K') { 1675 | vt->g1enc = USER_MAP; 1676 | } 1677 | 1678 | if (vt->charset == 1) { 1679 | vt->cur_enc = vt->g1enc; 1680 | } 1681 | 1682 | break; 1683 | case ESidquery: // vt100 query id 1684 | vt->tty_state = ESnormal; 1685 | 1686 | if (ch == 'c') { 1687 | #ifdef DEBUG 1688 | fprintf(stderr, "ESC[>c Query term ID\n"); 1689 | #endif 1690 | vncterm_respond_esc(vt, TERMIDCODE); 1691 | } 1692 | break; 1693 | case ESpercent: 1694 | vt->tty_state = ESnormal; 1695 | switch (ch) { 1696 | case '@': /* defined in ISO 2022 */ 1697 | vt->utf8 = 0; 1698 | break; 1699 | case 'G': /* prelim official escape code */ 1700 | case '8': /* retained for compatibility */ 1701 | vt->utf8 = 1; 1702 | break; 1703 | } 1704 | break; 1705 | default: // ESnormal 1706 | vt->tty_state = ESnormal; 1707 | 1708 | switch (ch) { 1709 | case 0: 1710 | break; 1711 | case 7: /* alert aka. bell */ 1712 | rfbSendBell(vt->screen); 1713 | break; 1714 | case 8: /* backspace */ 1715 | if (vt->cx > 0) { 1716 | vt->cx--; 1717 | } 1718 | break; 1719 | case 9: /* tabspace */ 1720 | if (vt->cx + (8 - (vt->cx % 8)) > vt->width) { 1721 | vt->cx = 0; 1722 | vncterm_put_lf(vt); 1723 | } else { 1724 | vt->cx = vt->cx + (8 - (vt->cx % 8)); 1725 | } 1726 | break; 1727 | case 10: /* LF,*/ 1728 | case 11: /* VT */ 1729 | case 12: /* FF */ 1730 | vncterm_put_lf(vt); 1731 | break; 1732 | case 13: /* carriage return */ 1733 | vt->cx = 0; 1734 | break; 1735 | case 14: 1736 | /* SI (shift in), select character set 1 */ 1737 | vt->charset = 1; 1738 | vt->cur_enc = vt->g1enc; 1739 | /* fixme: display controls = 1 */ 1740 | break; 1741 | case 15: 1742 | /* SO (shift out), select character set 0 */ 1743 | vt->charset = 0; 1744 | vt->cur_enc = vt->g0enc; 1745 | /* fixme: display controls = 0 */ 1746 | break; 1747 | case 27: /* esc */ 1748 | vt->tty_state = ESesc; 1749 | break; 1750 | case 127: /* delete */ 1751 | /* ignore */ 1752 | break; 1753 | case 128 + 27: /* csi */ 1754 | vt->tty_state = ESsquare; 1755 | break; 1756 | default: 1757 | if (vt->cx >= vt->width) { 1758 | /* line wrap */ 1759 | vt->cx = 0; 1760 | vncterm_put_lf(vt); 1761 | } 1762 | 1763 | int y1 = (vt->y_base + vt->cy) % vt->total_height; 1764 | int width = wcwidth(ch); 1765 | if (width > 0) { 1766 | // normal/wide character 1767 | TextCell *c = &vt->cells[y1 * vt->width + vt->cx]; 1768 | c->attrib = vt->cur_attrib; 1769 | c->ch = ch; 1770 | c->width = width; 1771 | c->combiningglyph = 0; 1772 | vncterm_update_xy(vt, vt->cx, vt->cy); 1773 | vt->cx += width; 1774 | } else if (width == 0) { 1775 | // combiningglyph 1776 | TextCell *c = &vt->cells[y1 * vt->width + vt->cx - 1]; 1777 | c->attrib = vt->cur_attrib; 1778 | c->combiningglyph = ch; 1779 | vncterm_update_xy(vt, vt->cx - 1, vt->cy); 1780 | } else { 1781 | // non printable character, so we do not save them 1782 | } 1783 | break; 1784 | } 1785 | break; 1786 | } 1787 | } 1788 | 1789 | static int vncterm_puts(vncTerm *vt, const char *buf, int len) { 1790 | unicode tc; 1791 | 1792 | vncterm_show_cursor(vt, 0); 1793 | 1794 | while (len) { 1795 | unsigned char c = *buf; 1796 | len--; 1797 | buf++; 1798 | 1799 | if (vt->tty_state != ESnormal) { 1800 | // never translate escape sequence 1801 | tc = c; 1802 | } else if (vt->utf8 && !vt->cur_enc) { 1803 | 1804 | if (c & 0x80) { // utf8 multi-byte sequence 1805 | 1806 | if (vt->utf_count > 0 && (c & 0xc0) == 0x80) { 1807 | // inside UTF8 sequence 1808 | vt->utf_char = (vt->utf_char << 6) | (c & 0x3f); 1809 | vt->utf_count--; 1810 | if (vt->utf_count == 0) { 1811 | if (vt->utf_char <= USHRT_MAX) { 1812 | tc = vt->utf_char; 1813 | } else { 1814 | tc = 0; 1815 | } 1816 | } else { 1817 | continue; 1818 | } 1819 | } else { 1820 | // first char of a UTF8 sequence 1821 | if ((c & 0xe0) == 0xc0) { 1822 | vt->utf_count = 1; 1823 | vt->utf_char = (c & 0x1f); 1824 | } else if ((c & 0xf0) == 0xe0) { 1825 | vt->utf_count = 2; 1826 | vt->utf_char = (c & 0x0f); 1827 | } else if ((c & 0xf8) == 0xf0) { 1828 | vt->utf_count = 3; 1829 | vt->utf_char = (c & 0x07); 1830 | } else if ((c & 0xfc) == 0xf8) { 1831 | vt->utf_count = 4; 1832 | vt->utf_char = (c & 0x03); 1833 | } else if ((c & 0xfe) == 0xfc) { 1834 | vt->utf_count = 5; 1835 | vt->utf_char = (c & 0x01); 1836 | } else { 1837 | vt->utf_count = 0; 1838 | } 1839 | 1840 | continue; 1841 | } 1842 | } else { 1843 | // utf8 single byte 1844 | tc = c; 1845 | vt->utf_count = 0; 1846 | } 1847 | 1848 | } else { 1849 | // never translate controls 1850 | if (c >= 32 && c != 127 && c != (128 + 27)) { 1851 | tc = translations[vt->cur_enc][c & 0x0ff]; 1852 | } else { 1853 | tc = c; 1854 | } 1855 | } 1856 | 1857 | vncterm_putchar(vt, tc); 1858 | } 1859 | 1860 | vncterm_show_cursor(vt, 1); 1861 | return len; 1862 | } 1863 | 1864 | void vncterm_kbd_event(rfbBool down, rfbKeySym keySym, rfbClientPtr cl) { 1865 | vncTerm *vt = (vncTerm *)cl->screen->screenData; 1866 | static int control = 0; 1867 | static int shift = 0; 1868 | char *esc = NULL; 1869 | 1870 | // fprintf (stderr, "KEYEVENT:%d: %08x\n", down == 0, keySym);fflush (stderr); 1871 | if (down) { 1872 | // fprintf (stderr, "KEYPRESS: %d\n", keySym);fflush (stderr); 1873 | 1874 | if (keySym == XK_Shift_L || keySym == XK_Shift_R) { 1875 | shift = 1; 1876 | } 1877 | if (keySym == XK_Control_L || keySym == XK_Control_R) { 1878 | control = 1; 1879 | } else if (vt->ibuf_count < (IBUFSIZE - 32)) { 1880 | 1881 | if (control) { 1882 | if (keySym >= 'a' && keySym <= 'z') { 1883 | keySym -= 'a' - 1; 1884 | } else if (keySym >= 'A' && keySym <= 'Z') { 1885 | keySym -= 'A' - 1; 1886 | } else { 1887 | keySym = 0xffff; 1888 | } 1889 | } else { 1890 | switch (keySym) { 1891 | case XK_Escape: 1892 | keySym = 27; 1893 | break; 1894 | case XK_Return: 1895 | keySym = '\r'; 1896 | break; 1897 | case XK_BackSpace: 1898 | keySym = 8; 1899 | break; 1900 | case XK_Tab: 1901 | keySym = '\t'; 1902 | break; 1903 | case XK_Delete: /* kdch1 */ 1904 | case XK_KP_Delete: 1905 | esc = "[3~"; 1906 | break; 1907 | case XK_Home: /* khome */ 1908 | case XK_KP_Home: 1909 | esc = "OH"; 1910 | break; 1911 | case XK_End: 1912 | case XK_KP_End: /* kend */ 1913 | esc = "OF"; 1914 | break; 1915 | case XK_Insert: /* kich1 */ 1916 | case XK_KP_Insert: 1917 | esc = "[2~"; 1918 | break; 1919 | case XK_Up: 1920 | case XK_KP_Up: /* kcuu1 */ 1921 | esc = "OA"; 1922 | break; 1923 | case XK_Down: /* kcud1 */ 1924 | case XK_KP_Down: 1925 | esc = "OB"; 1926 | break; 1927 | case XK_Right: 1928 | case XK_KP_Right: /* kcuf1 */ 1929 | esc = "OC"; 1930 | break; 1931 | case XK_Left: 1932 | case XK_KP_Left: /* kcub1 */ 1933 | esc = "OD"; 1934 | break; 1935 | case XK_Page_Up: 1936 | if (shift) { 1937 | vncterm_virtual_scroll(vt, -vt->height / 2); 1938 | return; 1939 | } 1940 | esc = "[5~"; 1941 | break; 1942 | case XK_Page_Down: 1943 | if (shift) { 1944 | vncterm_virtual_scroll(vt, vt->height / 2); 1945 | return; 1946 | } 1947 | esc = "[6~"; 1948 | break; 1949 | case XK_F1: 1950 | esc = "OP"; 1951 | break; 1952 | case XK_F2: 1953 | esc = "OQ"; 1954 | break; 1955 | case XK_F3: 1956 | esc = "OR"; 1957 | break; 1958 | case XK_F4: 1959 | esc = "OS"; 1960 | break; 1961 | case XK_F5: 1962 | esc = "[15~"; 1963 | break; 1964 | case XK_F6: 1965 | esc = "[17~"; 1966 | break; 1967 | case XK_F7: 1968 | esc = "[18~"; 1969 | break; 1970 | case XK_F8: 1971 | esc = "[19~"; 1972 | break; 1973 | case XK_F9: 1974 | esc = "[20~"; 1975 | break; 1976 | case XK_F10: 1977 | esc = "[21~"; 1978 | break; 1979 | case XK_F11: 1980 | esc = "[23~"; 1981 | break; 1982 | case XK_F12: 1983 | esc = "[24~"; 1984 | break; 1985 | default: 1986 | break; 1987 | } 1988 | } 1989 | 1990 | #ifdef DEBUG 1991 | fprintf(stderr, "KEYPRESS OUT:%s: %d\n", esc, keySym); 1992 | fflush(stderr); 1993 | #endif 1994 | 1995 | if (vt->y_displ != vt->y_base) { 1996 | vt->y_displ = vt->y_base; 1997 | vncterm_refresh(vt); 1998 | } 1999 | 2000 | if (esc) { 2001 | vncterm_respond_esc(vt, esc); 2002 | } else if (keySym < 0x100) { 2003 | if (vt->utf8) { 2004 | int len = ucs2_to_utf8(keySym & 0x0fff, &vt->ibuf[vt->ibuf_count]); 2005 | vt->ibuf_count += len; 2006 | } else { 2007 | vt->ibuf[vt->ibuf_count++] = (char)keySym; 2008 | } 2009 | } 2010 | } 2011 | } else { 2012 | if (keySym == XK_Shift_L || keySym == XK_Shift_R) { 2013 | shift = 0; 2014 | } else if (keySym == XK_Control_L || keySym == XK_Control_R) { 2015 | control = 0; 2016 | } 2017 | } 2018 | } 2019 | 2020 | void vncterm_set_xcut_text(char *str, int len, struct _rfbClientRec *cl) { 2021 | vncTerm *vt = (vncTerm *)cl->screen->screenData; 2022 | 2023 | // seems str is Latin-1 encoded 2024 | if (vt->selection) { 2025 | free(vt->selection); 2026 | } 2027 | vt->selection = (unicode *)malloc(len * sizeof(unicode)); 2028 | int i; 2029 | for (i = 0; i < len; i++) { 2030 | vt->selection[i] = str[i] & 0xff; 2031 | } 2032 | vt->selection_len = len; 2033 | } 2034 | 2035 | static void mouse_report(vncTerm *vt, int butt, int mrx, int mry) { 2036 | char buf[8]; 2037 | 2038 | sprintf(buf, "[M%c%c%c", (char)(' ' + butt), (char)('!' + mrx), (char)('!' + mry)); 2039 | 2040 | vncterm_respond_esc(vt, buf); 2041 | } 2042 | 2043 | void vncterm_toggle_marked_cell(vncTerm *vt, int pos) { 2044 | int x = (pos % vt->width) * 8; 2045 | int y = (pos / vt->width) * 16; 2046 | 2047 | int i, j; 2048 | rfbScreenInfoPtr s = vt->screen; 2049 | 2050 | char *b = s->frameBuffer + y * s->width + x; 2051 | 2052 | for (j = 0; j < 16; j++) { 2053 | for (i = 0; i < 8; i++) { 2054 | b[j * s->width + i] ^= 0x0f; 2055 | rfbMarkRectAsModified(s, x, y, x + 8, y + 16); 2056 | } 2057 | } 2058 | } 2059 | 2060 | void vncterm_pointer_event(int buttonMask, int x, int y, rfbClientPtr cl) { 2061 | vncTerm *vt = (vncTerm *)cl->screen->screenData; 2062 | static int button2_released = 1; 2063 | static int last_mask = 0; 2064 | static int sel_start_pos = 0; 2065 | static int sel_end_pos = 0; 2066 | int i; 2067 | 2068 | int cx = x / 8; 2069 | int cy = y / 16; 2070 | 2071 | if (cx < 0) { 2072 | cx = 0; 2073 | } 2074 | if (cx >= vt->width) { 2075 | cx = vt->width - 1; 2076 | } 2077 | if (cy < 0) { 2078 | cy = 0; 2079 | } 2080 | if (cy >= vt->height) { 2081 | cy = vt->height - 1; 2082 | } 2083 | 2084 | if (vt->report_mouse && buttonMask != last_mask) { 2085 | last_mask = buttonMask; 2086 | if (buttonMask & 1) { 2087 | mouse_report(vt, 0, cx, cy); 2088 | } 2089 | if (buttonMask & 2) { 2090 | mouse_report(vt, 1, cx, cy); 2091 | } 2092 | if (buttonMask & 4) { 2093 | mouse_report(vt, 2, cx, cy); 2094 | } 2095 | if (!buttonMask) { 2096 | mouse_report(vt, 3, cx, cy); 2097 | } 2098 | } 2099 | 2100 | if (buttonMask & 2) { 2101 | if (button2_released && vt->selection) { 2102 | int i; 2103 | for (i = 0; i < vt->selection_len; i++) { 2104 | if (vt->ibuf_count < IBUFSIZE - 6) { // uft8 is max 6 characters wide 2105 | if (vt->utf8) { 2106 | vt->ibuf_count += ucs2_to_utf8(vt->selection[i], &vt->ibuf[vt->ibuf_count]); 2107 | } else { 2108 | vt->ibuf[vt->ibuf_count++] = vt->selection[i]; 2109 | } 2110 | } 2111 | } 2112 | if (vt->y_displ != vt->y_base) { 2113 | vt->y_displ = vt->y_base; 2114 | vncterm_refresh(vt); 2115 | } 2116 | } 2117 | button2_released = 0; 2118 | } else { 2119 | button2_released = 1; 2120 | } 2121 | 2122 | if (buttonMask & 1) { 2123 | int pos = cy * vt->width + cx; 2124 | 2125 | // code borrowed from libvncserver (VNConsole.c) 2126 | 2127 | if (!vt->mark_active) { 2128 | 2129 | vt->mark_active = 1; 2130 | sel_start_pos = sel_end_pos = pos; 2131 | vncterm_toggle_marked_cell(vt, pos); 2132 | 2133 | } else { 2134 | 2135 | if (pos != sel_end_pos) { 2136 | 2137 | if (pos > sel_end_pos) { 2138 | cx = sel_end_pos; 2139 | cy = pos; 2140 | } else { 2141 | cx = pos; 2142 | cy = sel_end_pos; 2143 | } 2144 | 2145 | if (cx < sel_start_pos) { 2146 | if (cy < sel_start_pos) { 2147 | cy--; 2148 | } 2149 | } else { 2150 | cx++; 2151 | } 2152 | 2153 | while (cx <= cy) { 2154 | vncterm_toggle_marked_cell(vt, cx); 2155 | cx++; 2156 | } 2157 | 2158 | sel_end_pos = pos; 2159 | } 2160 | } 2161 | 2162 | } else if (vt->mark_active) { 2163 | vt->mark_active = 0; 2164 | 2165 | if (sel_start_pos > sel_end_pos) { 2166 | int tmp = sel_start_pos - 1; 2167 | sel_start_pos = sel_end_pos; 2168 | sel_end_pos = tmp; 2169 | } 2170 | 2171 | int len = sel_end_pos - sel_start_pos + 1; 2172 | 2173 | if (vt->selection) { 2174 | free(vt->selection); 2175 | } 2176 | vt->selection = (unicode *)malloc(len * sizeof(unicode)); 2177 | vt->selection_len = len; 2178 | char *sel_latin1 = (char *)malloc(len + 1); 2179 | 2180 | for (i = 0; i < len; i++) { 2181 | int pos = sel_start_pos + i; 2182 | int x = pos % vt->width; 2183 | int y1 = ((pos / vt->width) + vt->y_displ) % vt->total_height; 2184 | TextCell *c = &vt->cells[y1 * vt->width + x]; 2185 | vt->selection[i] = c->ch; 2186 | sel_latin1[i] = (char)c->ch; 2187 | c++; 2188 | } 2189 | sel_latin1[len] = 0; 2190 | rfbGotXCutText(vt->screen, sel_latin1, len); 2191 | free(sel_latin1); 2192 | 2193 | while (sel_start_pos <= sel_end_pos) { 2194 | vncterm_toggle_marked_cell(vt, sel_start_pos++); 2195 | } 2196 | } 2197 | 2198 | rfbDefaultPtrAddEvent(buttonMask, x, y, cl); 2199 | } 2200 | 2201 | static int client_count = 0; 2202 | static int client_connected = 0; 2203 | static int last_client = 1; 2204 | static time_t last_time = 0; 2205 | 2206 | void client_gone(rfbClientPtr client) { 2207 | client_count--; 2208 | 2209 | last_time = time(NULL); 2210 | 2211 | if (client_count <= 0) { 2212 | last_client = 1; 2213 | } 2214 | } 2215 | 2216 | /* libvncserver callback for when a new client connects */ 2217 | enum rfbNewClientAction new_client(rfbClientPtr client) { 2218 | client->clientGoneHook = client_gone; 2219 | client_count++; 2220 | 2221 | last_time = time(NULL); 2222 | 2223 | last_client = 0; 2224 | client_connected = 1; 2225 | 2226 | return RFB_CLIENT_ACCEPT; 2227 | } 2228 | 2229 | static char *vncticket = NULL; 2230 | 2231 | static void MakeRichCursor(rfbScreenInfoPtr rfbScreen) { 2232 | int w = 16, h = 16; 2233 | rfbCursorPtr c = rfbScreen->cursor; 2234 | char bitmap[] = 2235 | " " 2236 | " x " 2237 | " xx " 2238 | " xxx " 2239 | " xxxx " 2240 | " xxxxx " 2241 | " xxxxxx " 2242 | " xxxxxxx " 2243 | " xxxxxxxx " 2244 | " xxxxxxxxx " 2245 | " xxxxxxxxxx " 2246 | " xxxx " 2247 | " xxx " 2248 | " xx " 2249 | " x " 2250 | " "; 2251 | char edge[] = 2252 | " " 2253 | " x " 2254 | " xx " 2255 | " x x " 2256 | " x x " 2257 | " x x " 2258 | " x x " 2259 | " x x " 2260 | " x x " 2261 | " x x " 2262 | " x xxxxxx " 2263 | " x x " 2264 | " x x " 2265 | " xx " 2266 | " x " 2267 | " "; 2268 | 2269 | c = rfbScreen->cursor = rfbMakeXCursor(w, h, bitmap, bitmap); 2270 | c->richSource = (unsigned char *)calloc(w * h, 1); 2271 | c->cleanupRichSource = TRUE; 2272 | 2273 | for (int j = 0; j < h; j++) { 2274 | for (int i = 0; i < w; i++) { 2275 | unsigned int pos = j * w + i; 2276 | if (edge[pos] == 'x') { 2277 | c->richSource[pos] = 15; // white 2278 | } else { 2279 | c->richSource[pos] = 0; // black 2280 | } 2281 | } 2282 | } 2283 | } 2284 | 2285 | vncTerm *create_vncterm(int argc, char **argv, int maxx, int maxy) { 2286 | int i; 2287 | 2288 | rfbScreenInfoPtr screen = rfbGetScreen(&argc, argv, maxx, maxy, 8, 1, 1); 2289 | screen->frameBuffer = (char *)calloc(maxx * maxy, 1); 2290 | MakeRichCursor(screen); 2291 | 2292 | char **passwds = calloc(sizeof(char **), 2); 2293 | 2294 | vncTerm *vt = (vncTerm *)calloc(sizeof(vncTerm), 1); 2295 | 2296 | rfbColourMap *cmap = &screen->colourMap; 2297 | cmap->data.bytes = malloc(16 * 3); 2298 | for (i = 0; i < 16; i++) { 2299 | cmap->data.bytes[i * 3 + 0] = default_red[color_table[i]]; 2300 | cmap->data.bytes[i * 3 + 1] = default_grn[color_table[i]]; 2301 | cmap->data.bytes[i * 3 + 2] = default_blu[color_table[i]]; 2302 | } 2303 | cmap->count = 16; 2304 | cmap->is16 = FALSE; 2305 | screen->serverFormat.trueColour = FALSE; 2306 | 2307 | screen->kbdAddEvent = vncterm_kbd_event; 2308 | 2309 | screen->setXCutText = vncterm_set_xcut_text; 2310 | 2311 | screen->ptrAddEvent = vncterm_pointer_event; 2312 | 2313 | screen->desktopName = "VNC Command Terminal"; 2314 | 2315 | screen->newClientHook = new_client; 2316 | 2317 | vt->maxx = screen->width; 2318 | vt->maxy = screen->height; 2319 | 2320 | vt->width = vt->maxx / 8; 2321 | vt->height = vt->maxy / 16; 2322 | 2323 | vt->total_height = vt->height * 20; 2324 | vt->scroll_height = 0; 2325 | vt->y_base = 0; 2326 | vt->y_displ = 0; 2327 | 2328 | vt->region_top = 0; 2329 | vt->region_bottom = vt->height; 2330 | 2331 | vt->g0enc = LAT1_MAP; 2332 | vt->g1enc = GRAF_MAP; 2333 | vt->cur_enc = vt->g0enc; 2334 | vt->charset = 0; 2335 | 2336 | /* default text attributes */ 2337 | vt->default_attrib.bold = 0; 2338 | vt->default_attrib.uline = 0; 2339 | vt->default_attrib.blink = 0; 2340 | vt->default_attrib.invers = 0; 2341 | vt->default_attrib.unvisible = 0; 2342 | vt->default_attrib.fgcol = 7; 2343 | vt->default_attrib.bgcol = 0; 2344 | 2345 | vt->cur_attrib = vt->default_attrib; 2346 | 2347 | vt->cells = (TextCell *)calloc(sizeof(TextCell), vt->width * vt->total_height); 2348 | 2349 | for (i = 0; i < vt->width * vt->total_height; i++) { 2350 | vt->cells[i].ch = ' '; 2351 | vt->cells[i].attrib = vt->default_attrib; 2352 | } 2353 | 2354 | vt->altcells = (TextCell *)calloc(sizeof(TextCell), vt->width * vt->height); 2355 | 2356 | vt->screen = screen; 2357 | 2358 | screen->screenData = (void *)vt; 2359 | 2360 | // screen->autoPort = 1; 2361 | 2362 | if (vncticket) { 2363 | passwds[0] = vncticket; 2364 | passwds[1] = NULL; 2365 | 2366 | screen->authPasswdData = (void *)passwds; 2367 | screen->passwordCheck = rfbCheckPasswordByList; 2368 | } else { 2369 | rfbRegisterSecurityHandler(&VncSecurityHandlerVencrypt); 2370 | } 2371 | 2372 | rfbInitServer(screen); 2373 | 2374 | return vt; 2375 | } 2376 | 2377 | int main(int argc, char **argv) { 2378 | int i; 2379 | char **cmdargv = NULL; 2380 | char *command = "/bin/bash"; // execute normal shell as default 2381 | int fontfd; 2382 | struct stat sb; 2383 | int pid; 2384 | int master; 2385 | char ptyname[1024]; 2386 | fd_set fs, fs1; 2387 | struct timeval tv, tv1; 2388 | time_t elapsed, cur_time; 2389 | struct winsize dimensions; 2390 | unsigned long width = 0; 2391 | unsigned long height = 0; 2392 | 2393 | if (gnutls_global_init() < 0) { 2394 | fprintf(stderr, "gnutls_global_init failed\n"); 2395 | exit(-1); 2396 | } 2397 | 2398 | if (gnutls_dh_params_init(&dh_params) < 0) { 2399 | fprintf(stderr, "gnutls_dh_params_init failed\n"); 2400 | exit(-1); 2401 | } 2402 | 2403 | if (gnutls_dh_params_generate2(dh_params, DH_BITS) < 0) { 2404 | fprintf(stderr, "gnutls_dh_params_init failed\n"); 2405 | exit(-1); 2406 | } 2407 | 2408 | for (i = 1; i < argc; i++) { 2409 | if (!strcmp(argv[i], "-c")) { 2410 | command = argv[i + 1]; 2411 | cmdargv = &argv[i + 1]; 2412 | argc = i; 2413 | argv[i] = NULL; 2414 | break; 2415 | } 2416 | } 2417 | 2418 | for (i = 1; i < argc; i++) { 2419 | if (!strcmp(argv[i], "-timeout")) { 2420 | CHECK_ARGC(argc, argv, i); 2421 | idle_timeout = atoi(argv[i + 1]); 2422 | rfbPurgeArguments(&argc, &i, 2, argv); 2423 | i--; 2424 | } else if (!strcmp(argv[i], "-authpath")) { 2425 | CHECK_ARGC(argc, argv, i); 2426 | auth_path = argv[i + 1]; 2427 | rfbPurgeArguments(&argc, &i, 2, argv); 2428 | i--; 2429 | } else if (!strcmp(argv[i], "-perm")) { 2430 | CHECK_ARGC(argc, argv, i); 2431 | auth_perm = argv[i + 1]; 2432 | rfbPurgeArguments(&argc, &i, 2, argv); 2433 | i--; 2434 | } else if (!strcmp(argv[i], "-width")) { 2435 | CHECK_ARGC(argc, argv, i); 2436 | errno = 0; 2437 | width = strtoul(argv[i + 1], NULL, 10); 2438 | if (errno == 0 && width >= 16 && width < 0xFFFF) { 2439 | screen_width = width; 2440 | } 2441 | rfbPurgeArguments(&argc, &i, 2, argv); 2442 | i--; 2443 | } else if (!strcmp(argv[i], "-height")) { 2444 | CHECK_ARGC(argc, argv, i); 2445 | errno = 0; 2446 | height = strtoul(argv[i + 1], NULL, 10); 2447 | if (errno == 0 && height >= 32 && height < 0xFFFF) { 2448 | screen_height = height; 2449 | } 2450 | rfbPurgeArguments(&argc, &i, 2, argv); 2451 | i--; 2452 | } else if (!strcmp(argv[i], "-notls")) { 2453 | rfbPurgeArguments(&argc, &i, 1, argv); 2454 | i--; 2455 | if ((vncticket = getenv("PVE_VNC_TICKET")) == NULL) { 2456 | fprintf(stderr, "missing env PVE_VNC_TICKET (-notls)\n"); 2457 | exit(-1); 2458 | } 2459 | } 2460 | } 2461 | 2462 | unsetenv("PVE_VNC_TICKET"); // do not expose this to child 2463 | 2464 | #ifdef DEBUG 2465 | rfbLogEnable(1); 2466 | gnutls_global_set_log_level(10); 2467 | gnutls_global_set_log_function(vnc_debug_gnutls_log); 2468 | #else 2469 | rfbLogEnable(0); 2470 | #endif 2471 | 2472 | // mmap font file 2473 | fontfd = open(FONTFILE, O_RDONLY); 2474 | if (fontfd == -1) { 2475 | perror("Error opening Fontfile 'FONTFILE'"); 2476 | exit(-1); 2477 | } 2478 | if (fstat(fontfd, &sb) == -1) { 2479 | perror("Stat on 'FONTFILE' failed"); 2480 | exit(-1); 2481 | } 2482 | fontdata = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fontfd, 0); 2483 | if (fontdata == MAP_FAILED) { 2484 | perror("Could not mmap 'FONTFILE'"); 2485 | exit(-1); 2486 | } 2487 | 2488 | close(fontfd); 2489 | vncTerm *vt = create_vncterm(argc, argv, screen_width, screen_height); 2490 | 2491 | setlocale(LC_ALL, ""); // set from environment 2492 | 2493 | char *ctype = setlocale(LC_CTYPE, NULL); // query LC_CTYPE 2494 | 2495 | // fixme: ist there a standard way to detect utf8 mode ? 2496 | if (strcasestr(ctype, ".utf-8") || strcasestr(ctype, ".utf8")) { 2497 | vt->utf8 = 1; 2498 | } 2499 | 2500 | dimensions.ws_col = vt->width; 2501 | dimensions.ws_row = vt->height; 2502 | 2503 | setenv("TERM", TERM, 1); 2504 | 2505 | pid = forkpty(&master, ptyname, NULL, &dimensions); 2506 | if (!pid) { 2507 | 2508 | // install default signal handlers 2509 | signal(SIGQUIT, SIG_DFL); 2510 | signal(SIGTERM, SIG_DFL); 2511 | signal(SIGINT, SIG_DFL); 2512 | 2513 | if (cmdargv) { 2514 | execvp(command, cmdargv); 2515 | } else { 2516 | execlp(command, command, NULL); 2517 | } 2518 | perror("Error: exec failed\n"); 2519 | exit(-1); // should not be reached 2520 | } else if (pid == -1) { 2521 | perror("Error: fork failed\n"); 2522 | exit(-1); 2523 | } 2524 | 2525 | FD_ZERO(&fs); 2526 | FD_SET(master, &fs); 2527 | tv.tv_sec = 0; 2528 | tv.tv_usec = 5000; /* 5 ms */ 2529 | 2530 | last_time = time(NULL); 2531 | 2532 | int count = 0; 2533 | while (1) { 2534 | count++; 2535 | tv1 = tv; 2536 | fs1 = fs; 2537 | 2538 | cur_time = time(NULL); 2539 | 2540 | elapsed = cur_time - last_time; 2541 | // printf ("Elapsed %ld\n", elapsed); 2542 | 2543 | if (last_client) { 2544 | if (client_connected) { 2545 | if (idle_timeout && (elapsed >= idle_timeout)) { 2546 | break; 2547 | } 2548 | } else { 2549 | // wait at least 20 seconds for initial connect 2550 | if (idle_timeout && (elapsed >= (idle_timeout > 20 ? idle_timeout : 20))) { 2551 | break; 2552 | } 2553 | } 2554 | } else { 2555 | // exit after 30 minutes idle time 2556 | if (elapsed >= 30 * 60) { 2557 | break; 2558 | } 2559 | } 2560 | 2561 | rfbProcessEvents(vt->screen, 40000); /* 40 ms */ 2562 | 2563 | if (vt->ibuf_count > 0) { 2564 | // printf ("DEBUG: WRITE %d %d\n", vt->ibuf[0], vt->ibuf_count); 2565 | write(master, vt->ibuf, vt->ibuf_count); 2566 | vt->ibuf_count = 0; 2567 | last_time = time(NULL); 2568 | } 2569 | 2570 | if (!vt->mark_active) { 2571 | 2572 | int num_fds = select(master + 1, &fs1, NULL, NULL, &tv1); 2573 | if (num_fds >= 0) { 2574 | if (FD_ISSET(master, &fs1)) { 2575 | char buffer[1024]; 2576 | int c; 2577 | while ((c = read(master, buffer, 1024)) == -1) { 2578 | if (errno != EAGAIN) { 2579 | break; 2580 | } 2581 | } 2582 | if (c == -1) { 2583 | break; 2584 | } 2585 | vncterm_puts(vt, buffer, c); 2586 | } 2587 | } else { 2588 | break; 2589 | } 2590 | } 2591 | } 2592 | 2593 | rfbScreenCleanup(vt->screen); 2594 | 2595 | kill(pid, 9); 2596 | int status; 2597 | waitpid(pid, &status, 0); 2598 | 2599 | munmap(fontdata, sb.st_size); 2600 | exit(0); 2601 | } 2602 | --------------------------------------------------------------------------------