add width and height parameters
[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 }