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