]> git.proxmox.com Git - vncterm.git/blob - vncterm.c
0614e4b2df9531d169f2d90cfc6d636ae1654bb8
[vncterm.git] / 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 <dietmar@proxmox.com>
22
23 */
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <arpa/inet.h>
30 #include <netdb.h>
31 #include <rfb/rfb.h>
32 #include <rfb/keysym.h>
33 #include <pty.h> /* for openpty and forkpty */
34 #include <string.h>
35 #include <errno.h>
36 #include <sys/ioctl.h>
37 #include <sys/wait.h>
38 #include <signal.h>
39 #include <locale.h>
40
41 #include "vncterm.h"
42 #include "glyphs.h"
43
44 #include <gnutls/gnutls.h>
45 #include <gnutls/x509.h>
46
47 /* define this for debugging */
48 //#define DEBUG
49
50 char *auth_path = "/";
51 char *auth_perm = "Sys.Console";
52
53 int use_x509 = 1;
54
55 static char *
56 urlencode(char *buf, const char *value)
57 {
58 static const char *hexchar = "0123456789abcdef";
59 char *p = buf;
60 int i;
61 int l = strlen(value);
62 for (i = 0; i < l; i++) {
63 char c = value[i];
64 if (('a' <= c && c <= 'z') ||
65 ('A' <= c && c <= 'Z') ||
66 ('0' <= c && c <= '9')) {
67 *p++ = c;
68 } else if (c == 32) {
69 *p++ = '+';
70 } else {
71 *p++ = '%';
72 *p++ = hexchar[c >> 4];
73 *p++ = hexchar[c & 15];
74 }
75 }
76 *p = 0;
77
78 return p;
79 }
80
81 static int
82 pve_auth_verify(const char *clientip, const char *username, const char *passwd)
83 {
84 struct sockaddr_in server;
85
86 int sfd = socket(AF_INET, SOCK_STREAM, 0);
87 if (sfd == -1) {
88 perror("pve_auth_verify: socket failed");
89 return -1;
90 }
91
92 struct hostent *he;
93 if ((he = gethostbyname("localhost")) == NULL) {
94 fprintf(stderr, "pve_auth_verify: error resolving hostname\n");
95 goto err;
96 }
97
98 memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length);
99 server.sin_family = AF_INET;
100 server.sin_port = htons(85);
101
102 if (connect(sfd, (struct sockaddr *)&server, sizeof(server))) {
103 perror("pve_auth_verify: error connecting to server");
104 goto err;
105 }
106
107 char buf[8192];
108 char form[8192];
109
110 char *p = form;
111 p = urlencode(p, "username");
112 *p++ = '=';
113 p = urlencode(p, username);
114
115 *p++ = '&';
116 p = urlencode(p, "password");
117 *p++ = '=';
118 p = urlencode(p, passwd);
119
120 *p++ = '&';
121 p = urlencode(p, "path");
122 *p++ = '=';
123 p = urlencode(p, auth_path);
124
125 *p++ = '&';
126 p = urlencode(p, "privs");
127 *p++ = '=';
128 p = urlencode(p, auth_perm);
129
130 sprintf(buf, "POST /api2/json/access/ticket HTTP/1.1\n"
131 "Host: localhost:85\n"
132 "Connection: close\n"
133 "PVEClientIP: %s\n"
134 "Content-Type: application/x-www-form-urlencoded\n"
135 "Content-Length: %zd\n\n%s\n", clientip, strlen(form), form);
136 ssize_t len = strlen(buf);
137 ssize_t sb = send(sfd, buf, len, 0);
138 if (sb < 0) {
139 perror("pve_auth_verify: send failed");
140 goto err;
141 }
142 if (sb != len) {
143 fprintf(stderr, "pve_auth_verify: partial send error\n");
144 goto err;
145 }
146
147 len = recv(sfd, buf, sizeof(buf) - 1, 0);
148 if (len < 0) {
149 perror("pve_auth_verify: recv failed");
150 goto err;
151 }
152
153 buf[len] = 0;
154
155 //printf("DATA:%s\n", buf);
156
157 shutdown(sfd, SHUT_RDWR);
158
159 return strncmp(buf, "HTTP/1.1 200 OK", 15);
160
161 err:
162 shutdown(sfd, SHUT_RDWR);
163 return -1;
164 }
165
166 #ifdef DEBUG
167 static void vnc_debug_gnutls_log(int level, const char* str) {
168 fprintf(stderr, "%d %s", level, str);
169 }
170 #endif
171
172 #define DH_BITS 2048
173 static gnutls_dh_params_t dh_params;
174
175 typedef struct {
176 gnutls_session_t session;
177 } tls_client_t;
178
179 static ssize_t
180 vnc_tls_push(
181 gnutls_transport_ptr_t transport,
182 const void *data,
183 size_t len)
184 {
185 rfbClientPtr cl = (rfbClientPtr)transport;
186 int n;
187
188 retry:
189 n = send(cl->sock, data, len, 0);
190 if (n < 0) {
191 if (errno == EINTR)
192 goto retry;
193 return -1;
194 }
195 return n;
196 }
197
198 static ssize_t
199 vnc_tls_pull(
200 gnutls_transport_ptr_t transport,
201 void *data,
202 size_t len)
203 {
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 return -1;
213 }
214 return n;
215 }
216
217 ssize_t vnc_tls_read(rfbClientPtr cl, void *buf, size_t count)
218 {
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 ret = -1;
228 }
229
230 return ret;
231 }
232 ssize_t vnc_tls_write(rfbClientPtr cl, void *buf, size_t count)
233 {
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 ret = -1;
243 }
244
245 return ret;
246 }
247
248 static gnutls_anon_server_credentials
249 tls_initialize_anon_cred(void)
250 {
251 gnutls_anon_server_credentials anon_cred;
252 int ret;
253
254 if ((ret = gnutls_anon_allocate_server_credentials(&anon_cred)) < 0) {
255 rfbLog("can't allocate credentials: %s\n", gnutls_strerror(ret));
256 return NULL;
257 }
258
259 #if GNUTLS_VERSION_NUMBER >= 0x030506
260 gnutls_anon_set_server_known_dh_params(anon_cred, GNUTLS_SEC_PARAM_MEDIUM);
261 #else
262 gnutls_anon_set_server_dh_params(anon_cred, dh_params);
263 #endif
264
265 return anon_cred;
266 }
267
268 static gnutls_certificate_credentials_t
269 tls_initialize_x509_cred(void)
270 {
271 gnutls_certificate_credentials_t x509_cred;
272 int ret;
273
274 /* Paths to x509 certs/keys */
275 char *x509cacert = "/etc/pve/pve-root-ca.pem";
276 char *x509cert = "/etc/pve/local/pve-ssl.pem";
277 char *x509key = "/etc/pve/local/pve-ssl.key";
278
279 if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0) {
280 rfbLog("can't allocate credentials: %s\n", gnutls_strerror(ret));
281 return NULL;
282 }
283
284 if ((ret = gnutls_certificate_set_x509_trust_file
285 (x509_cred, x509cacert, GNUTLS_X509_FMT_PEM)) < 0) {
286 rfbLog("can't load CA certificate: %s\n", gnutls_strerror(ret));
287 gnutls_certificate_free_credentials(x509_cred);
288 return NULL;
289 }
290
291 if ((ret = gnutls_certificate_set_x509_key_file
292 (x509_cred, x509cert, x509key, GNUTLS_X509_FMT_PEM)) < 0) {
293 rfbLog("can't load certificate & key: %s\n", gnutls_strerror(ret));
294 gnutls_certificate_free_credentials(x509_cred);
295 return NULL;
296 }
297 #if GNUTLS_VERSION_NUMBER >= 0x030506
298 /* only available since GnuTLS 3.5.6, on previous versions see
299 * gnutls_certificate_set_dh_params(). */
300 gnutls_certificate_set_known_dh_params(x509_cred, GNUTLS_SEC_PARAM_MEDIUM);
301 #else
302 gnutls_certificate_set_dh_params (x509_cred, dh_params);
303 #endif
304
305 return x509_cred;
306 }
307
308 /* rfb tls security handler */
309
310 #define rfbSecTypeVencrypt 19
311 #define rfbVencryptTlsPlain 259
312 #define rfbVencryptX509Plain 262
313
314 void rfbEncodeU32(char *buf, uint32_t value)
315 {
316 buf[0] = (value >> 24) & 0xFF;
317 buf[1] = (value >> 16) & 0xFF;
318 buf[2] = (value >> 8) & 0xFF;
319 buf[3] = value & 0xFF;
320 }
321
322 uint32_t rfbDecodeU32(char *data, size_t offset)
323 {
324 return ((data[offset] << 24) | (data[offset + 1] << 16) |
325 (data[offset + 2] << 8) | data[offset + 3]);
326 }
327
328 static void
329 vencrypt_subauth_plain(rfbClientPtr cl)
330 {
331 const char *err = NULL;
332 char buf[4096];
333 int n;
334
335 char clientip[INET6_ADDRSTRLEN];
336 clientip[0] = 0;
337 struct sockaddr_in client;
338 socklen_t addrlen = sizeof(client);
339 if (getpeername(cl->sock, &client, &addrlen) == 0) {
340 inet_ntop(client.sin_family, &client.sin_addr,
341 clientip, sizeof(clientip));
342 }
343
344 if ((n = rfbReadExact(cl, buf, 8)) <= 0) {
345 err = n ? "read failed" : "client gone";
346 goto err;
347 }
348
349 uint32_t ulen = rfbDecodeU32(buf, 0);
350 uint32_t pwlen = rfbDecodeU32(buf, 4);
351
352 if (!ulen) {
353 err = "No User name.";
354 goto err;
355 }
356 if (ulen >= 255) {
357 err = "User name too long.";
358 goto err;
359 }
360 if (!pwlen) {
361 err = "Password too short";
362 goto err;
363 }
364 if (pwlen >= 511) {
365 err = "Password too long.";
366 goto err;
367 }
368
369 if ((n = rfbReadExact(cl, buf, ulen)) <= 0) {
370 err = n ? "read failed" : "client gone";
371 goto err;
372 }
373 buf[ulen] = 0;
374 char *username = buf;
375 char *passwd = buf + ulen + 1;
376 if ((n = rfbReadExact(cl, passwd, pwlen)) <= 0) {
377 err = n ? "read failed" : "client gone";
378 goto err;
379 }
380 passwd[pwlen] = 0;
381
382 rfbLog("VencryptPlain: username: %s pw: %s\n", username, passwd);
383
384 if (pve_auth_verify(clientip, username, passwd) == 0) {
385 rfbEncodeU32(buf, 0); /* Accept auth completion */
386 rfbWriteExact(cl, buf, 4);
387 cl->state = RFB_INITIALISATION;
388 return;
389 }
390
391 err = "Authentication failed";
392 err:
393 rfbLog("VencryptPlain: %s\n", err ? err : "no reason specified");
394 if (err) {
395 rfbEncodeU32(buf, 1); /* Reject auth */
396 rfbWriteExact(cl, buf, 4);
397 if (cl->protocolMinorVersion >= 8) {
398 int elen = strlen(err);
399 rfbEncodeU32(buf, elen);
400 rfbWriteExact(cl, buf, 4);
401 rfbWriteExact(cl, err, elen);
402 }
403 }
404 rfbCloseClient(cl);
405 return;
406 }
407
408 static void
409 rfbVncAuthVencrypt(rfbClientPtr cl)
410 {
411 int ret;
412
413 /* Send VeNCrypt version 0.2 */
414 char buf[256];
415 buf[0] = 0;
416 buf[1] = 2;
417
418 if (rfbWriteExact(cl, buf, 2) < 0) {
419 rfbLogPerror("rfbVncAuthVencrypt: write");
420 rfbCloseClient(cl);
421 return;
422 }
423
424 int n = rfbReadExact(cl, buf, 2);
425 if (n <= 0) {
426 if (n == 0)
427 rfbLog("rfbVncAuthVencrypt: client gone\n");
428 else
429 rfbLogPerror("rfbVncAuthVencrypt: read");
430 rfbCloseClient(cl);
431 return;
432 }
433
434 if (buf[0] != 0 || buf[1] != 2) {
435 rfbLog("Unsupported VeNCrypt protocol %d.%d\n",
436 (int)buf[0], (int)buf[1]);
437 buf[0] = 1; /* Reject version */
438 rfbWriteExact(cl, buf, 1);
439 rfbCloseClient(cl);
440 return;
441 }
442
443 /* Sending allowed auth */
444 int req_auth = use_x509 ? rfbVencryptX509Plain : rfbVencryptTlsPlain;
445
446 buf[0] = 0; /* Accept version */
447 buf[1] = 1; /* number of sub auths */
448 rfbEncodeU32(buf+2, req_auth);
449 if (rfbWriteExact(cl, buf, 6) < 0) {
450 rfbLogPerror("rfbVncAuthVencrypt: write");
451 rfbCloseClient(cl);
452 return;
453 }
454
455 n = rfbReadExact(cl, buf, 4);
456 if (n <= 0) {
457 if (n == 0)
458 rfbLog("rfbVncAuthVencrypt: client gone\n");
459 else
460 rfbLogPerror("rfbVncAuthVencrypt: read");
461 rfbCloseClient(cl);
462 return;
463 }
464
465 int auth = rfbDecodeU32(buf, 0);
466 if (auth != req_auth) {
467 buf[0] = 1; /* Reject auth*/
468 rfbWriteExact(cl, buf, 1);
469 rfbCloseClient(cl);
470 return;
471 }
472
473 buf[0] = 1; /* Accept auth */
474 if (rfbWriteExact(cl, buf, 1) < 0) {
475 rfbLogPerror("rfbVncAuthVencrypt: write");
476 rfbCloseClient(cl);
477 return;
478 }
479
480 tls_client_t *sd = calloc(1, sizeof(tls_client_t));
481
482 if (sd->session == NULL) {
483 if (gnutls_init(&sd->session, GNUTLS_SERVER) < 0) {
484 rfbLog("gnutls_init failed\n");
485 rfbCloseClient(cl);
486 return;
487
488 }
489
490 if ((ret = gnutls_set_default_priority(sd->session)) < 0) {
491 rfbLog("gnutls_set_default_priority failed: %s\n", gnutls_strerror(ret));
492 sd->session = NULL;
493 rfbCloseClient(cl);
494 return;
495 }
496
497 /* optimize for speed */
498 static const int cipher_priority_performance[] = {
499 GNUTLS_CIPHER_ARCFOUR_128,
500 GNUTLS_CIPHER_AES_128_CBC,
501 GNUTLS_CIPHER_3DES_CBC, 0
502 };
503
504 if ((ret = gnutls_cipher_set_priority(sd->session, cipher_priority_performance)) < 0) {
505 rfbLog("gnutls_cipher_set_priority failed: %s\n", gnutls_strerror(ret));
506 sd->session = NULL;
507 rfbCloseClient(cl);
508 return;
509 }
510
511 static const int kx_anon[] = {GNUTLS_KX_ANON_DH, 0};
512 static const int kx_x509[] = {GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, GNUTLS_KX_SRP, 0};
513 if ((ret = gnutls_kx_set_priority(sd->session, use_x509 ? kx_x509 : kx_anon)) < 0) {
514 rfbLog("gnutls_kx_set_priority failed: %s\n", gnutls_strerror(ret));
515 sd->session = NULL;
516 rfbCloseClient(cl);
517 return;
518 }
519
520 static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 };
521 if ((ret = gnutls_certificate_type_set_priority(sd->session, cert_type_priority)) < 0) {
522 rfbLog("gnutls_certificate_type_set_priority failed: %s\n",
523 gnutls_strerror(ret));
524 sd->session = NULL;
525 rfbCloseClient(cl);
526 return;
527 }
528
529 static const int protocol_priority[]= { GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0 };
530 if ((ret = gnutls_protocol_set_priority(sd->session, protocol_priority)) < 0) {
531 rfbLog("gnutls_protocol_set_priority failed: %s\n",
532 gnutls_strerror(ret));
533 sd->session = NULL;
534 rfbCloseClient(cl);
535 return;
536 }
537
538 if (use_x509) {
539 gnutls_certificate_server_credentials x509_cred;
540
541 if (!(x509_cred = tls_initialize_x509_cred())) {
542 sd->session = NULL;
543 rfbCloseClient(cl);
544 return;
545 }
546
547 if (gnutls_credentials_set(sd->session, GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) {
548 sd->session = NULL;
549 gnutls_certificate_free_credentials(x509_cred);
550 rfbCloseClient(cl);
551 return;
552 }
553
554 } else {
555 gnutls_anon_server_credentials anon_cred;
556
557 if (!(anon_cred = tls_initialize_anon_cred())) {
558 sd->session = NULL;
559 rfbCloseClient(cl);
560 return;
561 }
562
563 if ((ret = gnutls_credentials_set(sd->session, GNUTLS_CRD_ANON, anon_cred)) < 0) {
564 rfbLog("gnutls_credentials_set failed: %s\n", gnutls_strerror(ret));
565 gnutls_anon_free_server_credentials(anon_cred);
566 sd->session = NULL;
567 rfbCloseClient(cl);
568 return;
569 }
570 }
571
572 gnutls_transport_set_ptr(sd->session, (gnutls_transport_ptr_t)cl);
573 gnutls_transport_set_push_function(sd->session, vnc_tls_push);
574 gnutls_transport_set_pull_function(sd->session, vnc_tls_pull);
575 }
576
577
578 retry:
579 if ((ret = gnutls_handshake(sd->session)) < 0) {
580 if (!gnutls_error_is_fatal(ret)) {
581 usleep(100000);
582 goto retry;
583 }
584 rfbLog("rfbVncAuthVencrypt: handshake failed\n");
585 rfbCloseClient(cl);
586 return;
587 }
588
589 /* set up TLS read/write hooks */
590 cl->clientData = sd;
591 cl->sock_read_fn = &vnc_tls_read;
592 cl->sock_write_fn = &vnc_tls_write;
593
594 vencrypt_subauth_plain(cl);
595 }
596
597 static rfbSecurityHandler VncSecurityHandlerVencrypt = {
598 rfbSecTypeVencrypt,
599 rfbVncAuthVencrypt,
600 NULL
601 };
602
603 #define TERM "xterm"
604
605 #define TERMIDCODE "[?1;2c" // vt100 ID
606
607 #define CHECK_ARGC(argc,argv,i) if (i >= argc-1) { \
608 fprintf (stderr, "ERROR: not enough arguments for: %s\n", argv[i]); \
609 print_usage (NULL); \
610 exit(1); \
611 }
612
613 /* these colours are from linux kernel drivers/char/vt.c */
614
615 static int idle_timeout = 1;
616
617 unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7,
618 8,12,10,14, 9,13,11,15 };
619
620 /* the default colour table, for VGA+ colour systems */
621 int default_red[] = {0x00,0xaa,0x00,0xaa,0x00,0xaa,0x00,0xaa,
622 0x55,0xff,0x55,0xff,0x55,0xff,0x55,0xff};
623 int default_grn[] = {0x00,0x00,0xaa,0x55,0x00,0x00,0xaa,0xaa,
624 0x55,0x55,0xff,0xff,0x55,0x55,0xff,0xff};
625 int default_blu[] = {0x00,0x00,0x00,0x00,0xaa,0xaa,0xaa,0xaa,
626 0x55,0x55,0x55,0x55,0xff,0xff,0xff,0xff};
627
628 static void
629 print_usage (const char *msg)
630 {
631 if (msg) { fprintf (stderr, "ERROR: %s\n", msg); }
632 fprintf (stderr, "USAGE: vncterm [vncopts] [-c command [args]]\n");
633 }
634
635 /* Convert UCS2 to UTF8 sequence, trailing zero */
636 static int
637 ucs2_to_utf8 (unicode c, char *out)
638 {
639 if (c < 0x80) {
640 out[0] = c; // 0*******
641 out[1] = 0;
642 return 1;
643 } else if (c < 0x800) {
644 out[0] = 0xc0 | (c >> 6); // 110***** 10******
645 out[1] = 0x80 | (c & 0x3f);
646 out[2] = 0;
647 return 2;
648 } else {
649 out[0] = 0xe0 | (c >> 12); // 1110**** 10****** 10******
650 out[1] = 0x80 | ((c >> 6) & 0x3f);
651 out[2] = 0x80 | (c & 0x3f);
652 out[3] = 0;
653 return 3;
654 }
655
656 return 0;
657 }
658
659 static void
660 rfb_draw_char (rfbScreenInfoPtr rfbScreen, int x, int y,
661 unsigned short c, rfbPixel col)
662 {
663 if (c > vt_font_size) {
664 rfbLog ("undefined font glyph %d\n", c);
665 return;
666 }
667
668 int i,j;
669 unsigned char *data= vt_font_data + c*16;
670 unsigned char d=*data;
671 int rowstride=rfbScreen->paddedWidthInBytes;
672 char *colour=(char*)&col;
673
674 for(j = 0; j < 16; j++) {
675 for(i = 0; i < 8; i++) {
676 if ((i&7) == 0) {
677 d=*data;
678 data++;
679 }
680 if (d&0x80)
681 *(rfbScreen->frameBuffer+(y+j)*rowstride+(x+i)) = *colour;
682 d<<=1;
683 }
684 }
685 }
686
687 static void
688 draw_char_at (vncTerm *vt, int x, int y, unicode ch, TextAttributes attrib)
689 {
690 if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; }
691
692 int rx = x*8;
693 int ry = y*16;
694 int rxe = x*8+8;
695 int rye = y*16+16;
696
697 int fg, bg;
698
699 if (attrib.invers) {
700 bg = attrib.fgcol;
701 fg = attrib.bgcol;
702 } else {
703 bg = attrib.bgcol;
704 fg = attrib.fgcol;
705 }
706
707 int ec = vt_fontmap[ch];
708
709 rfbFillRect (vt->screen, rx, ry, rxe, rye, bg);
710
711 if (attrib.bold) {
712 fg += 8;
713 }
714
715 // unsuported attributes = (attrib.blink || attrib.unvisible)
716
717 rfb_draw_char (vt->screen, rx, ry, ec, fg);
718
719 if (attrib.uline) {
720 rfbDrawLine (vt->screen, rx, ry + 14, rxe, ry + 14, fg);
721 }
722
723 rfbMarkRectAsModified (vt->screen, rx, ry, rxe, rye);
724
725 }
726
727 static void
728 vncterm_update_xy (vncTerm *vt, int x, int y)
729 {
730 if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; }
731
732 int y1 = (vt->y_base + y) % vt->total_height;
733 int y2 = y1 - vt->y_displ;
734 if (y2 < 0) {
735 y2 += vt->total_height;
736 }
737 if (y2 < vt->height) {
738 TextCell *c = &vt->cells[y1 * vt->width + x];
739 draw_char_at (vt, x, y2, c->ch, c->attrib);
740 }
741 }
742
743 static void
744 vncterm_clear_xy (vncTerm *vt, int x, int y)
745 {
746 if (x < 0 || y < 0 || x >= vt->width || y >= vt->height) { return; }
747
748 int y1 = (vt->y_base + y) % vt->total_height;
749 int y2 = y1 - vt->y_displ;
750 if (y2 < 0) {
751 y2 += vt->total_height;
752 }
753 if (y2 < vt->height) {
754 TextCell *c = &vt->cells[y1 * vt->width + x];
755 c->ch = ' ';
756 c->attrib = vt->default_attrib;
757 c->attrib.fgcol = vt->cur_attrib.fgcol;
758 c->attrib.bgcol = vt->cur_attrib.bgcol;
759
760 draw_char_at (vt, x, y, c->ch, c->attrib);
761 }
762 }
763
764 static void
765 vncterm_show_cursor (vncTerm *vt, int show)
766 {
767 int x = vt->cx;
768 if (x >= vt->width) {
769 x = vt->width - 1;
770 }
771
772 int y1 = (vt->y_base + vt->cy) % vt->total_height;
773 int y = y1 - vt->y_displ;
774 if (y < 0) {
775 y += vt->total_height;
776 }
777
778 if (y < vt->height) {
779
780 TextCell *c = &vt->cells[y1 * vt->width + x];
781
782 if (show) {
783 TextAttributes attrib = vt->default_attrib;
784 attrib.invers = !(attrib.invers); /* invert fg and bg */
785 draw_char_at (vt, x, y, c->ch, attrib);
786 } else {
787 draw_char_at (vt, x, y, c->ch, c->attrib);
788 }
789 }
790 }
791
792 static void
793 vncterm_refresh (vncTerm *vt)
794 {
795 int x, y, y1;
796
797 rfbFillRect (vt->screen, 0, 0, vt->maxx, vt->maxy, vt->default_attrib.bgcol);
798
799 y1 = vt->y_displ;
800 for(y = 0; y < vt->height; y++) {
801 TextCell *c = vt->cells + y1 * vt->width;
802 for(x = 0; x < vt->width; x++) {
803 draw_char_at (vt, x, y, c->ch, c->attrib);
804 c++;
805 }
806 if (++y1 == vt->total_height)
807 y1 = 0;
808 }
809 rfbMarkRectAsModified (vt->screen, 0, 0, vt->maxx, vt->maxy);
810
811 vncterm_show_cursor (vt, 1);
812 }
813
814 static void
815 vncterm_scroll_down (vncTerm *vt, int top, int bottom, int lines)
816 {
817 if ((top + lines) >= bottom) {
818 lines = bottom - top -1;
819 }
820
821 if (top < 0 || bottom > vt->height || top >= bottom || lines < 1) {
822 return;
823 }
824
825 int h = lines * 16;
826 int y0 = top*16;
827 int y1 = y0 + h;
828 int y2 = bottom*16;
829 int rowstride = vt->screen->paddedWidthInBytes;
830 int rows = (bottom - top - lines)*16;
831
832 char *in = vt->screen->frameBuffer+y0*rowstride;
833 char *out = vt->screen->frameBuffer+y1*rowstride;
834 memmove(out,in, rowstride*rows);
835
836 memset(vt->screen->frameBuffer+y0*rowstride, 0, h*rowstride);
837 rfbMarkRectAsModified (vt->screen, 0, y0, vt->screen->width, y2);
838
839 int i;
840 for(i = bottom - top - lines - 1; i >= 0; i--) {
841 int src = ((vt->y_base + top + i) % vt->total_height)*vt->width;
842 int dst = ((vt->y_base + top + lines + i) % vt->total_height)*vt->width;
843
844 memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof (TextCell));
845 }
846
847 for (i = 0; i < lines; i++) {
848 int j;
849 TextCell *c = vt->cells + ((vt->y_base + top + i) % vt->total_height)*vt->width;
850 for(j = 0; j < vt->width; j++) {
851 c->attrib = vt->default_attrib;
852 c->ch = ' ';
853 c++;
854 }
855 }
856 }
857
858 static void
859 vncterm_scroll_up (vncTerm *vt, int top, int bottom, int lines, int moveattr)
860 {
861 if ((top + lines) >= bottom) {
862 lines = bottom - top - 1;
863 }
864
865 if (top < 0 || bottom > vt->height || top >= bottom || lines < 1) {
866 return;
867 }
868
869 int h = lines * 16;
870 int y0 = top*16;
871 int y1 = (top + lines)*16;
872 int y2 = bottom*16;
873 int rowstride = vt->screen->paddedWidthInBytes;
874 int rows = (bottom - top - lines)*16;
875
876 char *in = vt->screen->frameBuffer+y1*rowstride;
877 char *out = vt->screen->frameBuffer+y0*rowstride;
878 memmove(out,in, rowstride*rows);
879
880 memset(vt->screen->frameBuffer+(y2-h)*rowstride, 0, h*rowstride);
881
882 rfbMarkRectAsModified (vt->screen, 0, y0, vt->screen->width, y2);
883
884 if (!moveattr) return;
885
886 // move attributes
887
888 int i;
889 for(i = 0; i < (bottom - top - lines); i++) {
890 int dst = ((vt->y_base + top + i) % vt->total_height)*vt->width;
891 int src = ((vt->y_base + top + lines + i) % vt->total_height)*vt->width;
892
893 memmove(vt->cells + dst, vt->cells + src, vt->width*sizeof (TextCell));
894 }
895
896 for (i = 1; i <= lines; i++) {
897 int j;
898 TextCell *c = vt->cells + ((vt->y_base + bottom - i) % vt->total_height)*vt->width;
899 for(j = 0; j < vt->width; j++) {
900 c->attrib = vt->default_attrib;
901 c->ch = ' ';
902 c++;
903 }
904 }
905 }
906
907 static void
908 vncterm_virtual_scroll (vncTerm *vt, int lines)
909 {
910 if (vt->altbuf || lines == 0) return;
911
912 if (lines < 0) {
913 lines = -lines;
914 int i = vt->scroll_height;
915 if (i > vt->total_height - vt->height)
916 i = vt->total_height - vt->height;
917 int y1 = vt->y_base - i;
918 if (y1 < 0)
919 y1 += vt->total_height;
920 for(i = 0; i < lines; i++) {
921 if (vt->y_displ == y1) break;
922 if (--vt->y_displ < 0) {
923 vt->y_displ = vt->total_height - 1;
924 }
925 }
926 } else {
927 int i;
928 for(i = 0; i < lines; i++) {
929 if (vt->y_displ == vt->y_base) break;
930 if (++vt->y_displ == vt->total_height) {
931 vt->y_displ = 0;
932 }
933 }
934
935 }
936
937 vncterm_refresh (vt);
938 }
939 static void
940 vncterm_respond_esc (vncTerm *vt, const char *esc)
941 {
942 int len = strlen (esc);
943 int i;
944
945 if (vt->ibuf_count < (IBUFSIZE - 1 - len)) {
946 vt->ibuf[vt->ibuf_count++] = 27;
947 for (i = 0; i < len; i++) {
948 vt->ibuf[vt->ibuf_count++] = esc[i];
949 }
950 }
951 }
952
953 static void
954 vncterm_put_lf (vncTerm *vt)
955 {
956 if (vt->cy + 1 == vt->region_bottom) {
957
958 if (vt->altbuf || vt->region_top != 0 || vt->region_bottom != vt->height) {
959 vncterm_scroll_up (vt, vt->region_top, vt->region_bottom, 1, 1);
960 return;
961 }
962
963 if (vt->y_displ == vt->y_base) {
964 vncterm_scroll_up (vt, vt->region_top, vt->region_bottom, 1, 0);
965 }
966
967 if (vt->y_displ == vt->y_base) {
968 if (++vt->y_displ == vt->total_height) {
969 vt->y_displ = 0;
970 }
971 }
972
973 if (++vt->y_base == vt->total_height) {
974 vt->y_base = 0;
975 }
976
977 if (vt->scroll_height < vt->total_height) {
978 vt->scroll_height++;
979 }
980
981 int y1 = (vt->y_base + vt->height - 1) % vt->total_height;
982 TextCell *c = &vt->cells[y1 * vt->width];
983 int x;
984 for (x = 0; x < vt->width; x++) {
985 c->ch = ' ';
986 c->attrib = vt->default_attrib;
987 c++;
988 }
989
990 // fprintf (stderr, "BASE: %d DISPLAY %d\n", vt->y_base, vt->y_displ);
991
992 } else if (vt->cy < vt->height - 1) {
993 vt->cy += 1;
994 }
995 }
996
997
998 static void
999 vncterm_csi_m (vncTerm *vt)
1000 {
1001 int i;
1002
1003 for (i = 0; i < vt->esc_count; i++) {
1004 switch (vt->esc_buf[i]) {
1005 case 0: /* reset all console attributes to default */
1006 vt->cur_attrib = vt->default_attrib;
1007 break;
1008 case 1:
1009 vt->cur_attrib.bold = 1;
1010 break;
1011 case 4:
1012 vt->cur_attrib.uline = 1;
1013 break;
1014 case 5:
1015 vt->cur_attrib.blink = 1;
1016 break;
1017 case 7:
1018 vt->cur_attrib.invers = 1;
1019 break;
1020 case 8:
1021 vt->cur_attrib.unvisible = 1;
1022 break;
1023 case 10:
1024 vt->cur_enc = LAT1_MAP;
1025 // fixme: dispaly controls = 0 ?
1026 // fixme: toggle meta = 0 ?
1027 break;
1028 case 11:
1029 vt->cur_enc = IBMPC_MAP;
1030 // fixme: dispaly controls = 1 ?
1031 // fixme: toggle meta = 0 ?
1032 break;
1033 case 12:
1034 vt->cur_enc = IBMPC_MAP;
1035 // fixme: dispaly controls = 1 ?
1036 // fixme: toggle meta = 1 ?
1037 break;
1038 case 22:
1039 vt->cur_attrib.bold = 0;
1040 break;
1041 case 24:
1042 vt->cur_attrib.uline = 0;
1043 break;
1044 case 25:
1045 vt->cur_attrib.blink = 0;
1046 break;
1047 case 27:
1048 vt->cur_attrib.invers = 0;
1049 break;
1050 case 28:
1051 vt->cur_attrib.unvisible = 0;
1052 break;
1053 case 30:
1054 case 31:
1055 case 32:
1056 case 33:
1057 case 34:
1058 case 35:
1059 case 36:
1060 case 37:
1061 /* set foreground color */
1062 vt->cur_attrib.fgcol = color_table [vt->esc_buf[i] - 30];
1063 break;
1064 case 38:
1065 /* reset color to default, enable underline */
1066 vt->cur_attrib.fgcol = vt->default_attrib.fgcol;
1067 vt->cur_attrib.uline = 1;
1068 break;
1069 case 39:
1070 /* reset color to default, disable underline */
1071 vt->cur_attrib.fgcol = vt->default_attrib.fgcol;
1072 vt->cur_attrib.uline = 0;
1073 break;
1074 case 40:
1075 case 41:
1076 case 42:
1077 case 43:
1078 case 44:
1079 case 45:
1080 case 46:
1081 case 47:
1082 /* set background color */
1083 vt->cur_attrib.bgcol = color_table [vt->esc_buf[i] - 40];
1084 break;
1085 case 49:
1086 /* reset background color */
1087 vt->cur_attrib.bgcol = vt->default_attrib.bgcol;
1088 break;
1089 default:
1090 fprintf (stderr, "unhandled ESC[%d m code\n",vt->esc_buf[i]);
1091 //fixme: implement
1092 }
1093 }
1094 }
1095
1096 static void
1097 vncterm_save_cursor (vncTerm *vt)
1098 {
1099 vt->cx_saved = vt->cx;
1100 vt->cy_saved = vt->cy;
1101 vt->cur_attrib_saved = vt->cur_attrib;
1102 vt->charset_saved = vt->charset;
1103 vt->g0enc_saved = vt->g0enc;
1104 vt->g1enc_saved = vt->g1enc;
1105 vt->cur_enc_saved = vt->cur_enc;
1106 }
1107
1108 static void
1109 vncterm_restore_cursor (vncTerm *vt)
1110 {
1111 vt->cx = vt->cx_saved;
1112 vt->cy = vt->cy_saved;
1113 vt->cur_attrib = vt->cur_attrib_saved;
1114 vt->charset = vt->charset_saved;
1115 vt->g0enc = vt->g0enc_saved;
1116 vt->g1enc = vt->g1enc_saved;
1117 vt->cur_enc = vt->cur_enc_saved;
1118 }
1119
1120 static void
1121 vncterm_set_alternate_buffer (vncTerm *vt, int on_off)
1122 {
1123 int x, y;
1124
1125 vt->y_displ = vt->y_base;
1126
1127 if (on_off) {
1128
1129 if (vt->altbuf) return;
1130
1131 vt->altbuf = 1;
1132
1133 /* alternate buffer & cursor */
1134
1135 vncterm_save_cursor (vt);
1136 /* save screen to altcels */
1137 for (y = 0; y < vt->height; y++) {
1138 int y1 = (vt->y_base + y) % vt->total_height;
1139 for (x = 0; x < vt->width; x++) {
1140 vt->altcells[y*vt->width + x] = vt->cells[y1*vt->width + x];
1141 }
1142 }
1143
1144 /* clear screen */
1145 for (y = 0; y <= vt->height; y++) {
1146 for (x = 0; x < vt->width; x++) {
1147 vncterm_clear_xy (vt, x, y);
1148 }
1149 }
1150
1151 } else {
1152
1153 if (vt->altbuf == 0) return;
1154
1155 vt->altbuf = 0;
1156
1157 /* restore saved data */
1158 for (y = 0; y < vt->height; y++) {
1159 int y1 = (vt->y_base + y) % vt->total_height;
1160 for (x = 0; x < vt->width; x++) {
1161 vt->cells[y1*vt->width + x] = vt->altcells[y*vt->width + x];
1162 }
1163 }
1164
1165 vncterm_restore_cursor (vt);
1166 }
1167
1168 vncterm_refresh (vt);
1169 }
1170
1171 static void
1172 vncterm_set_mode (vncTerm *vt, int on_off)
1173 {
1174 int i;
1175
1176 for (i = 0; i <= vt->esc_count; i++) {
1177 if (vt->esc_ques) { /* DEC private modes set/reset */
1178 switch(vt->esc_buf[i]) {
1179 case 10: /* X11 mouse reporting on/off */
1180 case 1000:
1181 vt->report_mouse = on_off;
1182 break;
1183 case 1049: /* start/end special app mode (smcup/rmcup) */
1184 vncterm_set_alternate_buffer (vt, on_off);
1185 break;
1186 case 25: /* Cursor on/off */
1187 case 9: /* X10 mouse reporting on/off */
1188 case 6: /* Origin relative/absolute */
1189 case 1: /* Cursor keys in appl mode*/
1190 case 5: /* Inverted screen on/off */
1191 case 7: /* Autowrap on/off */
1192 case 8: /* Autorepeat on/off */
1193 break;
1194 }
1195 } else { /* ANSI modes set/reset */
1196 /* fixme: implement me */
1197 }
1198 }
1199 }
1200
1201 static void
1202 vncterm_gotoxy (vncTerm *vt, int x, int y)
1203 {
1204 /* verify all boundaries */
1205
1206 if (x < 0) {
1207 x = 0;
1208 }
1209
1210 if (x >= vt->width) {
1211 x = vt->width - 1;
1212 }
1213
1214 vt->cx = x;
1215
1216 if (y < 0) {
1217 y = 0;
1218 }
1219
1220 if (y >= vt->height) {
1221 y = vt->height - 1;
1222 }
1223
1224 vt->cy = y;
1225 }
1226
1227 enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey,
1228 EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd,
1229 ESpalette, ESidquery, ESosc1, ESosc2};
1230
1231 static void
1232 vncterm_putchar (vncTerm *vt, unicode ch)
1233 {
1234 int x, y, i, c;
1235
1236 #ifdef DEBUG
1237 if (!vt->tty_state)
1238 fprintf (stderr, "CHAR:%2d: %4x '%c' (cur_enc %d) %d %d\n", vt->tty_state, ch, ch, vt->cur_enc, vt->cx, vt->cy);
1239 #endif
1240
1241 switch(vt->tty_state) {
1242 case ESesc:
1243 vt->tty_state = ESnormal;
1244 switch (ch) {
1245 case '[':
1246 vt->tty_state = ESsquare;
1247 break;
1248 case ']':
1249 vt->tty_state = ESnonstd;
1250 break;
1251 case '%':
1252 vt->tty_state = ESpercent;
1253 break;
1254 case '7':
1255 vncterm_save_cursor (vt);
1256 break;
1257 case '8':
1258 vncterm_restore_cursor (vt);
1259 break;
1260 case '(':
1261 vt->tty_state = ESsetG0; // SET G0
1262 break;
1263 case ')':
1264 vt->tty_state = ESsetG1; // SET G1
1265 break;
1266 case 'M':
1267 /* cursor up (ri) */
1268 if (vt->cy == vt->region_top)
1269 vncterm_scroll_down (vt, vt->region_top, vt->region_bottom, 1);
1270 else if (vt->cy > 0) {
1271 vt->cy--;
1272 }
1273 break;
1274 case '>':
1275 /* numeric keypad - ignored */
1276 break;
1277 case '=':
1278 /* appl. keypad - ignored */
1279 break;
1280 default:
1281 #ifdef DEBUG
1282 fprintf(stderr, "got unhandled ESC%c %d\n", ch, ch);
1283 #endif
1284 break;
1285 }
1286 break;
1287 case ESnonstd: /* Operating System Controls */
1288 vt->tty_state = ESnormal;
1289
1290 switch (ch) {
1291 case 'P': /* palette escape sequence */
1292 for(i = 0; i < MAX_ESC_PARAMS; i++) {
1293 vt->esc_buf[i] = 0;
1294 }
1295
1296 vt->esc_count = 0;
1297 vt->tty_state = ESpalette;
1298 break;
1299 case 'R': /* reset palette */
1300 // fixme: reset_palette(vc);
1301 break;
1302 case '0':
1303 case '1':
1304 case '2':
1305 case '4':
1306 vt->osc_cmd = ch;
1307 vt->osc_textbuf[0] = 0;
1308 vt->tty_state = ESosc1;
1309 break;
1310 default:
1311 #ifdef DEBUG
1312 fprintf (stderr, "unhandled OSC %c\n", ch);
1313 #endif
1314 vt->tty_state = ESnormal;
1315 break;
1316 }
1317 break;
1318 case ESosc1:
1319 vt->tty_state = ESnormal;
1320 if (ch == ';') {
1321 vt->tty_state = ESosc2;
1322 } else {
1323 #ifdef DEBUG
1324 fprintf (stderr, "got illegal OSC sequence\n");
1325 #endif
1326 }
1327 break;
1328 case ESosc2:
1329 if (ch != 0x9c && ch != 7) {
1330 int i = 0;
1331 while (vt->osc_textbuf[i]) i++;
1332 vt->osc_textbuf[i++] = ch;
1333 vt->osc_textbuf[i] = 0;
1334 } else {
1335 #ifdef DEBUG
1336 fprintf (stderr, "OSC:%c:%s\n", vt->osc_cmd, vt->osc_textbuf);
1337 #endif
1338 vt->tty_state = ESnormal;
1339 }
1340 break;
1341 case ESpalette:
1342 if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F')
1343 || (ch >= 'a' && ch <= 'f')) {
1344 vt->esc_buf[vt->esc_count++] = (ch > '9' ? (ch & 0xDF) - 'A' + 10 : ch - '0');
1345 if (vt->esc_count == 7) {
1346 // fixme: this does not work - please test
1347 rfbColourMap *cmap =&vt->screen->colourMap;
1348
1349 int i = color_table[vt->esc_buf[0]] * 3, j = 1;
1350 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
1351 cmap->data.bytes[i++] += vt->esc_buf[j++];
1352 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
1353 cmap->data.bytes[i++] += vt->esc_buf[j++];
1354 cmap->data.bytes[i] = 16 * vt->esc_buf[j++];
1355 cmap->data.bytes[i] += vt->esc_buf[j];
1356
1357 //set_palette(vc); ?
1358
1359 vt->tty_state = ESnormal;
1360 }
1361 } else
1362 vt->tty_state = ESnormal;
1363 break;
1364 case ESsquare:
1365 for(i = 0; i < MAX_ESC_PARAMS; i++) {
1366 vt->esc_buf[i] = 0;
1367 }
1368
1369 vt->esc_count = 0;
1370 vt->esc_has_par = 0;
1371 vt->tty_state = ESgetpars;
1372
1373 if (ch == '>') {
1374 vt->tty_state = ESidquery;
1375 break;
1376 }
1377
1378 if ((vt->esc_ques = (ch == '?'))) {
1379 break;
1380 }
1381 case ESgetpars:
1382 if (ch >= '0' && ch <= '9') {
1383 vt->esc_has_par = 1;
1384 if (vt->esc_count < MAX_ESC_PARAMS) {
1385 vt->esc_buf[vt->esc_count] = vt->esc_buf[vt->esc_count] * 10 + ch - '0';
1386 }
1387 break;
1388 } else if (ch == ';') {
1389 vt->esc_count++;
1390 break;
1391 } else {
1392 if (vt->esc_has_par) {
1393 vt->esc_count++;
1394 }
1395 vt->tty_state = ESgotpars;
1396 }
1397 case ESgotpars:
1398
1399 vt->tty_state = ESnormal;
1400
1401 #ifdef DEBUG
1402 char *qes = vt->esc_ques ? "?" : "";
1403 if (vt->esc_count == 0) {
1404 fprintf(stderr, "ESC[%s%c\n", qes, ch);
1405 } else if (vt->esc_count == 1) {
1406 fprintf(stderr, "ESC[%s%d%c\n", qes, vt->esc_buf[0], ch);
1407 } else {
1408 int i;
1409 fprintf(stderr, "ESC[%s%d", qes, vt->esc_buf[0]);
1410 for (i = 1; i < vt->esc_count; i++) {
1411 fprintf(stderr, ";%d", vt->esc_buf[i]);
1412 }
1413 fprintf (stderr, "%c\n", ch);
1414 }
1415 #endif
1416
1417 switch (ch) {
1418 case 'h':
1419 vncterm_set_mode (vt, 1);
1420 break;
1421 case 'l':
1422 vncterm_set_mode (vt, 0);
1423 break;
1424 case 'm':
1425 if (!vt->esc_count) {
1426 vt->esc_count++; // default parameter 0
1427 }
1428 vncterm_csi_m (vt);
1429 break;
1430 case 'n':
1431 /* report cursor position */
1432 /* TODO: send ESC[row;colR */
1433 break;
1434 case 'A':
1435 /* move cursor up */
1436 if (vt->esc_buf[0] == 0) {
1437 vt->esc_buf[0] = 1;
1438 }
1439 vt->cy -= vt->esc_buf[0];
1440 if (vt->cy < 0) {
1441 vt->cy = 0;
1442 }
1443 break;
1444 case 'B':
1445 case 'e':
1446 /* move cursor down */
1447 if (vt->esc_buf[0] == 0) {
1448 vt->esc_buf[0] = 1;
1449 }
1450 vt->cy += vt->esc_buf[0];
1451 if (vt->cy >= vt->height) {
1452 vt->cy = vt->height - 1;
1453 }
1454 break;
1455 case 'C':
1456 case 'a':
1457 /* move cursor right */
1458 if (vt->esc_buf[0] == 0) {
1459 vt->esc_buf[0] = 1;
1460 }
1461 vt->cx += vt->esc_buf[0];
1462 if (vt->cx >= vt->width) {
1463 vt->cx = vt->width - 1;
1464 }
1465 break;
1466 case 'D':
1467 /* move cursor left */
1468 if (vt->esc_buf[0] == 0) {
1469 vt->esc_buf[0] = 1;
1470 }
1471 vt->cx -= vt->esc_buf[0];
1472 if (vt->cx < 0) {
1473 vt->cx = 0;
1474 }
1475 break;
1476 case 'G':
1477 case '`':
1478 /* move cursor to column */
1479 vncterm_gotoxy (vt, vt->esc_buf[0] - 1, vt->cy);
1480 break;
1481 case 'd':
1482 /* move cursor to row */
1483 vncterm_gotoxy (vt, vt->cx , vt->esc_buf[0] - 1);
1484 break;
1485 case 'f':
1486 case 'H':
1487 /* move cursor to row, column */
1488 vncterm_gotoxy (vt, vt->esc_buf[1] - 1, vt->esc_buf[0] - 1);
1489 break;
1490 case 'J':
1491 switch (vt->esc_buf[0]) {
1492 case 0:
1493 /* clear to end of screen */
1494 for (y = vt->cy; y < vt->height; y++) {
1495 for (x = 0; x < vt->width; x++) {
1496 if (y == vt->cy && x < vt->cx) {
1497 continue;
1498 }
1499 vncterm_clear_xy (vt, x, y);
1500 }
1501 }
1502 break;
1503 case 1:
1504 /* clear from beginning of screen */
1505 for (y = 0; y <= vt->cy; y++) {
1506 for (x = 0; x < vt->width; x++) {
1507 if (y == vt->cy && x > vt->cx) {
1508 break;
1509 }
1510 vncterm_clear_xy (vt, x, y);
1511 }
1512 }
1513 break;
1514 case 2:
1515 /* clear entire screen */
1516 for (y = 0; y <= vt->height; y++) {
1517 for (x = 0; x < vt->width; x++) {
1518 vncterm_clear_xy (vt, x, y);
1519 }
1520 }
1521 break;
1522 }
1523 break;
1524 case 'K':
1525 switch (vt->esc_buf[0]) {
1526 case 0:
1527 /* clear to eol */
1528 for(x = vt->cx; x < vt->width; x++) {
1529 vncterm_clear_xy (vt, x, vt->cy);
1530 }
1531 break;
1532 case 1:
1533 /* clear from beginning of line */
1534 for (x = 0; x <= vt->cx; x++) {
1535 vncterm_clear_xy (vt, x, vt->cy);
1536 }
1537 break;
1538 case 2:
1539 /* clear entire line */
1540 for(x = 0; x < vt->width; x++) {
1541 vncterm_clear_xy (vt, x, vt->cy);
1542 }
1543 break;
1544 }
1545 break;
1546 case 'L':
1547 /* insert line */
1548 c = vt->esc_buf[0];
1549
1550 if (c > vt->height - vt->cy)
1551 c = vt->height - vt->cy;
1552 else if (!c)
1553 c = 1;
1554
1555 vncterm_scroll_down (vt, vt->cy, vt->region_bottom, c);
1556 break;
1557 case 'M':
1558 /* delete line */
1559 c = vt->esc_buf[0];
1560
1561 if (c > vt->height - vt->cy)
1562 c = vt->height - vt->cy;
1563 else if (!c)
1564 c = 1;
1565
1566 vncterm_scroll_up (vt, vt->cy, vt->region_bottom, c, 1);
1567 break;
1568 case 'T':
1569 /* scroll down */
1570 c = vt->esc_buf[0];
1571 if (!c) c = 1;
1572 vncterm_scroll_down (vt, vt->region_top, vt->region_bottom, c);
1573 break;
1574 case 'S':
1575 /* scroll up */
1576 c = vt->esc_buf[0];
1577 if (!c) c = 1;
1578 vncterm_scroll_up (vt, vt->region_top, vt->region_bottom, c, 1);
1579 break;
1580 case 'P':
1581 /* delete c character */
1582 c = vt->esc_buf[0];
1583
1584 if (c > vt->width - vt->cx)
1585 c = vt->width - vt->cx;
1586 else if (!c)
1587 c = 1;
1588
1589 for (x = vt->cx; x < vt->width - c; x++) {
1590 int y1 = (vt->y_base + vt->cy) % vt->total_height;
1591 TextCell *dst = &vt->cells[y1 * vt->width + x];
1592 TextCell *src = dst + c;
1593 *dst = *src;
1594 vncterm_update_xy (vt, x + c, vt->cy);
1595 src->ch = ' ';
1596 src->attrib = vt->default_attrib;
1597 vncterm_update_xy (vt, x, vt->cy);
1598 }
1599 break;
1600 case 's':
1601 /* save cursor position */
1602 vncterm_save_cursor (vt);
1603 break;
1604 case 'u':
1605 /* restore cursor position */
1606 vncterm_restore_cursor (vt);
1607 break;
1608 case 'X':
1609 /* erase c characters */
1610 c = vt->esc_buf[0];
1611 if (!c) c = 1;
1612
1613 if (c > (vt->width - vt->cx)) c = vt->width - vt->cx;
1614
1615 for(i = 0; i < c; i++) {
1616 vncterm_clear_xy (vt, vt->cx + i, vt->cy);
1617 }
1618 break;
1619 case '@':
1620 /* insert c character */
1621 c = vt->esc_buf[0];
1622 if (c > (vt->width - vt->cx)) {
1623 c = vt->width - vt->cx;
1624 }
1625 if (!c) c = 1;
1626
1627 for (x = vt->width - c; x >= vt->cx; x--) {
1628 int y1 = (vt->y_base + vt->cy) % vt->total_height;
1629 TextCell *src = &vt->cells[y1 * vt->width + x];
1630 TextCell *dst = src + c;
1631 *dst = *src;
1632 vncterm_update_xy (vt, x + c, vt->cy);
1633 src->ch = ' ';
1634 src->attrib = vt->cur_attrib;
1635 vncterm_update_xy (vt, x, vt->cy);
1636 }
1637
1638 break;
1639 case 'r':
1640 /* set region */
1641 if (!vt->esc_buf[0])
1642 vt->esc_buf[0]++;
1643 if (!vt->esc_buf[1])
1644 vt->esc_buf[1] = vt->height;
1645 /* Minimum allowed region is 2 lines */
1646 if (vt->esc_buf[0] < vt->esc_buf[1] &&
1647 vt->esc_buf[1] <= vt->height) {
1648 vt->region_top = vt->esc_buf[0] - 1;
1649 vt->region_bottom = vt->esc_buf[1];
1650 vt->cx = 0;
1651 vt->cy = vt->region_top;
1652 #ifdef DEBUG
1653 fprintf (stderr, "set region %d %d\n", vt->region_top, vt->region_bottom);
1654 #endif
1655 }
1656
1657 break;
1658 default:
1659 #ifdef DEBUG
1660 if (vt->esc_count == 0) {
1661 fprintf(stderr, "unhandled escape ESC[%s%c\n", qes, ch);
1662 } else if (vt->esc_count == 1) {
1663 fprintf(stderr, "unhandled escape ESC[%s%d%c\n", qes, vt->esc_buf[0], ch);
1664 } else {
1665 int i;
1666 fprintf(stderr, "unhandled escape ESC[%s%d", qes, vt->esc_buf[0]);
1667 for (i = 1; i < vt->esc_count; i++) {
1668 fprintf(stderr, ";%d", vt->esc_buf[i]);
1669 }
1670 fprintf (stderr, "%c\n", ch);
1671 }
1672 #endif
1673 break;
1674 }
1675 vt->esc_ques = 0;
1676 break;
1677 case ESsetG0: // Set G0
1678 vt->tty_state = ESnormal;
1679
1680 if (ch == '0')
1681 vt->g0enc = GRAF_MAP;
1682 else if (ch == 'B')
1683 vt->g0enc = LAT1_MAP;
1684 else if (ch == 'U')
1685 vt->g0enc = IBMPC_MAP;
1686 else if (ch == 'K')
1687 vt->g0enc = USER_MAP;
1688
1689 if (vt->charset == 0)
1690 vt->cur_enc = vt->g0enc;
1691
1692 break;
1693 case ESsetG1: // Set G1
1694 vt->tty_state = ESnormal;
1695
1696 if (ch == '0')
1697 vt->g1enc = GRAF_MAP;
1698 else if (ch == 'B')
1699 vt->g1enc = LAT1_MAP;
1700 else if (ch == 'U')
1701 vt->g1enc = IBMPC_MAP;
1702 else if (ch == 'K')
1703 vt->g1enc = USER_MAP;
1704
1705 if (vt->charset == 1)
1706 vt->cur_enc = vt->g1enc;
1707
1708 break;
1709 case ESidquery: // vt100 query id
1710 vt->tty_state = ESnormal;
1711
1712 if (ch == 'c') {
1713 #ifdef DEBUG
1714 fprintf (stderr, "ESC[>c Query term ID\n");
1715 #endif
1716 vncterm_respond_esc (vt, TERMIDCODE);
1717 }
1718 break;
1719 case ESpercent:
1720 vt->tty_state = ESnormal;
1721 switch (ch) {
1722 case '@': /* defined in ISO 2022 */
1723 vt->utf8 = 0;
1724 break;
1725 case 'G': /* prelim official escape code */
1726 case '8': /* retained for compatibility */
1727 vt->utf8 = 1;
1728 break;
1729 }
1730 break;
1731 default: // ESnormal
1732 vt->tty_state = ESnormal;
1733
1734 switch(ch) {
1735 case 0:
1736 break;
1737 case 7: /* alert aka. bell */
1738 rfbSendBell(vt->screen);
1739 break;
1740 case 8: /* backspace */
1741 if (vt->cx > 0)
1742 vt->cx--;
1743 break;
1744 case 9: /* tabspace */
1745 if (vt->cx + (8 - (vt->cx % 8)) > vt->width) {
1746 vt->cx = 0;
1747 vncterm_put_lf (vt);
1748 } else {
1749 vt->cx = vt->cx + (8 - (vt->cx % 8));
1750 }
1751 break;
1752 case 10: /* LF,*/
1753 case 11: /* VT */
1754 case 12: /* FF */
1755 vncterm_put_lf (vt);
1756 break;
1757 case 13: /* carriage return */
1758 vt->cx = 0;
1759 break;
1760 case 14:
1761 /* SI (shift in), select character set 1 */
1762 vt->charset = 1;
1763 vt->cur_enc = vt->g1enc;
1764 /* fixme: display controls = 1 */
1765 break;
1766 case 15:
1767 /* SO (shift out), select character set 0 */
1768 vt->charset = 0;
1769 vt->cur_enc = vt->g0enc;
1770 /* fixme: display controls = 0 */
1771 break;
1772 case 27: /* esc */
1773 vt->tty_state = ESesc;
1774 break;
1775 case 127: /* delete */
1776 /* ignore */
1777 break;
1778 case 128+27: /* csi */
1779 vt->tty_state = ESsquare;
1780 break;
1781 default:
1782 if (vt->cx >= vt->width) {
1783 /* line wrap */
1784 vt->cx = 0;
1785 vncterm_put_lf (vt);
1786 }
1787
1788 int y1 = (vt->y_base + vt->cy) % vt->total_height;
1789 TextCell *c = &vt->cells[y1*vt->width + vt->cx];
1790 c->attrib = vt->cur_attrib;
1791 c->ch = ch;
1792 vncterm_update_xy (vt, vt->cx, vt->cy);
1793 vt->cx++;
1794 break;
1795 }
1796 break;
1797 }
1798 }
1799
1800 static int
1801 vncterm_puts (vncTerm *vt, const char *buf, int len)
1802 {
1803 unicode tc;
1804
1805 vncterm_show_cursor (vt, 0);
1806
1807 while (len) {
1808 unsigned char c = *buf;
1809 len--;
1810 buf++;
1811
1812 if (vt->tty_state != ESnormal) {
1813 // never translate escape sequence
1814 tc = c;
1815 } else if (vt->utf8 && !vt->cur_enc) {
1816
1817 if(c & 0x80) { // utf8 multi-byte sequence
1818
1819 if (vt->utf_count > 0 && (c & 0xc0) == 0x80) {
1820 // inside UTF8 sequence
1821 vt->utf_char = (vt->utf_char << 6) | (c & 0x3f);
1822 vt->utf_count--;
1823 if (vt->utf_count == 0) {
1824 tc = vt->utf_char;
1825 } else {
1826 continue;
1827 }
1828 } else {
1829 // first char of a UTF8 sequence
1830 if ((c & 0xe0) == 0xc0) {
1831 vt->utf_count = 1;
1832 vt->utf_char = (c & 0x1f);
1833 } else if ((c & 0xf0) == 0xe0) {
1834 vt->utf_count = 2;
1835 vt->utf_char = (c & 0x0f);
1836 } else if ((c & 0xf8) == 0xf0) {
1837 vt->utf_count = 3;
1838 vt->utf_char = (c & 0x07);
1839 } else if ((c & 0xfc) == 0xf8) {
1840 vt->utf_count = 4;
1841 vt->utf_char = (c & 0x03);
1842 } else if ((c & 0xfe) == 0xfc) {
1843 vt->utf_count = 5;
1844 vt->utf_char = (c & 0x01);
1845 } else
1846 vt->utf_count = 0;
1847
1848 continue;
1849 }
1850 } else {
1851 // utf8 single byte
1852 tc = c;
1853 vt->utf_count = 0;
1854 }
1855
1856 } else {
1857 // never translate controls
1858 if (c >= 32 && c != 127 && c != (128+27)) {
1859 tc = translations[vt->cur_enc][c & 0x0ff];
1860 } else {
1861 tc = c;
1862 }
1863 }
1864
1865 vncterm_putchar (vt, tc);
1866 }
1867
1868 vncterm_show_cursor (vt, 1);
1869 return len;
1870 }
1871
1872 void
1873 vncterm_kbd_event (rfbBool down, rfbKeySym keySym, rfbClientPtr cl)
1874 {
1875 vncTerm *vt =(vncTerm *)cl->screen->screenData;
1876 static int control = 0;
1877 static int shift = 0;
1878 char *esc = NULL;
1879
1880 //fprintf (stderr, "KEYEVENT:%d: %08x\n", down == 0, keySym);fflush (stderr);
1881 if (down) {
1882 //fprintf (stderr, "KEYPRESS: %d\n", keySym);fflush (stderr);
1883
1884 if (keySym == XK_Shift_L || keySym == XK_Shift_R) {
1885 shift = 1;
1886 } if (keySym == XK_Control_L || keySym == XK_Control_R) {
1887 control = 1;
1888 } else if (vt->ibuf_count < (IBUFSIZE - 32)) {
1889
1890 if (control) {
1891 if(keySym >= 'a' && keySym <= 'z')
1892 keySym -= 'a' -1;
1893 else if (keySym >= 'A' && keySym <= 'Z')
1894 keySym -= 'A'-1;
1895 else
1896 keySym=0xffff;
1897 } else {
1898 switch (keySym) {
1899 case XK_Escape:
1900 keySym=27; break;
1901 case XK_Return:
1902 keySym='\r'; break;
1903 case XK_BackSpace:
1904 keySym=8; break;
1905 case XK_Tab:
1906 keySym='\t'; break;
1907 case XK_Delete: /* kdch1 */
1908 case XK_KP_Delete:
1909 esc = "[3~";break;
1910 case XK_Home: /* khome */
1911 case XK_KP_Home:
1912 esc = "OH";break;
1913 case XK_End:
1914 case XK_KP_End: /* kend */
1915 esc = "OF";break;
1916 case XK_Insert: /* kich1 */
1917 case XK_KP_Insert:
1918 esc = "[2~";break;
1919 case XK_Up:
1920 case XK_KP_Up: /* kcuu1 */
1921 esc = "OA";break;
1922 case XK_Down: /* kcud1 */
1923 case XK_KP_Down:
1924 esc = "OB";break;
1925 case XK_Right:
1926 case XK_KP_Right: /* kcuf1 */
1927 esc = "OC";break;
1928 case XK_Left:
1929 case XK_KP_Left: /* kcub1 */
1930 esc = "OD";break;
1931 case XK_Page_Up:
1932 if (shift) {
1933 vncterm_virtual_scroll (vt, -vt->height/2);
1934 return;
1935 }
1936 esc = "[5~";break;
1937 case XK_Page_Down:
1938 if (shift) {
1939 vncterm_virtual_scroll (vt, vt->height/2);
1940 return;
1941 }
1942 esc = "[6~";break;
1943 case XK_F1:
1944 esc = "OP";break;
1945 case XK_F2:
1946 esc = "OQ";break;
1947 case XK_F3:
1948 esc = "OR";break;
1949 case XK_F4:
1950 esc = "OS";break;
1951 case XK_F5:
1952 esc = "[15~";break;
1953 case XK_F6:
1954 esc = "[17~";break;
1955 case XK_F7:
1956 esc = "[18~";break;
1957 case XK_F8:
1958 esc = "[19~";break;
1959 case XK_F9:
1960 esc = "[20~";break;
1961 case XK_F10:
1962 esc = "[21~";break;
1963 case XK_F11:
1964 esc = "[23~";break;
1965 case XK_F12:
1966 esc = "[24~";break;
1967 default:
1968 break;
1969 }
1970 }
1971
1972 #ifdef DEBUG
1973 fprintf (stderr, "KEYPRESS OUT:%s: %d\n", esc, keySym); fflush (stderr);
1974 #endif
1975
1976 if (vt->y_displ != vt->y_base) {
1977 vt->y_displ = vt->y_base;
1978 vncterm_refresh (vt);
1979 }
1980
1981 if (esc) {
1982 vncterm_respond_esc (vt, esc);
1983 } else if(keySym<0x100) {
1984 if (vt->utf8) {
1985 int len = ucs2_to_utf8 (keySym & 0x0fff, &vt->ibuf[vt->ibuf_count]);
1986 vt->ibuf_count += len;
1987 } else {
1988 vt->ibuf[vt->ibuf_count++] = (char)keySym;
1989 }
1990 }
1991 }
1992 } else {
1993 if (keySym == XK_Shift_L || keySym == XK_Shift_R) {
1994 shift = 0;
1995 } else if (keySym == XK_Control_L || keySym == XK_Control_R) {
1996 control = 0;
1997 }
1998 }
1999 }
2000
2001 void
2002 vncterm_set_xcut_text (char* str, int len, struct _rfbClientRec* cl)
2003 {
2004 vncTerm *vt =(vncTerm *)cl->screen->screenData;
2005
2006 // seems str is Latin-1 encoded
2007 if (vt->selection) free (vt->selection);
2008 vt->selection = (unicode *)malloc (len*sizeof (unicode));
2009 int i;
2010 for (i = 0; i < len; i++) {
2011 vt->selection[i] = str[i] & 0xff;
2012 }
2013 vt->selection_len = len;
2014 }
2015
2016 static void
2017 mouse_report (vncTerm *vt, int butt, int mrx, int mry)
2018 {
2019 char buf[8];
2020
2021 sprintf (buf, "[M%c%c%c", (char)(' ' + butt), (char)('!' + mrx),
2022 (char)('!' + mry));
2023
2024 vncterm_respond_esc (vt, buf);
2025 }
2026
2027 void
2028 vncterm_toggle_marked_cell (vncTerm *vt, int pos)
2029 {
2030 int x= (pos%vt->width)*8;
2031 int y= (pos/vt->width)*16;
2032
2033 int i,j;
2034 rfbScreenInfoPtr s=vt->screen;
2035
2036 char *b = s->frameBuffer+y*s->width+x;
2037
2038 for (j=0; j < 16; j++) {
2039 for(i=0; i < 8; i++) {
2040 b[j*s->width+i] ^= 0x0f;
2041 rfbMarkRectAsModified (s, x, y, x+8, y+16);
2042 }
2043 }
2044 }
2045
2046 void
2047 vncterm_pointer_event (int buttonMask, int x, int y, rfbClientPtr cl)
2048 {
2049 vncTerm *vt =(vncTerm *)cl->screen->screenData;
2050 static int button2_released = 1;
2051 static int last_mask = 0;
2052 static int sel_start_pos = 0;
2053 static int sel_end_pos = 0;
2054 int i;
2055
2056 int cx = x/8;
2057 int cy = y/16;
2058
2059 if (cx < 0) cx = 0;
2060 if (cx >= vt->width) cx = vt->width - 1;
2061 if (cy < 0) cy = 0;
2062 if (cy >= vt->height) cy = vt->height - 1;
2063
2064 if (vt->report_mouse && buttonMask != last_mask) {
2065 last_mask = buttonMask;
2066 if (buttonMask & 1) {
2067 mouse_report (vt, 0, cx, cy);
2068 }
2069 if (buttonMask & 2) {
2070 mouse_report (vt, 1, cx, cy);
2071 }
2072 if (buttonMask & 4) {
2073 mouse_report (vt, 2, cx, cy);
2074 }
2075 if (!buttonMask) {
2076 mouse_report (vt, 3, cx, cy);
2077 }
2078 }
2079
2080 if (buttonMask & 2) {
2081 if(button2_released && vt->selection) {
2082 int i;
2083 for(i = 0; i < vt->selection_len; i++) {
2084 if (vt->ibuf_count < IBUFSIZE - 6) { // uft8 is max 6 characters wide
2085 if (vt->utf8) {
2086 vt->ibuf_count += ucs2_to_utf8 (vt->selection[i], &vt->ibuf[vt->ibuf_count]);
2087 } else {
2088 vt->ibuf[vt->ibuf_count++] = vt->selection[i];
2089 }
2090 }
2091 }
2092 if (vt->y_displ != vt->y_base) {
2093 vt->y_displ = vt->y_base;
2094 vncterm_refresh (vt);
2095 }
2096 }
2097 button2_released = 0;
2098 } else {
2099 button2_released = 1;
2100 }
2101
2102 if (buttonMask & 1) {
2103 int pos = cy*vt->width + cx;
2104
2105 // code borrowed from libvncserver (VNConsole.c)
2106
2107 if (!vt->mark_active) {
2108
2109 vt->mark_active = 1;
2110 sel_start_pos = sel_end_pos = pos;
2111 vncterm_toggle_marked_cell (vt, pos);
2112
2113 } else {
2114
2115 if (pos != sel_end_pos) {
2116
2117 if (pos > sel_end_pos) {
2118 cx = sel_end_pos; cy=pos;
2119 } else {
2120 cx=pos; cy=sel_end_pos;
2121 }
2122
2123 if (cx < sel_start_pos) {
2124 if (cy < sel_start_pos) cy--;
2125 } else {
2126 cx++;
2127 }
2128
2129 while (cx <= cy) {
2130 vncterm_toggle_marked_cell (vt, cx);
2131 cx++;
2132 }
2133
2134 sel_end_pos = pos;
2135 }
2136 }
2137
2138 } else if (vt->mark_active) {
2139 vt->mark_active = 0;
2140
2141 if (sel_start_pos > sel_end_pos) {
2142 int tmp = sel_start_pos - 1;
2143 sel_start_pos = sel_end_pos;
2144 sel_end_pos = tmp;
2145 }
2146
2147 int len = sel_end_pos - sel_start_pos + 1;
2148
2149 if (vt->selection) free (vt->selection);
2150 vt->selection = (unicode *)malloc (len*sizeof (unicode));
2151 vt->selection_len = len;
2152 char *sel_latin1 = (char *)malloc (len + 1);
2153
2154 for (i = 0; i < len; i++) {
2155 int pos = sel_start_pos + i;
2156 int x = pos % vt->width;
2157 int y1 = ((pos / vt->width) + vt->y_displ) % vt->total_height;
2158 TextCell *c = &vt->cells[y1*vt->width + x];
2159 vt->selection[i] = c->ch;
2160 sel_latin1[i] = (char)c->ch;
2161 c++;
2162 }
2163 sel_latin1[len] = 0;
2164 rfbGotXCutText (vt->screen, sel_latin1, len);
2165 free (sel_latin1);
2166
2167 while (sel_start_pos <= sel_end_pos) {
2168 vncterm_toggle_marked_cell (vt, sel_start_pos++);
2169 }
2170
2171 }
2172
2173 rfbDefaultPtrAddEvent (buttonMask, x, y, cl);
2174 }
2175
2176 static int client_count = 0;
2177 static int client_connected = 0;
2178 static int last_client = 1;
2179 static time_t last_time = 0;
2180
2181 void
2182 client_gone (rfbClientPtr client)
2183 {
2184 client_count--;
2185
2186 last_time = time (NULL);
2187
2188 if (client_count <= 0) {
2189 last_client = 1;
2190 }
2191 }
2192
2193 /* libvncserver callback for when a new client connects */
2194 enum rfbNewClientAction
2195 new_client (rfbClientPtr client)
2196 {
2197 client->clientGoneHook = client_gone;
2198 client_count++;
2199
2200 last_time = time (NULL);
2201
2202 last_client = 0;
2203 client_connected = 1;
2204
2205 return RFB_CLIENT_ACCEPT;
2206 }
2207
2208 static char *vncticket = NULL;
2209
2210 vncTerm *
2211 create_vncterm (int argc, char** argv, int maxx, int maxy)
2212 {
2213 int i;
2214
2215 rfbScreenInfoPtr screen = rfbGetScreen (&argc, argv, maxx, maxy, 8, 1, 1);
2216 screen->frameBuffer=(char*)calloc(maxx*maxy, 1);
2217
2218 char **passwds = calloc(sizeof(char**), 2);
2219
2220 vncTerm *vt = (vncTerm *)calloc (sizeof(vncTerm), 1);
2221
2222 rfbColourMap *cmap =&screen->colourMap;
2223 cmap->data.bytes = malloc (16*3);
2224 for(i=0;i<16;i++) {
2225 cmap->data.bytes[i*3 + 0] = default_red[color_table[i]];
2226 cmap->data.bytes[i*3 + 1] = default_grn[color_table[i]];
2227 cmap->data.bytes[i*3 + 2] = default_blu[color_table[i]];
2228 }
2229 cmap->count = 16;
2230 cmap->is16 = FALSE;
2231 screen->serverFormat.trueColour = FALSE;
2232
2233 screen->kbdAddEvent = vncterm_kbd_event;
2234
2235 screen->setXCutText = vncterm_set_xcut_text;
2236
2237 screen->ptrAddEvent = vncterm_pointer_event;
2238
2239 screen->desktopName = "VNC Command Terminal";
2240
2241 screen->newClientHook = new_client;
2242
2243 vt->maxx = screen->width;
2244 vt->maxy = screen->height;
2245
2246 vt->width = vt->maxx / 8;
2247 vt->height = vt->maxy / 16;
2248
2249 vt->total_height = vt->height * 20;
2250 vt->scroll_height = 0;
2251 vt->y_base = 0;
2252 vt->y_displ = 0;
2253
2254 vt->region_top = 0;
2255 vt->region_bottom = vt->height;
2256
2257 vt->g0enc = LAT1_MAP;
2258 vt->g1enc = GRAF_MAP;
2259 vt->cur_enc = vt->g0enc;
2260 vt->charset = 0;
2261
2262 /* default text attributes */
2263 vt->default_attrib.bold = 0;
2264 vt->default_attrib.uline = 0;
2265 vt->default_attrib.blink = 0;
2266 vt->default_attrib.invers = 0;
2267 vt->default_attrib.unvisible = 0;
2268 vt->default_attrib.fgcol = 7;
2269 vt->default_attrib.bgcol = 0;
2270
2271 vt->cur_attrib = vt->default_attrib;
2272
2273 vt->cells = (TextCell *)calloc (sizeof (TextCell), vt->width*vt->total_height);
2274
2275 for (i = 0; i < vt->width*vt->total_height; i++) {
2276 vt->cells[i].ch = ' ';
2277 vt->cells[i].attrib = vt->default_attrib;
2278 }
2279
2280 vt->altcells = (TextCell *)calloc (sizeof (TextCell), vt->width*vt->height);
2281
2282 vt->screen = screen;
2283
2284 screen->screenData = (void*)vt;
2285
2286 //screen->autoPort = 1;
2287
2288 if (vncticket) {
2289 passwds[0] = vncticket;
2290 passwds[1] = NULL;
2291
2292 screen->authPasswdData = (void *)passwds;
2293 screen->passwordCheck = rfbCheckPasswordByList;
2294 } else {
2295 rfbRegisterSecurityHandler(&VncSecurityHandlerVencrypt);
2296 }
2297
2298 rfbInitServer(screen);
2299
2300 return vt;
2301 }
2302
2303 int
2304 main (int argc, char** argv)
2305 {
2306 int i;
2307 char **cmdargv = NULL;
2308 char *command = "/bin/bash"; // execute normal shell as default
2309 int pid;
2310 int master;
2311 char ptyname[1024];
2312 fd_set fs, fs1;
2313 struct timeval tv, tv1;
2314 time_t elapsed, cur_time;
2315 struct winsize dimensions;
2316
2317 if (gnutls_global_init () < 0) {
2318 fprintf(stderr, "gnutls_global_init failed\n");
2319 exit(-1);
2320 }
2321
2322 if (gnutls_dh_params_init (&dh_params) < 0) {
2323 fprintf(stderr, "gnutls_dh_params_init failed\n");
2324 exit(-1);
2325 }
2326
2327 if (gnutls_dh_params_generate2 (dh_params, DH_BITS) < 0) {
2328 fprintf(stderr, "gnutls_dh_params_init failed\n");
2329 exit(-1);
2330 }
2331
2332 for (i = 1; i < argc; i++) {
2333 if (!strcmp (argv[i], "-c")) {
2334 command = argv[i+1];
2335 cmdargv = &argv[i+1];
2336 argc = i;
2337 argv[i] = NULL;
2338 break;
2339 }
2340 }
2341
2342 for (i = 1; i < argc; i++) {
2343 if (!strcmp (argv[i], "-timeout")) {
2344 CHECK_ARGC (argc, argv, i);
2345 idle_timeout = atoi(argv[i+1]);
2346 rfbPurgeArguments(&argc, &i, 2, argv); i--;
2347 } else if (!strcmp (argv[i], "-authpath")) {
2348 CHECK_ARGC (argc, argv, i);
2349 auth_path = argv[i+1];
2350 rfbPurgeArguments(&argc, &i, 2, argv); i--;
2351 } else if (!strcmp (argv[i], "-perm")) {
2352 CHECK_ARGC (argc, argv, i);
2353 auth_perm = argv[i+1];
2354 rfbPurgeArguments(&argc, &i, 2, argv); i--;
2355 } else if (!strcmp (argv[i], "-notls")) {
2356 rfbPurgeArguments(&argc, &i, 1, argv); i--;
2357 if ((vncticket = getenv("PVE_VNC_TICKET")) == NULL) {
2358 fprintf(stderr, "missing env PVE_VNC_TICKET (-notls)\n");
2359 exit(-1);
2360 }
2361 }
2362 }
2363
2364 unsetenv("PVE_VNC_TICKET"); // do not expose this to child
2365
2366 #ifdef DEBUG
2367 rfbLogEnable (1);
2368 gnutls_global_set_log_level(10);
2369 gnutls_global_set_log_function(vnc_debug_gnutls_log);
2370 #else
2371 rfbLogEnable (0);
2372 #endif
2373
2374 vncTerm *vt = create_vncterm (argc, argv, 745, 400);
2375
2376 setlocale(LC_ALL, ""); // set from environment
2377
2378 char *ctype = setlocale (LC_CTYPE, NULL); // query LC_CTYPE
2379
2380 // fixme: ist there a standard way to detect utf8 mode ?
2381 if (strcasestr (ctype, ".utf-8")||strcasestr (ctype, ".utf8")) {
2382 vt->utf8 = 1;
2383 }
2384
2385 dimensions.ws_col = vt->width;
2386 dimensions.ws_row = vt->height;
2387
2388 setenv ("TERM", TERM, 1);
2389
2390 pid = forkpty (&master, ptyname, NULL, &dimensions);
2391 if(!pid) {
2392
2393 // install default signal handlers
2394 signal (SIGQUIT, SIG_DFL);
2395 signal (SIGTERM, SIG_DFL);
2396 signal (SIGINT, SIG_DFL);
2397
2398 if (cmdargv) {
2399 execvp (command, cmdargv);
2400 } else {
2401 execlp (command, command, NULL);
2402 }
2403 perror ("Error: exec failed\n");
2404 exit (-1); // should not be reached
2405 } else if (pid == -1) {
2406 perror ("Error: fork failed\n");
2407 exit (-1);
2408 }
2409
2410 FD_ZERO (&fs);
2411 FD_SET (master, &fs);
2412 tv.tv_sec = 0;
2413 tv.tv_usec = 5000; /* 5 ms */
2414
2415 last_time = time (NULL);
2416
2417 int count = 0;
2418 while (1) {
2419 count ++;
2420 tv1 = tv;
2421 fs1 = fs;
2422
2423 cur_time = time (NULL);
2424
2425 elapsed = cur_time - last_time;
2426 //printf ("Elapsed %ld\n", elapsed);
2427
2428 if (last_client) {
2429 if (client_connected) {
2430 if (idle_timeout && (elapsed >= idle_timeout)) {
2431 break;
2432 }
2433 } else {
2434 // wait at least 20 seconds for initial connect
2435 if (idle_timeout && (elapsed >= (idle_timeout > 20 ? idle_timeout : 20))) {
2436 break;
2437 }
2438 }
2439 } else {
2440 // exit after 30 minutes idle time
2441 if (elapsed >= 30*60) {
2442 break;
2443 }
2444 }
2445
2446 rfbProcessEvents (vt->screen, 40000); /* 40 ms */
2447
2448 if (vt->ibuf_count > 0) {
2449 //printf ("DEBUG: WRITE %d %d\n", vt->ibuf[0], vt->ibuf_count);
2450 write (master, vt->ibuf, vt->ibuf_count);
2451 vt->ibuf_count = 0;
2452 last_time = time (NULL);
2453 }
2454
2455 if (!vt->mark_active) {
2456
2457 int num_fds = select (master+1, &fs1, NULL, NULL, &tv1);
2458 if (num_fds >= 0) {
2459 if (FD_ISSET (master, &fs1)) {
2460 char buffer[1024];
2461 int c;
2462 while ((c = read (master, buffer, 1024)) == -1) {
2463 if (errno != EAGAIN) break;
2464 }
2465 if (c == -1) break;
2466 vncterm_puts (vt, buffer, c);
2467 }
2468 } else {
2469 break;
2470 }
2471 }
2472 }
2473
2474 kill (pid, 9);
2475 int status;
2476 waitpid(pid, &status, 0);
2477
2478 exit (0);
2479 }