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