bound check utf8 characters
[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 }