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