Diff for /qemu/vnc.c between versions 1.1.1.4 and 1.1.1.5

version 1.1.1.4, 2018/04/24 16:47:31 version 1.1.1.5, 2018/04/24 16:50:51
Line 28 Line 28
 #include "sysemu.h"  #include "sysemu.h"
 #include "qemu_socket.h"  #include "qemu_socket.h"
 #include "qemu-timer.h"  #include "qemu-timer.h"
   #include "audio/audio.h"
   #include <zlib.h>
   
 #define VNC_REFRESH_INTERVAL (1000 / 30)  #define VNC_REFRESH_INTERVAL (1000 / 30)
   
   #include "vnc.h"
 #include "vnc_keysym.h"  #include "vnc_keysym.h"
 #include "keymaps.c"  #include "keymaps.c"
 #include "d3des.h"  #include "d3des.h"
   
 #if CONFIG_VNC_TLS  #ifdef CONFIG_VNC_TLS
 #include <gnutls/gnutls.h>  #include <gnutls/gnutls.h>
 #include <gnutls/x509.h>  #include <gnutls/x509.h>
 #endif /* CONFIG_VNC_TLS */  #endif /* CONFIG_VNC_TLS */
   
 // #define _VNC_DEBUG 1  // #define _VNC_DEBUG 1
   
 #if _VNC_DEBUG  #ifdef _VNC_DEBUG
 #define VNC_DEBUG(fmt, ...) do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)  #define VNC_DEBUG(fmt, ...) do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
   
 #if CONFIG_VNC_TLS && _VNC_DEBUG >= 2  #if defined(CONFIG_VNC_TLS) && _VNC_DEBUG >= 2
 /* Very verbose, so only enabled for _VNC_DEBUG >= 2 */  /* Very verbose, so only enabled for _VNC_DEBUG >= 2 */
 static void vnc_debug_gnutls_log(int level, const char* str) {  static void vnc_debug_gnutls_log(int level, const char* str) {
     VNC_DEBUG("%d %s", level, str);      VNC_DEBUG("%d %s", level, str);
Line 55  static void vnc_debug_gnutls_log(int lev Line 58  static void vnc_debug_gnutls_log(int lev
 #define VNC_DEBUG(fmt, ...) do { } while (0)  #define VNC_DEBUG(fmt, ...) do { } while (0)
 #endif  #endif
   
   #define count_bits(c, v) { \
       for (c = 0; v; v >>= 1) \
       { \
           c += v & 1; \
       } \
   }
   
 typedef struct Buffer  typedef struct Buffer
 {  {
Line 71  typedef void VncWritePixels(VncState *vs Line 80  typedef void VncWritePixels(VncState *vs
   
 typedef void VncSendHextileTile(VncState *vs,  typedef void VncSendHextileTile(VncState *vs,
                                 int x, int y, int w, int h,                                  int x, int y, int w, int h,
                                 uint32_t *last_bg,                                  void *last_bg,
                                 uint32_t *last_fg,                                  void *last_fg,
                                 int *has_bg, int *has_fg);                                  int *has_bg, int *has_fg);
   
 #define VNC_MAX_WIDTH 2048  #define VNC_MAX_WIDTH 2048
Line 81  typedef void VncSendHextileTile(VncState Line 90  typedef void VncSendHextileTile(VncState
   
 #define VNC_AUTH_CHALLENGE_SIZE 16  #define VNC_AUTH_CHALLENGE_SIZE 16
   
 enum {  typedef struct VncDisplay VncDisplay;
     VNC_AUTH_INVALID = 0,  
     VNC_AUTH_NONE = 1,  
     VNC_AUTH_VNC = 2,  
     VNC_AUTH_RA2 = 5,  
     VNC_AUTH_RA2NE = 6,  
     VNC_AUTH_TIGHT = 16,  
     VNC_AUTH_ULTRA = 17,  
     VNC_AUTH_TLS = 18,  
     VNC_AUTH_VENCRYPT = 19  
 };  
   
 #if CONFIG_VNC_TLS  struct VncDisplay
 enum {  {
     VNC_WIREMODE_CLEAR,      int lsock;
     VNC_WIREMODE_TLS,      DisplayState *ds;
 };      VncState *clients;
       kbd_layout_t *kbd_layout;
   
 enum {      char *display;
     VNC_AUTH_VENCRYPT_PLAIN = 256,      char *password;
     VNC_AUTH_VENCRYPT_TLSNONE = 257,      int auth;
     VNC_AUTH_VENCRYPT_TLSVNC = 258,  #ifdef CONFIG_VNC_TLS
     VNC_AUTH_VENCRYPT_TLSPLAIN = 259,      int subauth;
     VNC_AUTH_VENCRYPT_X509NONE = 260,      int x509verify;
     VNC_AUTH_VENCRYPT_X509VNC = 261,  
     VNC_AUTH_VENCRYPT_X509PLAIN = 262,  
 };  
   
 #if CONFIG_VNC_TLS      char *x509cacert;
 #define X509_CA_CERT_FILE "ca-cert.pem"      char *x509cacrl;
 #define X509_CA_CRL_FILE "ca-crl.pem"      char *x509cert;
 #define X509_SERVER_KEY_FILE "server-key.pem"      char *x509key;
 #define X509_SERVER_CERT_FILE "server-cert.pem"  
 #endif  #endif
   };
 #endif /* CONFIG_VNC_TLS */  
   
 struct VncState  struct VncState
 {  {
     QEMUTimer *timer;      QEMUTimer *timer;
     int lsock;  
     int csock;      int csock;
     DisplayState *ds;      DisplayState *ds;
       VncDisplay *vd;
     int need_update;      int need_update;
     int width;  
     int height;  
     uint32_t dirty_row[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS];      uint32_t dirty_row[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS];
     char *old_data;      char *old_data;
     int depth; /* internal VNC frame buffer byte per pixel */      uint32_t features;
     int has_resize;  
     int has_hextile;  
     int has_pointer_type_change;  
     int absolute;      int absolute;
     int last_x;      int last_x;
     int last_y;      int last_y;
   
       uint32_t vnc_encoding;
       uint8_t tight_quality;
       uint8_t tight_compression;
   
     int major;      int major;
     int minor;      int minor;
   
     char *display;  
     char *password;  
     int auth;  
 #if CONFIG_VNC_TLS  
     int subauth;  
     int x509verify;  
   
     char *x509cacert;  
     char *x509cacrl;  
     char *x509cert;  
     char *x509key;  
 #endif  
     char challenge[VNC_AUTH_CHALLENGE_SIZE];      char challenge[VNC_AUTH_CHALLENGE_SIZE];
   
 #if CONFIG_VNC_TLS  #ifdef CONFIG_VNC_TLS
     int wiremode;      int wiremode;
     gnutls_session_t tls_session;      gnutls_session_t tls_session;
 #endif  #endif
   
     Buffer output;      Buffer output;
     Buffer input;      Buffer input;
     kbd_layout_t *kbd_layout;  
     /* current output mode information */      /* current output mode information */
     VncWritePixels *write_pixels;      VncWritePixels *write_pixels;
     VncSendHextileTile *send_hextile_tile;      VncSendHextileTile *send_hextile_tile;
     int pix_bpp, pix_big_endian;      DisplaySurface clientds, serverds;
     int red_shift, red_max, red_shift1;  
     int green_shift, green_max, green_shift1;      CaptureVoiceOut *audio_cap;
     int blue_shift, blue_max, blue_shift1;      struct audsettings as;
   
     VncReadEvent *read_handler;      VncReadEvent *read_handler;
     size_t read_handler_expect;      size_t read_handler_expect;
     /* input */      /* input */
     uint8_t modifiers_state[256];      uint8_t modifiers_state[256];
   
       Buffer zlib;
       Buffer zlib_tmp;
       z_stream zlib_stream[4];
   
       VncState *next;
 };  };
   
 static VncState *vnc_state; /* needed for info vnc */  static VncDisplay *vnc_display; /* needed for info vnc */
   static DisplayChangeListener *dcl;
   
 void do_info_vnc(void)  void do_info_vnc(void)
 {  {
     if (vnc_state == NULL)      if (vnc_display == NULL || vnc_display->display == NULL)
         term_printf("VNC server disabled\n");          term_printf("VNC server disabled\n");
     else {      else {
         term_printf("VNC server active on: ");          term_printf("VNC server active on: ");
         term_print_filename(vnc_state->display);          term_print_filename(vnc_display->display);
         term_printf("\n");          term_printf("\n");
   
         if (vnc_state->csock == -1)          if (vnc_display->clients == NULL)
             term_printf("No client connected\n");              term_printf("No client connected\n");
         else          else
             term_printf("Client connected\n");              term_printf("Client connected\n");
     }      }
 }  }
   
   static inline uint32_t vnc_has_feature(VncState *vs, int feature) {
       return (vs->features & (1 << feature));
   }
   
 /* TODO  /* TODO
    1) Get the queue working for IO.     1) Get the queue working for IO.
    2) there is some weirdness when using the -S option (the screen is grey     2) there is some weirdness when using the -S option (the screen is grey
Line 210  static void vnc_flush(VncState *vs); Line 202  static void vnc_flush(VncState *vs);
 static void vnc_update_client(void *opaque);  static void vnc_update_client(void *opaque);
 static void vnc_client_read(void *opaque);  static void vnc_client_read(void *opaque);
   
   static void vnc_colordepth(VncState *vs);
   
 static inline void vnc_set_bit(uint32_t *d, int k)  static inline void vnc_set_bit(uint32_t *d, int k)
 {  {
     d[k >> 5] |= 1 << (k & 0x1f);      d[k >> 5] |= 1 << (k & 0x1f);
Line 251  static inline int vnc_and_bits(const uin Line 245  static inline int vnc_and_bits(const uin
     return 0;      return 0;
 }  }
   
 static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h)  static void vnc_update(VncState *vs, int x, int y, int w, int h)
 {  {
     VncState *vs = ds->opaque;  
     int i;      int i;
   
     h += y;      h += y;
Line 265  static void vnc_dpy_update(DisplayState  Line 258  static void vnc_dpy_update(DisplayState 
     w += (x % 16);      w += (x % 16);
     x -= (x % 16);      x -= (x % 16);
   
       x = MIN(x, vs->serverds.width);
       y = MIN(y, vs->serverds.height);
       w = MIN(x + w, vs->serverds.width) - x;
       h = MIN(h, vs->serverds.height);
   
     for (; y < h; y++)      for (; y < h; y++)
         for (i = 0; i < w; i += 16)          for (i = 0; i < w; i += 16)
             vnc_set_bit(vs->dirty_row[y], (x + i) / 16);              vnc_set_bit(vs->dirty_row[y], (x + i) / 16);
 }  }
   
   static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h)
   {
       VncDisplay *vd = ds->opaque;
       VncState *vs = vd->clients;
       while (vs != NULL) {
           vnc_update(vs, x, y, w, h);
           vs = vs->next;
       }
   }
   
 static void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,  static void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
                                    int32_t encoding)                                     int32_t encoding)
 {  {
Line 281  static void vnc_framebuffer_update(VncSt Line 289  static void vnc_framebuffer_update(VncSt
     vnc_write_s32(vs, encoding);      vnc_write_s32(vs, encoding);
 }  }
   
 static void vnc_dpy_resize(DisplayState *ds, int w, int h)  static void buffer_reserve(Buffer *buffer, size_t len)
   {
       if ((buffer->capacity - buffer->offset) < len) {
           buffer->capacity += (len + 1024);
           buffer->buffer = qemu_realloc(buffer->buffer, buffer->capacity);
           if (buffer->buffer == NULL) {
               fprintf(stderr, "vnc: out of memory\n");
               exit(1);
           }
       }
   }
   
   static int buffer_empty(Buffer *buffer)
   {
       return buffer->offset == 0;
   }
   
   static uint8_t *buffer_end(Buffer *buffer)
   {
       return buffer->buffer + buffer->offset;
   }
   
   static void buffer_reset(Buffer *buffer)
   {
           buffer->offset = 0;
   }
   
   static void buffer_append(Buffer *buffer, const void *data, size_t len)
 {  {
       memcpy(buffer->buffer + buffer->offset, data, len);
       buffer->offset += len;
   }
   
   static void vnc_resize(VncState *vs)
   {
       DisplayState *ds = vs->ds;
   
     int size_changed;      int size_changed;
     VncState *vs = ds->opaque;  
   
     ds->data = realloc(ds->data, w * h * vs->depth);      vs->old_data = qemu_realloc(vs->old_data, ds_get_linesize(ds) * ds_get_height(ds));
     vs->old_data = realloc(vs->old_data, w * h * vs->depth);  
   
     if (ds->data == NULL || vs->old_data == NULL) {      if (vs->old_data == NULL) {
         fprintf(stderr, "vnc: memory allocation failed\n");          fprintf(stderr, "vnc: memory allocation failed\n");
         exit(1);          exit(1);
     }      }
   
     if (ds->depth != vs->depth * 8) {      if (ds_get_bytes_per_pixel(ds) != vs->serverds.pf.bytes_per_pixel)
         ds->depth = vs->depth * 8;  
         console_color_init(ds);          console_color_init(ds);
       vnc_colordepth(vs);
       size_changed = ds_get_width(ds) != vs->serverds.width ||
                      ds_get_height(ds) != vs->serverds.height;
       vs->serverds = *(ds->surface);
       if (size_changed) {
           if (vs->csock != -1 && vnc_has_feature(vs, VNC_FEATURE_RESIZE)) {
               vnc_write_u8(vs, 0);  /* msg id */
               vnc_write_u8(vs, 0);
               vnc_write_u16(vs, 1); /* number of rects */
               vnc_framebuffer_update(vs, 0, 0, ds_get_width(ds), ds_get_height(ds),
                                      VNC_ENCODING_DESKTOPRESIZE);
               vnc_flush(vs);
           }
     }      }
     size_changed = ds->width != w || ds->height != h;  
     ds->width = w;      memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row));
     ds->height = h;      memset(vs->old_data, 42, ds_get_linesize(vs->ds) * ds_get_height(vs->ds));
     ds->linesize = w * vs->depth;  }
     if (vs->csock != -1 && vs->has_resize && size_changed) {  
         vnc_write_u8(vs, 0);  /* msg id */  static void vnc_dpy_resize(DisplayState *ds)
         vnc_write_u8(vs, 0);  {
         vnc_write_u16(vs, 1); /* number of rects */      VncDisplay *vd = ds->opaque;
         vnc_framebuffer_update(vs, 0, 0, ds->width, ds->height, -223);      VncState *vs = vd->clients;
         vnc_flush(vs);      while (vs != NULL) {
         vs->width = ds->width;          vnc_resize(vs);
         vs->height = ds->height;          vs = vs->next;
     }      }
 }  }
   
Line 322  static void vnc_write_pixels_copy(VncSta Line 375  static void vnc_write_pixels_copy(VncSta
 /* slowest but generic code. */  /* slowest but generic code. */
 static void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v)  static void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v)
 {  {
     unsigned int r, g, b;      uint8_t r, g, b;
   
     r = (v >> vs->red_shift1) & vs->red_max;      r = ((((v & vs->serverds.pf.rmask) >> vs->serverds.pf.rshift) << vs->clientds.pf.rbits) >>
     g = (v >> vs->green_shift1) & vs->green_max;          vs->serverds.pf.rbits);
     b = (v >> vs->blue_shift1) & vs->blue_max;      g = ((((v & vs->serverds.pf.gmask) >> vs->serverds.pf.gshift) << vs->clientds.pf.gbits) >>
     v = (r << vs->red_shift) |          vs->serverds.pf.gbits);
         (g << vs->green_shift) |      b = ((((v & vs->serverds.pf.bmask) >> vs->serverds.pf.bshift) << vs->clientds.pf.bbits) >>
         (b << vs->blue_shift);          vs->serverds.pf.bbits);
     switch(vs->pix_bpp) {      v = (r << vs->clientds.pf.rshift) |
           (g << vs->clientds.pf.gshift) |
           (b << vs->clientds.pf.bshift);
       switch(vs->clientds.pf.bytes_per_pixel) {
     case 1:      case 1:
         buf[0] = v;          buf[0] = v;
         break;          break;
     case 2:      case 2:
         if (vs->pix_big_endian) {          if (vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) {
             buf[0] = v >> 8;              buf[0] = v >> 8;
             buf[1] = v;              buf[1] = v;
         } else {          } else {
Line 345  static void vnc_convert_pixel(VncState * Line 401  static void vnc_convert_pixel(VncState *
         break;          break;
     default:      default:
     case 4:      case 4:
         if (vs->pix_big_endian) {          if (vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) {
             buf[0] = v >> 24;              buf[0] = v >> 24;
             buf[1] = v >> 16;              buf[1] = v >> 16;
             buf[2] = v >> 8;              buf[2] = v >> 8;
Line 362  static void vnc_convert_pixel(VncState * Line 418  static void vnc_convert_pixel(VncState *
   
 static void vnc_write_pixels_generic(VncState *vs, void *pixels1, int size)  static void vnc_write_pixels_generic(VncState *vs, void *pixels1, int size)
 {  {
     uint32_t *pixels = pixels1;  
     uint8_t buf[4];      uint8_t buf[4];
     int n, i;  
   
     n = size >> 2;      if (vs->serverds.pf.bytes_per_pixel == 4) {
     for(i = 0; i < n; i++) {          uint32_t *pixels = pixels1;
         vnc_convert_pixel(vs, buf, pixels[i]);          int n, i;
         vnc_write(vs, buf, vs->pix_bpp);          n = size >> 2;
           for(i = 0; i < n; i++) {
               vnc_convert_pixel(vs, buf, pixels[i]);
               vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel);
           }
       } else if (vs->serverds.pf.bytes_per_pixel == 2) {
           uint16_t *pixels = pixels1;
           int n, i;
           n = size >> 1;
           for(i = 0; i < n; i++) {
               vnc_convert_pixel(vs, buf, pixels[i]);
               vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel);
           }
       } else if (vs->serverds.pf.bytes_per_pixel == 1) {
           uint8_t *pixels = pixels1;
           int n, i;
           n = size;
           for(i = 0; i < n; i++) {
               vnc_convert_pixel(vs, buf, pixels[i]);
               vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel);
           }
       } else {
           fprintf(stderr, "vnc_write_pixels_generic: VncState color depth not supported\n");
     }      }
 }  }
   
Line 378  static void send_framebuffer_update_raw( Line 454  static void send_framebuffer_update_raw(
     int i;      int i;
     uint8_t *row;      uint8_t *row;
   
     vnc_framebuffer_update(vs, x, y, w, h, 0);      row = ds_get_data(vs->ds) + y * ds_get_linesize(vs->ds) + x * ds_get_bytes_per_pixel(vs->ds);
   
     row = vs->ds->data + y * vs->ds->linesize + x * vs->depth;  
     for (i = 0; i < h; i++) {      for (i = 0; i < h; i++) {
         vs->write_pixels(vs, row, w * vs->depth);          vs->write_pixels(vs, row, w * ds_get_bytes_per_pixel(vs->ds));
         row += vs->ds->linesize;          row += ds_get_linesize(vs->ds);
     }      }
 }  }
   
Line 406  static void hextile_enc_cord(uint8_t *pt Line 480  static void hextile_enc_cord(uint8_t *pt
 #undef BPP  #undef BPP
   
 #define GENERIC  #define GENERIC
   #define BPP 8
   #include "vnchextile.h"
   #undef BPP
   #undef GENERIC
   
   #define GENERIC
   #define BPP 16
   #include "vnchextile.h"
   #undef BPP
   #undef GENERIC
   
   #define GENERIC
 #define BPP 32  #define BPP 32
 #include "vnchextile.h"  #include "vnchextile.h"
 #undef BPP  #undef BPP
Line 415  static void send_framebuffer_update_hext Line 501  static void send_framebuffer_update_hext
 {  {
     int i, j;      int i, j;
     int has_fg, has_bg;      int has_fg, has_bg;
     uint32_t last_fg32, last_bg32;      uint8_t *last_fg, *last_bg;
   
     vnc_framebuffer_update(vs, x, y, w, h, 5);  
   
       last_fg = (uint8_t *) qemu_malloc(vs->serverds.pf.bytes_per_pixel);
       last_bg = (uint8_t *) qemu_malloc(vs->serverds.pf.bytes_per_pixel);
     has_fg = has_bg = 0;      has_fg = has_bg = 0;
     for (j = y; j < (y + h); j += 16) {      for (j = y; j < (y + h); j += 16) {
         for (i = x; i < (x + w); i += 16) {          for (i = x; i < (x + w); i += 16) {
             vs->send_hextile_tile(vs, i, j,              vs->send_hextile_tile(vs, i, j,
                                   MIN(16, x + w - i), MIN(16, y + h - j),                                    MIN(16, x + w - i), MIN(16, y + h - j),
                                   &last_bg32, &last_fg32, &has_bg, &has_fg);                                    last_bg, last_fg, &has_bg, &has_fg);
         }          }
     }      }
       free(last_fg);
       free(last_bg);
   
   }
   
   static void vnc_zlib_init(VncState *vs)
   {
       int i;
       for (i=0; i<(sizeof(vs->zlib_stream) / sizeof(z_stream)); i++)
           vs->zlib_stream[i].opaque = NULL;
   }
   
   static void vnc_zlib_start(VncState *vs)
   {
       buffer_reset(&vs->zlib);
   
       // make the output buffer be the zlib buffer, so we can compress it later
       vs->zlib_tmp = vs->output;
       vs->output = vs->zlib;
   }
   
   static int vnc_zlib_stop(VncState *vs, int stream_id)
   {
       z_streamp zstream = &vs->zlib_stream[stream_id];
       int previous_out;
   
       // switch back to normal output/zlib buffers
       vs->zlib = vs->output;
       vs->output = vs->zlib_tmp;
   
       // compress the zlib buffer
   
       // initialize the stream
       // XXX need one stream per session
       if (zstream->opaque != vs) {
           int err;
   
           VNC_DEBUG("VNC: initializing zlib stream %d\n", stream_id);
           VNC_DEBUG("VNC: opaque = %p | vs = %p\n", zstream->opaque, vs);
           zstream->zalloc = Z_NULL;
           zstream->zfree = Z_NULL;
   
           err = deflateInit2(zstream, vs->tight_compression, Z_DEFLATED, MAX_WBITS,
                              MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
   
           if (err != Z_OK) {
               fprintf(stderr, "VNC: error initializing zlib\n");
               return -1;
           }
   
           zstream->opaque = vs;
       }
   
       // XXX what to do if tight_compression changed in between?
   
       // reserve memory in output buffer
       buffer_reserve(&vs->output, vs->zlib.offset + 64);
   
       // set pointers
       zstream->next_in = vs->zlib.buffer;
       zstream->avail_in = vs->zlib.offset;
       zstream->next_out = vs->output.buffer + vs->output.offset;
       zstream->avail_out = vs->output.capacity - vs->output.offset;
       zstream->data_type = Z_BINARY;
       previous_out = zstream->total_out;
   
       // start encoding
       if (deflate(zstream, Z_SYNC_FLUSH) != Z_OK) {
           fprintf(stderr, "VNC: error during zlib compression\n");
           return -1;
       }
   
       vs->output.offset = vs->output.capacity - zstream->avail_out;
       return zstream->total_out - previous_out;
   }
   
   static void send_framebuffer_update_zlib(VncState *vs, int x, int y, int w, int h)
   {
       int old_offset, new_offset, bytes_written;
   
       vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_ZLIB);
   
       // remember where we put in the follow-up size
       old_offset = vs->output.offset;
       vnc_write_s32(vs, 0);
   
       // compress the stream
       vnc_zlib_start(vs);
       send_framebuffer_update_raw(vs, x, y, w, h);
       bytes_written = vnc_zlib_stop(vs, 0);
   
       if (bytes_written == -1)
           return;
   
       // hack in the size
       new_offset = vs->output.offset;
       vs->output.offset = old_offset;
       vnc_write_u32(vs, bytes_written);
       vs->output.offset = new_offset;
 }  }
   
 static void send_framebuffer_update(VncState *vs, int x, int y, int w, int h)  static void send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
 {  {
         if (vs->has_hextile)      switch(vs->vnc_encoding) {
           case VNC_ENCODING_ZLIB:
               send_framebuffer_update_zlib(vs, x, y, w, h);
               break;
           case VNC_ENCODING_HEXTILE:
               vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_HEXTILE);
             send_framebuffer_update_hextile(vs, x, y, w, h);              send_framebuffer_update_hextile(vs, x, y, w, h);
         else              break;
           default:
               vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_RAW);
             send_framebuffer_update_raw(vs, x, y, w, h);              send_framebuffer_update_raw(vs, x, y, w, h);
               break;
       }
 }  }
   
 static void vnc_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_y, int w, int h)  static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
 {  {
     int src, dst;  
     uint8_t *src_row;  
     uint8_t *dst_row;  
     char *old_row;  
     int y = 0;  
     int pitch = ds->linesize;  
     VncState *vs = ds->opaque;  
   
     vnc_update_client(vs);      vnc_update_client(vs);
   
     if (dst_y > src_y) {  
         y = h - 1;  
         pitch = -pitch;  
     }  
   
     src = (ds->linesize * (src_y + y) + vs->depth * src_x);  
     dst = (ds->linesize * (dst_y + y) + vs->depth * dst_x);  
   
     src_row = ds->data + src;  
     dst_row = ds->data + dst;  
     old_row = vs->old_data + dst;  
   
     for (y = 0; y < h; y++) {  
         memmove(old_row, src_row, w * vs->depth);  
         memmove(dst_row, src_row, w * vs->depth);  
         src_row += pitch;  
         dst_row += pitch;  
         old_row += pitch;  
     }  
   
     vnc_write_u8(vs, 0);  /* msg id */      vnc_write_u8(vs, 0);  /* msg id */
     vnc_write_u8(vs, 0);      vnc_write_u8(vs, 0);
     vnc_write_u16(vs, 1); /* number of rects */      vnc_write_u16(vs, 1); /* number of rects */
     vnc_framebuffer_update(vs, dst_x, dst_y, w, h, 1);      vnc_framebuffer_update(vs, dst_x, dst_y, w, h, VNC_ENCODING_COPYRECT);
     vnc_write_u16(vs, src_x);      vnc_write_u16(vs, src_x);
     vnc_write_u16(vs, src_y);      vnc_write_u16(vs, src_y);
     vnc_flush(vs);      vnc_flush(vs);
 }  }
   
   static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
   {
       VncDisplay *vd = ds->opaque;
       VncState *vs = vd->clients;
       while (vs != NULL) {
           if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT))
               vnc_copy(vs, src_x, src_y, dst_x, dst_y, w, h);
           else /* TODO */
               vnc_update(vs, dst_x, dst_y, w, h);
           vs = vs->next;
       }
   }
   
 static int find_dirty_height(VncState *vs, int y, int last_x, int x)  static int find_dirty_height(VncState *vs, int y, int last_x, int x)
 {  {
     int h;      int h;
   
     for (h = 1; h < (vs->height - y); h++) {      for (h = 1; h < (vs->serverds.height - y); h++) {
         int tmp_x;          int tmp_x;
         if (!vnc_get_bit(vs->dirty_row[y + h], last_x))          if (!vnc_get_bit(vs->dirty_row[y + h], last_x))
             break;              break;
Line 496  static int find_dirty_height(VncState *v Line 675  static int find_dirty_height(VncState *v
 static void vnc_update_client(void *opaque)  static void vnc_update_client(void *opaque)
 {  {
     VncState *vs = opaque;      VncState *vs = opaque;
   
     if (vs->need_update && vs->csock != -1) {      if (vs->need_update && vs->csock != -1) {
         int y;          int y;
         uint8_t *row;          uint8_t *row;
Line 506  static void vnc_update_client(void *opaq Line 684  static void vnc_update_client(void *opaq
         int saved_offset;          int saved_offset;
         int has_dirty = 0;          int has_dirty = 0;
   
         vnc_set_bits(width_mask, (vs->width / 16), VNC_DIRTY_WORDS);          vga_hw_update();
   
           vnc_set_bits(width_mask, (ds_get_width(vs->ds) / 16), VNC_DIRTY_WORDS);
   
         /* Walk through the dirty map and eliminate tiles that          /* Walk through the dirty map and eliminate tiles that
            really aren't dirty */             really aren't dirty */
         row = vs->ds->data;          row = ds_get_data(vs->ds);
         old_row = vs->old_data;          old_row = vs->old_data;
   
         for (y = 0; y < vs->height; y++) {          for (y = 0; y < ds_get_height(vs->ds); y++) {
             if (vnc_and_bits(vs->dirty_row[y], width_mask, VNC_DIRTY_WORDS)) {              if (vnc_and_bits(vs->dirty_row[y], width_mask, VNC_DIRTY_WORDS)) {
                 int x;                  int x;
                 uint8_t *ptr;                  uint8_t *ptr;
Line 522  static void vnc_update_client(void *opaq Line 702  static void vnc_update_client(void *opaq
                 ptr = row;                  ptr = row;
                 old_ptr = (char*)old_row;                  old_ptr = (char*)old_row;
   
                 for (x = 0; x < vs->ds->width; x += 16) {                  for (x = 0; x < ds_get_width(vs->ds); x += 16) {
                     if (memcmp(old_ptr, ptr, 16 * vs->depth) == 0) {                      if (memcmp(old_ptr, ptr, 16 * ds_get_bytes_per_pixel(vs->ds)) == 0) {
                         vnc_clear_bit(vs->dirty_row[y], (x / 16));                          vnc_clear_bit(vs->dirty_row[y], (x / 16));
                     } else {                      } else {
                         has_dirty = 1;                          has_dirty = 1;
                         memcpy(old_ptr, ptr, 16 * vs->depth);                          memcpy(old_ptr, ptr, 16 * ds_get_bytes_per_pixel(vs->ds));
                     }                      }
   
                     ptr += 16 * vs->depth;                      ptr += 16 * ds_get_bytes_per_pixel(vs->ds);
                     old_ptr += 16 * vs->depth;                      old_ptr += 16 * ds_get_bytes_per_pixel(vs->ds);
                 }                  }
             }              }
   
             row += vs->ds->linesize;              row += ds_get_linesize(vs->ds);
             old_row += vs->ds->linesize;              old_row += ds_get_linesize(vs->ds);
         }          }
   
         if (!has_dirty) {          if (!has_dirty && !vs->audio_cap) {
             qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL);              qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL);
             return;              return;
         }          }
Line 551  static void vnc_update_client(void *opaq Line 731  static void vnc_update_client(void *opaq
         saved_offset = vs->output.offset;          saved_offset = vs->output.offset;
         vnc_write_u16(vs, 0);          vnc_write_u16(vs, 0);
   
         for (y = 0; y < vs->height; y++) {          for (y = 0; y < vs->serverds.height; y++) {
             int x;              int x;
             int last_x = -1;              int last_x = -1;
             for (x = 0; x < vs->width / 16; x++) {              for (x = 0; x < vs->serverds.width / 16; x++) {
                 if (vnc_get_bit(vs->dirty_row[y], x)) {                  if (vnc_get_bit(vs->dirty_row[y], x)) {
                     if (last_x == -1) {                      if (last_x == -1) {
                         last_x = x;                          last_x = x;
Line 580  static void vnc_update_client(void *opaq Line 760  static void vnc_update_client(void *opaq
         vnc_flush(vs);          vnc_flush(vs);
   
     }      }
     qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL);  
 }  
   
 static void vnc_timer_init(VncState *vs)      if (vs->csock != -1) {
 {          qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL);
     if (vs->timer == NULL) {  
         vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs);  
         qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock));  
     }      }
 }  
   
 static void vnc_dpy_refresh(DisplayState *ds)  
 {  
     VncState *vs = ds->opaque;  
     vnc_timer_init(vs);  
     vga_hw_update();  
 }  }
   
 static int vnc_listen_poll(void *opaque)  /* audio */
   static void audio_capture_notify(void *opaque, audcnotification_e cmd)
 {  {
     VncState *vs = opaque;      VncState *vs = opaque;
     if (vs->csock == -1)  
         return 1;  
     return 0;  
 }  
   
 static void buffer_reserve(Buffer *buffer, size_t len)      switch (cmd) {
 {      case AUD_CNOTIFY_DISABLE:
     if ((buffer->capacity - buffer->offset) < len) {          vnc_write_u8(vs, 255);
         buffer->capacity += (len + 1024);          vnc_write_u8(vs, 1);
         buffer->buffer = realloc(buffer->buffer, buffer->capacity);          vnc_write_u16(vs, 0);
         if (buffer->buffer == NULL) {          vnc_flush(vs);
             fprintf(stderr, "vnc: out of memory\n");          break;
             exit(1);  
         }      case AUD_CNOTIFY_ENABLE:
           vnc_write_u8(vs, 255);
           vnc_write_u8(vs, 1);
           vnc_write_u16(vs, 1);
           vnc_flush(vs);
           break;
     }      }
 }  }
   
 static int buffer_empty(Buffer *buffer)  static void audio_capture_destroy(void *opaque)
 {  {
     return buffer->offset == 0;  
 }  }
   
 static uint8_t *buffer_end(Buffer *buffer)  static void audio_capture(void *opaque, void *buf, int size)
 {  {
     return buffer->buffer + buffer->offset;      VncState *vs = opaque;
   
       vnc_write_u8(vs, 255);
       vnc_write_u8(vs, 1);
       vnc_write_u16(vs, 2);
       vnc_write_u32(vs, size);
       vnc_write(vs, buf, size);
       vnc_flush(vs);
 }  }
   
 static void buffer_reset(Buffer *buffer)  static void audio_add(VncState *vs)
 {  {
         buffer->offset = 0;      struct audio_capture_ops ops;
   
       if (vs->audio_cap) {
           term_printf ("audio already running\n");
           return;
       }
   
       ops.notify = audio_capture_notify;
       ops.destroy = audio_capture_destroy;
       ops.capture = audio_capture;
   
       vs->audio_cap = AUD_add_capture(NULL, &vs->as, &ops, vs);
       if (!vs->audio_cap) {
           term_printf ("Failed to add audio capture\n");
       }
 }  }
   
 static void buffer_append(Buffer *buffer, const void *data, size_t len)  static void audio_del(VncState *vs)
 {  {
     memcpy(buffer->buffer + buffer->offset, data, len);      if (vs->audio_cap) {
     buffer->offset += len;          AUD_del_capture(vs->audio_cap, vs);
           vs->audio_cap = NULL;
       }
 }  }
   
 static int vnc_client_io_error(VncState *vs, int ret, int last_errno)  static int vnc_client_io_error(VncState *vs, int ret, int last_errno)
 {  {
     if (ret == 0 || ret == -1) {      if (ret == 0 || ret == -1) {
         if (ret == -1 && (last_errno == EINTR || last_errno == EAGAIN))          if (ret == -1) {
             return 0;              switch (last_errno) {
                   case EINTR:
                   case EAGAIN:
   #ifdef _WIN32
                   case WSAEWOULDBLOCK:
   #endif
                       return 0;
                   default:
                       break;
               }
           }
   
         VNC_DEBUG("Closing down client sock %d %d\n", ret, ret < 0 ? last_errno : 0);          VNC_DEBUG("Closing down client sock %d %d\n", ret, ret < 0 ? last_errno : 0);
         qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);          qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);
         closesocket(vs->csock);          closesocket(vs->csock);
         vs->csock = -1;          qemu_del_timer(vs->timer);
         buffer_reset(&vs->input);          qemu_free_timer(vs->timer);
         buffer_reset(&vs->output);          if (vs->input.buffer) qemu_free(vs->input.buffer);
         vs->need_update = 0;          if (vs->output.buffer) qemu_free(vs->output.buffer);
 #if CONFIG_VNC_TLS  #ifdef CONFIG_VNC_TLS
         if (vs->tls_session) {          if (vs->tls_session) {
             gnutls_deinit(vs->tls_session);              gnutls_deinit(vs->tls_session);
             vs->tls_session = NULL;              vs->tls_session = NULL;
         }          }
         vs->wiremode = VNC_WIREMODE_CLEAR;  
 #endif /* CONFIG_VNC_TLS */  #endif /* CONFIG_VNC_TLS */
           audio_del(vs);
   
           VncState *p, *parent = NULL;
           for (p = vs->vd->clients; p != NULL; p = p->next) {
               if (p == vs) {
                   if (parent)
                       parent->next = p->next;
                   else
                       vs->vd->clients = p->next;
                   break;
               }
               parent = p;
           }
           if (!vs->vd->clients)
               dcl->idle = 1;
   
           qemu_free(vs->old_data);
           qemu_free(vs);
     
         return 0;          return 0;
     }      }
     return ret;      return ret;
Line 674  static void vnc_client_write(void *opaqu Line 895  static void vnc_client_write(void *opaqu
     long ret;      long ret;
     VncState *vs = opaque;      VncState *vs = opaque;
   
 #if CONFIG_VNC_TLS  #ifdef CONFIG_VNC_TLS
     if (vs->tls_session) {      if (vs->tls_session) {
         ret = gnutls_write(vs->tls_session, vs->output.buffer, vs->output.offset);          ret = gnutls_write(vs->tls_session, vs->output.buffer, vs->output.offset);
         if (ret < 0) {          if (ret < 0) {
Line 712  static void vnc_client_read(void *opaque Line 933  static void vnc_client_read(void *opaque
   
     buffer_reserve(&vs->input, 4096);      buffer_reserve(&vs->input, 4096);
   
 #if CONFIG_VNC_TLS  #ifdef CONFIG_VNC_TLS
     if (vs->tls_session) {      if (vs->tls_session) {
         ret = gnutls_read(vs->tls_session, buffer_end(&vs->input), 4096);          ret = gnutls_read(vs->tls_session, buffer_end(&vs->input), 4096);
         if (ret < 0) {          if (ret < 0) {
Line 819  static uint32_t read_u32(uint8_t *data,  Line 1040  static uint32_t read_u32(uint8_t *data, 
             (data[offset + 2] << 8) | data[offset + 3]);              (data[offset + 2] << 8) | data[offset + 3]);
 }  }
   
 #if CONFIG_VNC_TLS  #ifdef CONFIG_VNC_TLS
 static ssize_t vnc_tls_push(gnutls_transport_ptr_t transport,  static ssize_t vnc_tls_push(gnutls_transport_ptr_t transport,
                             const void *data,                              const void *data,
                             size_t len) {                              size_t len) {
Line 860  static void client_cut_text(VncState *vs Line 1081  static void client_cut_text(VncState *vs
   
 static void check_pointer_type_change(VncState *vs, int absolute)  static void check_pointer_type_change(VncState *vs, int absolute)
 {  {
     if (vs->has_pointer_type_change && vs->absolute != absolute) {      if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE) && vs->absolute != absolute) {
         vnc_write_u8(vs, 0);          vnc_write_u8(vs, 0);
         vnc_write_u8(vs, 0);          vnc_write_u8(vs, 0);
         vnc_write_u16(vs, 1);          vnc_write_u16(vs, 1);
         vnc_framebuffer_update(vs, absolute, 0,          vnc_framebuffer_update(vs, absolute, 0,
                                vs->ds->width, vs->ds->height, -257);                                 ds_get_width(vs->ds), ds_get_height(vs->ds),
                                  VNC_ENCODING_POINTER_TYPE_CHANGE);
         vnc_flush(vs);          vnc_flush(vs);
     }      }
     vs->absolute = absolute;      vs->absolute = absolute;
Line 888  static void pointer_event(VncState *vs,  Line 1110  static void pointer_event(VncState *vs, 
         dz = 1;          dz = 1;
   
     if (vs->absolute) {      if (vs->absolute) {
         kbd_mouse_event(x * 0x7FFF / vs->ds->width,          kbd_mouse_event(x * 0x7FFF / (ds_get_width(vs->ds) - 1),
                         y * 0x7FFF / vs->ds->height,                          y * 0x7FFF / (ds_get_height(vs->ds) - 1),
                         dz, buttons);                          dz, buttons);
     } else if (vs->has_pointer_type_change) {      } else if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE)) {
         x -= 0x7FFF;          x -= 0x7FFF;
         y -= 0x7FFF;          y -= 0x7FFF;
   
Line 923  static void reset_keys(VncState *vs) Line 1145  static void reset_keys(VncState *vs)
   
 static void press_key(VncState *vs, int keysym)  static void press_key(VncState *vs, int keysym)
 {  {
     kbd_put_keycode(keysym2scancode(vs->kbd_layout, keysym) & 0x7f);      kbd_put_keycode(keysym2scancode(vs->vd->kbd_layout, keysym) & 0x7f);
     kbd_put_keycode(keysym2scancode(vs->kbd_layout, keysym) | 0x80);      kbd_put_keycode(keysym2scancode(vs->vd->kbd_layout, keysym) | 0x80);
 }  }
   
 static void do_key_event(VncState *vs, int down, uint32_t sym)  static void do_key_event(VncState *vs, int down, int keycode, int sym)
 {  {
     int keycode;  
   
     keycode = keysym2scancode(vs->kbd_layout, sym & 0xFFFF);  
   
     /* QEMU console switch */      /* QEMU console switch */
     switch(keycode) {      switch(keycode) {
     case 0x2a:                          /* Left Shift */      case 0x2a:                          /* Left Shift */
Line 954  static void do_key_event(VncState *vs, i Line 1172  static void do_key_event(VncState *vs, i
             return;              return;
         }          }
         break;          break;
       case 0x3a:                  /* CapsLock */
     case 0x45:                  /* NumLock */      case 0x45:                  /* NumLock */
         if (!down)          if (!down)
             vs->modifiers_state[keycode] ^= 1;              vs->modifiers_state[keycode] ^= 1;
         break;          break;
     }      }
   
     if (keycode_is_keypad(vs->kbd_layout, keycode)) {      if (keycode_is_keypad(vs->vd->kbd_layout, keycode)) {
         /* If the numlock state needs to change then simulate an additional          /* If the numlock state needs to change then simulate an additional
            keypress before sending this one.  This will happen if the user             keypress before sending this one.  This will happen if the user
            toggles numlock away from the VNC window.             toggles numlock away from the VNC window.
         */          */
         if (keysym_is_numlock(vs->kbd_layout, sym & 0xFFFF)) {          if (keysym_is_numlock(vs->vd->kbd_layout, sym & 0xFFFF)) {
             if (!vs->modifiers_state[0x45]) {              if (!vs->modifiers_state[0x45]) {
                 vs->modifiers_state[0x45] = 1;                  vs->modifiers_state[0x45] = 1;
                 press_key(vs, 0xff7f);                  press_key(vs, 0xff7f);
Line 1033  static void do_key_event(VncState *vs, i Line 1252  static void do_key_event(VncState *vs, i
   
 static void key_event(VncState *vs, int down, uint32_t sym)  static void key_event(VncState *vs, int down, uint32_t sym)
 {  {
       int keycode;
   
     if (sym >= 'A' && sym <= 'Z' && is_graphic_console())      if (sym >= 'A' && sym <= 'Z' && is_graphic_console())
         sym = sym - 'A' + 'a';          sym = sym - 'A' + 'a';
     do_key_event(vs, down, sym);  
       keycode = keysym2scancode(vs->vd->kbd_layout, sym & 0xFFFF);
       do_key_event(vs, down, keycode, sym);
   }
   
   static void ext_key_event(VncState *vs, int down,
                             uint32_t sym, uint16_t keycode)
   {
       /* if the user specifies a keyboard layout, always use it */
       if (keyboard_layout)
           key_event(vs, down, sym);
       else
           do_key_event(vs, down, keycode, sym);
 }  }
   
 static void framebuffer_update_request(VncState *vs, int incremental,  static void framebuffer_update_request(VncState *vs, int incremental,
                                        int x_position, int y_position,                                         int x_position, int y_position,
                                        int w, int h)                                         int w, int h)
 {  {
     if (x_position > vs->ds->width)      if (x_position > ds_get_width(vs->ds))
         x_position = vs->ds->width;          x_position = ds_get_width(vs->ds);
     if (y_position > vs->ds->height)      if (y_position > ds_get_height(vs->ds))
         y_position = vs->ds->height;          y_position = ds_get_height(vs->ds);
     if (x_position + w >= vs->ds->width)      if (x_position + w >= ds_get_width(vs->ds))
         w = vs->ds->width  - x_position;          w = ds_get_width(vs->ds)  - x_position;
     if (y_position + h >= vs->ds->height)      if (y_position + h >= ds_get_height(vs->ds))
         h = vs->ds->height - y_position;          h = ds_get_height(vs->ds) - y_position;
   
     int i;      int i;
     vs->need_update = 1;      vs->need_update = 1;
     if (!incremental) {      if (!incremental) {
         char *old_row = vs->old_data + y_position * vs->ds->linesize;          char *old_row = vs->old_data + y_position * ds_get_linesize(vs->ds);
   
         for (i = 0; i < h; i++) {          for (i = 0; i < h; i++) {
             vnc_set_bits(vs->dirty_row[y_position + i],              vnc_set_bits(vs->dirty_row[y_position + i],
                          (vs->ds->width / 16), VNC_DIRTY_WORDS);                           (ds_get_width(vs->ds) / 16), VNC_DIRTY_WORDS);
             memset(old_row, 42, vs->ds->width * vs->depth);              memset(old_row, 42, ds_get_width(vs->ds) * ds_get_bytes_per_pixel(vs->ds));
             old_row += vs->ds->linesize;              old_row += ds_get_linesize(vs->ds);
         }          }
     }      }
 }  }
   
   static void send_ext_key_event_ack(VncState *vs)
   {
       vnc_write_u8(vs, 0);
       vnc_write_u8(vs, 0);
       vnc_write_u16(vs, 1);
       vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds),
                              VNC_ENCODING_EXT_KEY_EVENT);
       vnc_flush(vs);
   }
   
   static void send_ext_audio_ack(VncState *vs)
   {
       vnc_write_u8(vs, 0);
       vnc_write_u8(vs, 0);
       vnc_write_u16(vs, 1);
       vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds),
                              VNC_ENCODING_AUDIO);
       vnc_flush(vs);
   }
   
 static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)  static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
 {  {
     int i;      int i;
       unsigned int enc = 0;
   
     vs->has_hextile = 0;      vnc_zlib_init(vs);
     vs->has_resize = 0;      vs->features = 0;
     vs->has_pointer_type_change = 0;      vs->vnc_encoding = 0;
       vs->tight_compression = 9;
       vs->tight_quality = 9;
     vs->absolute = -1;      vs->absolute = -1;
     vs->ds->dpy_copy = NULL;  
   
     for (i = n_encodings - 1; i >= 0; i--) {      for (i = n_encodings - 1; i >= 0; i--) {
         switch (encodings[i]) {          enc = encodings[i];
         case 0: /* Raw */          switch (enc) {
             vs->has_hextile = 0;          case VNC_ENCODING_RAW:
             break;              vs->vnc_encoding = enc;
         case 1: /* CopyRect */              break;
             vs->ds->dpy_copy = vnc_copy;          case VNC_ENCODING_COPYRECT:
             break;              vs->features |= VNC_FEATURE_COPYRECT_MASK;
         case 5: /* Hextile */              break;
             vs->has_hextile = 1;          case VNC_ENCODING_HEXTILE:
             break;              vs->features |= VNC_FEATURE_HEXTILE_MASK;
         case -223: /* DesktopResize */              vs->vnc_encoding = enc;
             vs->has_resize = 1;              break;
             break;          case VNC_ENCODING_ZLIB:
         case -257:              vs->features |= VNC_FEATURE_ZLIB_MASK;
             vs->has_pointer_type_change = 1;              vs->vnc_encoding = enc;
             break;              break;
         default:          case VNC_ENCODING_DESKTOPRESIZE:
             break;              vs->features |= VNC_FEATURE_RESIZE_MASK;
         }              break;
           case VNC_ENCODING_POINTER_TYPE_CHANGE:
               vs->features |= VNC_FEATURE_POINTER_TYPE_CHANGE_MASK;
               break;
           case VNC_ENCODING_EXT_KEY_EVENT:
               send_ext_key_event_ack(vs);
               break;
           case VNC_ENCODING_AUDIO:
               send_ext_audio_ack(vs);
               break;
           case VNC_ENCODING_WMVi:
               vs->features |= VNC_FEATURE_WMVI_MASK;
               break;
           case VNC_ENCODING_COMPRESSLEVEL0 ... VNC_ENCODING_COMPRESSLEVEL0 + 9:
               vs->tight_compression = (enc & 0x0F);
               break;
           case VNC_ENCODING_QUALITYLEVEL0 ... VNC_ENCODING_QUALITYLEVEL0 + 9:
               vs->tight_quality = (enc & 0x0F);
               break;
           default:
               VNC_DEBUG("Unknown encoding: %d (0x%.8x): %d\n", i, enc, enc);
               break;
           }
     }      }
   
     check_pointer_type_change(vs, kbd_mouse_is_absolute());      check_pointer_type_change(vs, kbd_mouse_is_absolute());
 }  }
   
 static int compute_nbits(unsigned int val)  static void set_pixel_conversion(VncState *vs)
 {  {
     int n;      if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) ==
     n = 0;          (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG) && 
     while (val != 0) {          !memcmp(&(vs->clientds.pf), &(vs->ds->surface->pf), sizeof(PixelFormat))) {
         n++;          vs->write_pixels = vnc_write_pixels_copy;
         val >>= 1;          switch (vs->ds->surface->pf.bits_per_pixel) {
               case 8:
                   vs->send_hextile_tile = send_hextile_tile_8;
                   break;
               case 16:
                   vs->send_hextile_tile = send_hextile_tile_16;
                   break;
               case 32:
                   vs->send_hextile_tile = send_hextile_tile_32;
                   break;
           }
       } else {
           vs->write_pixels = vnc_write_pixels_generic;
           switch (vs->ds->surface->pf.bits_per_pixel) {
               case 8:
                   vs->send_hextile_tile = send_hextile_tile_generic_8;
                   break;
               case 16:
                   vs->send_hextile_tile = send_hextile_tile_generic_16;
                   break;
               case 32:
                   vs->send_hextile_tile = send_hextile_tile_generic_32;
                   break;
           }
     }      }
     return n;  
 }  }
   
 static void set_pixel_format(VncState *vs,  static void set_pixel_format(VncState *vs,
Line 1117  static void set_pixel_format(VncState *v Line 1416  static void set_pixel_format(VncState *v
                              int red_max, int green_max, int blue_max,                               int red_max, int green_max, int blue_max,
                              int red_shift, int green_shift, int blue_shift)                               int red_shift, int green_shift, int blue_shift)
 {  {
     int host_big_endian_flag;  
   
 #ifdef WORDS_BIGENDIAN  
     host_big_endian_flag = 1;  
 #else  
     host_big_endian_flag = 0;  
 #endif  
     if (!true_color_flag) {      if (!true_color_flag) {
     fail:  
         vnc_client_error(vs);          vnc_client_error(vs);
         return;          return;
     }      }
     if (bits_per_pixel == 32 &&  
         host_big_endian_flag == big_endian_flag &&      vs->clientds = vs->serverds;
         red_max == 0xff && green_max == 0xff && blue_max == 0xff &&      vs->clientds.pf.rmax = red_max;
         red_shift == 16 && green_shift == 8 && blue_shift == 0) {      count_bits(vs->clientds.pf.rbits, red_max);
         vs->depth = 4;      vs->clientds.pf.rshift = red_shift;
         vs->write_pixels = vnc_write_pixels_copy;      vs->clientds.pf.rmask = red_max << red_shift;
       vs->clientds.pf.gmax = green_max;
       count_bits(vs->clientds.pf.gbits, green_max);
       vs->clientds.pf.gshift = green_shift;
       vs->clientds.pf.gmask = green_max << green_shift;
       vs->clientds.pf.bmax = blue_max;
       count_bits(vs->clientds.pf.bbits, blue_max);
       vs->clientds.pf.bshift = blue_shift;
       vs->clientds.pf.bmask = blue_max << blue_shift;
       vs->clientds.pf.bits_per_pixel = bits_per_pixel;
       vs->clientds.pf.bytes_per_pixel = bits_per_pixel / 8;
       vs->clientds.pf.depth = bits_per_pixel == 32 ? 24 : bits_per_pixel;
       vs->clientds.flags = big_endian_flag ? QEMU_BIG_ENDIAN_FLAG : 0x00;
   
       set_pixel_conversion(vs);
   
       vga_hw_invalidate();
       vga_hw_update();
   }
   
   static void pixel_format_message (VncState *vs) {
       char pad[3] = { 0, 0, 0 };
   
       vnc_write_u8(vs, vs->ds->surface->pf.bits_per_pixel); /* bits-per-pixel */
       vnc_write_u8(vs, vs->ds->surface->pf.depth); /* depth */
   
   #ifdef WORDS_BIGENDIAN
       vnc_write_u8(vs, 1);             /* big-endian-flag */
   #else
       vnc_write_u8(vs, 0);             /* big-endian-flag */
   #endif
       vnc_write_u8(vs, 1);             /* true-color-flag */
       vnc_write_u16(vs, vs->ds->surface->pf.rmax);     /* red-max */
       vnc_write_u16(vs, vs->ds->surface->pf.gmax);     /* green-max */
       vnc_write_u16(vs, vs->ds->surface->pf.bmax);     /* blue-max */
       vnc_write_u8(vs, vs->ds->surface->pf.rshift);    /* red-shift */
       vnc_write_u8(vs, vs->ds->surface->pf.gshift);    /* green-shift */
       vnc_write_u8(vs, vs->ds->surface->pf.bshift);    /* blue-shift */
       if (vs->ds->surface->pf.bits_per_pixel == 32)
         vs->send_hextile_tile = send_hextile_tile_32;          vs->send_hextile_tile = send_hextile_tile_32;
     } else      else if (vs->ds->surface->pf.bits_per_pixel == 16)
     if (bits_per_pixel == 16 &&  
         host_big_endian_flag == big_endian_flag &&  
         red_max == 31 && green_max == 63 && blue_max == 31 &&  
         red_shift == 11 && green_shift == 5 && blue_shift == 0) {  
         vs->depth = 2;  
         vs->write_pixels = vnc_write_pixels_copy;  
         vs->send_hextile_tile = send_hextile_tile_16;          vs->send_hextile_tile = send_hextile_tile_16;
     } else      else if (vs->ds->surface->pf.bits_per_pixel == 8)
     if (bits_per_pixel == 8 &&  
         red_max == 7 && green_max == 7 && blue_max == 3 &&  
         red_shift == 5 && green_shift == 2 && blue_shift == 0) {  
         vs->depth = 1;  
         vs->write_pixels = vnc_write_pixels_copy;  
         vs->send_hextile_tile = send_hextile_tile_8;          vs->send_hextile_tile = send_hextile_tile_8;
     } else      vs->clientds = *(vs->ds->surface);
     {      vs->clientds.flags |= ~QEMU_ALLOCATED_FLAG;
         /* generic and slower case */      vs->write_pixels = vnc_write_pixels_copy;
         if (bits_per_pixel != 8 &&  
             bits_per_pixel != 16 &&  
             bits_per_pixel != 32)  
             goto fail;  
         vs->depth = 4;  
         vs->red_shift = red_shift;  
         vs->red_max = red_max;  
         vs->red_shift1 = 24 - compute_nbits(red_max);  
         vs->green_shift = green_shift;  
         vs->green_max = green_max;  
         vs->green_shift1 = 16 - compute_nbits(green_max);  
         vs->blue_shift = blue_shift;  
         vs->blue_max = blue_max;  
         vs->blue_shift1 = 8 - compute_nbits(blue_max);  
         vs->pix_bpp = bits_per_pixel / 8;  
         vs->pix_big_endian = big_endian_flag;  
         vs->write_pixels = vnc_write_pixels_generic;  
         vs->send_hextile_tile = send_hextile_tile_generic;  
     }  
   
     vnc_dpy_resize(vs->ds, vs->ds->width, vs->ds->height);      vnc_write(vs, pad, 3);           /* padding */
     memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row));  }
     memset(vs->old_data, 42, vs->ds->linesize * vs->ds->height);  
   
     vga_hw_invalidate();  static void vnc_dpy_setdata(DisplayState *ds)
     vga_hw_update();  {
       /* We don't have to do anything */
   }
   
   static void vnc_colordepth(VncState *vs)
   {
       if (vnc_has_feature(vs, VNC_FEATURE_WMVI)) {
           /* Sending a WMVi message to notify the client*/
           vnc_write_u8(vs, 0);  /* msg id */
           vnc_write_u8(vs, 0);
           vnc_write_u16(vs, 1); /* number of rects */
           vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), 
                                  ds_get_height(vs->ds), VNC_ENCODING_WMVi);
           pixel_format_message(vs);
           vnc_flush(vs);
       } else {
           set_pixel_conversion(vs);
       }
 }  }
   
 static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)  static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
Line 1202  static int protocol_client_msg(VncState  Line 1517  static int protocol_client_msg(VncState 
         if (len == 1)          if (len == 1)
             return 4;              return 4;
   
         if (len == 4)          if (len == 4) {
             return 4 + (read_u16(data, 2) * 4);              limit = read_u16(data, 2);
               if (limit > 0)
                   return 4 + (limit * 4);
           } else
               limit = read_u16(data, 2);
   
         limit = read_u16(data, 2);  
         for (i = 0; i < limit; i++) {          for (i = 0; i < limit; i++) {
             int32_t val = read_s32(data, 4 + (i * 4));              int32_t val = read_s32(data, 4 + (i * 4));
             memcpy(data + 4 + (i * 4), &val, sizeof(val));              memcpy(data + 4 + (i * 4), &val, sizeof(val));
Line 1245  static int protocol_client_msg(VncState  Line 1563  static int protocol_client_msg(VncState 
   
         client_cut_text(vs, read_u32(data, 4), data + 8);          client_cut_text(vs, read_u32(data, 4), data + 8);
         break;          break;
       case 255:
           if (len == 1)
               return 2;
   
           switch (read_u8(data, 1)) {
           case 0:
               if (len == 2)
                   return 12;
   
               ext_key_event(vs, read_u16(data, 2),
                             read_u32(data, 4), read_u32(data, 8));
               break;
           case 1:
               if (len == 2)
                   return 4;
   
               switch (read_u16 (data, 2)) {
               case 0:
                   audio_add(vs);
                   break;
               case 1:
                   audio_del(vs);
                   break;
               case 2:
                   if (len == 4)
                       return 10;
                   switch (read_u8(data, 4)) {
                   case 0: vs->as.fmt = AUD_FMT_U8; break;
                   case 1: vs->as.fmt = AUD_FMT_S8; break;
                   case 2: vs->as.fmt = AUD_FMT_U16; break;
                   case 3: vs->as.fmt = AUD_FMT_S16; break;
                   case 4: vs->as.fmt = AUD_FMT_U32; break;
                   case 5: vs->as.fmt = AUD_FMT_S32; break;
                   default:
                       printf("Invalid audio format %d\n", read_u8(data, 4));
                       vnc_client_error(vs);
                       break;
                   }
                   vs->as.nchannels = read_u8(data, 5);
                   if (vs->as.nchannels != 1 && vs->as.nchannels != 2) {
                       printf("Invalid audio channel coount %d\n",
                              read_u8(data, 5));
                       vnc_client_error(vs);
                       break;
                   }
                   vs->as.freq = read_u32(data, 6);
                   break;
               default:
                   printf ("Invalid audio message %d\n", read_u8(data, 4));
                   vnc_client_error(vs);
                   break;
               }
               break;
   
           default:
               printf("Msg: %d\n", read_u16(data, 0));
               vnc_client_error(vs);
               break;
           }
           break;
     default:      default:
         printf("Msg: %d\n", data[0]);          printf("Msg: %d\n", data[0]);
         vnc_client_error(vs);          vnc_client_error(vs);
Line 1257  static int protocol_client_msg(VncState  Line 1635  static int protocol_client_msg(VncState 
   
 static int protocol_client_init(VncState *vs, uint8_t *data, size_t len)  static int protocol_client_init(VncState *vs, uint8_t *data, size_t len)
 {  {
     char pad[3] = { 0, 0, 0 };  
     char buf[1024];      char buf[1024];
     int size;      int size;
   
     vs->width = vs->ds->width;      vnc_write_u16(vs, ds_get_width(vs->ds));
     vs->height = vs->ds->height;      vnc_write_u16(vs, ds_get_height(vs->ds));
     vnc_write_u16(vs, vs->ds->width);  
     vnc_write_u16(vs, vs->ds->height);  
   
     vnc_write_u8(vs, vs->depth * 8); /* bits-per-pixel */      pixel_format_message(vs);
     vnc_write_u8(vs, vs->depth * 8); /* depth */  
 #ifdef WORDS_BIGENDIAN  
     vnc_write_u8(vs, 1);             /* big-endian-flag */  
 #else  
     vnc_write_u8(vs, 0);             /* big-endian-flag */  
 #endif  
     vnc_write_u8(vs, 1);             /* true-color-flag */  
     if (vs->depth == 4) {  
         vnc_write_u16(vs, 0xFF);     /* red-max */  
         vnc_write_u16(vs, 0xFF);     /* green-max */  
         vnc_write_u16(vs, 0xFF);     /* blue-max */  
         vnc_write_u8(vs, 16);        /* red-shift */  
         vnc_write_u8(vs, 8);         /* green-shift */  
         vnc_write_u8(vs, 0);         /* blue-shift */  
         vs->send_hextile_tile = send_hextile_tile_32;  
     } else if (vs->depth == 2) {  
         vnc_write_u16(vs, 31);       /* red-max */  
         vnc_write_u16(vs, 63);       /* green-max */  
         vnc_write_u16(vs, 31);       /* blue-max */  
         vnc_write_u8(vs, 11);        /* red-shift */  
         vnc_write_u8(vs, 5);         /* green-shift */  
         vnc_write_u8(vs, 0);         /* blue-shift */  
         vs->send_hextile_tile = send_hextile_tile_16;  
     } else if (vs->depth == 1) {  
         /* XXX: change QEMU pixel 8 bit pixel format to match the VNC one ? */  
         vnc_write_u16(vs, 7);        /* red-max */  
         vnc_write_u16(vs, 7);        /* green-max */  
         vnc_write_u16(vs, 3);        /* blue-max */  
         vnc_write_u8(vs, 5);         /* red-shift */  
         vnc_write_u8(vs, 2);         /* green-shift */  
         vnc_write_u8(vs, 0);         /* blue-shift */  
         vs->send_hextile_tile = send_hextile_tile_8;  
     }  
     vs->write_pixels = vnc_write_pixels_copy;  
   
     vnc_write(vs, pad, 3);           /* padding */  
   
     if (qemu_name)      if (qemu_name)
         size = snprintf(buf, sizeof(buf), "QEMU (%s)", qemu_name);          size = snprintf(buf, sizeof(buf), "QEMU (%s)", qemu_name);
Line 1334  static int protocol_client_auth_vnc(VncS Line 1673  static int protocol_client_auth_vnc(VncS
     int i, j, pwlen;      int i, j, pwlen;
     unsigned char key[8];      unsigned char key[8];
   
     if (!vs->password || !vs->password[0]) {      if (!vs->vd->password || !vs->vd->password[0]) {
         VNC_DEBUG("No password configured on server");          VNC_DEBUG("No password configured on server");
         vnc_write_u32(vs, 1); /* Reject auth */          vnc_write_u32(vs, 1); /* Reject auth */
         if (vs->minor >= 8) {          if (vs->minor >= 8) {
Line 1350  static int protocol_client_auth_vnc(VncS Line 1689  static int protocol_client_auth_vnc(VncS
     memcpy(response, vs->challenge, VNC_AUTH_CHALLENGE_SIZE);      memcpy(response, vs->challenge, VNC_AUTH_CHALLENGE_SIZE);
   
     /* Calculate the expected challenge response */      /* Calculate the expected challenge response */
     pwlen = strlen(vs->password);      pwlen = strlen(vs->vd->password);
     for (i=0; i<sizeof(key); i++)      for (i=0; i<sizeof(key); i++)
         key[i] = i<pwlen ? vs->password[i] : 0;          key[i] = i<pwlen ? vs->vd->password[i] : 0;
     deskey(key, EN0);      deskey(key, EN0);
     for (j = 0; j < VNC_AUTH_CHALLENGE_SIZE; j += 8)      for (j = 0; j < VNC_AUTH_CHALLENGE_SIZE; j += 8)
         des(response+j, response+j);          des(response+j, response+j);
Line 1390  static int start_auth_vnc(VncState *vs) Line 1729  static int start_auth_vnc(VncState *vs)
 }  }
   
   
 #if CONFIG_VNC_TLS  #ifdef CONFIG_VNC_TLS
 #define DH_BITS 1024  #define DH_BITS 1024
 static gnutls_dh_params_t dh_params;  static gnutls_dh_params_t dh_params;
   
Line 1410  static int vnc_tls_initialize(void) Line 1749  static int vnc_tls_initialize(void)
     if (gnutls_dh_params_generate2 (dh_params, DH_BITS) < 0)      if (gnutls_dh_params_generate2 (dh_params, DH_BITS) < 0)
         return 0;          return 0;
   
 #if _VNC_DEBUG == 2  #if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2
     gnutls_global_set_log_level(10);      gnutls_global_set_log_level(10);
     gnutls_global_set_log_function(vnc_debug_gnutls_log);      gnutls_global_set_log_function(vnc_debug_gnutls_log);
 #endif  #endif
Line 1441  static gnutls_certificate_credentials_t  Line 1780  static gnutls_certificate_credentials_t 
     gnutls_certificate_credentials_t x509_cred;      gnutls_certificate_credentials_t x509_cred;
     int ret;      int ret;
   
     if (!vs->x509cacert) {      if (!vs->vd->x509cacert) {
         VNC_DEBUG("No CA x509 certificate specified\n");          VNC_DEBUG("No CA x509 certificate specified\n");
         return NULL;          return NULL;
     }      }
     if (!vs->x509cert) {      if (!vs->vd->x509cert) {
         VNC_DEBUG("No server x509 certificate specified\n");          VNC_DEBUG("No server x509 certificate specified\n");
         return NULL;          return NULL;
     }      }
     if (!vs->x509key) {      if (!vs->vd->x509key) {
         VNC_DEBUG("No server private key specified\n");          VNC_DEBUG("No server private key specified\n");
         return NULL;          return NULL;
     }      }
Line 1459  static gnutls_certificate_credentials_t  Line 1798  static gnutls_certificate_credentials_t 
         return NULL;          return NULL;
     }      }
     if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred,      if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred,
                                                       vs->x509cacert,                                                        vs->vd->x509cacert,
                                                       GNUTLS_X509_FMT_PEM)) < 0) {                                                        GNUTLS_X509_FMT_PEM)) < 0) {
         VNC_DEBUG("Cannot load CA certificate %s\n", gnutls_strerror(ret));          VNC_DEBUG("Cannot load CA certificate %s\n", gnutls_strerror(ret));
         gnutls_certificate_free_credentials(x509_cred);          gnutls_certificate_free_credentials(x509_cred);
Line 1467  static gnutls_certificate_credentials_t  Line 1806  static gnutls_certificate_credentials_t 
     }      }
   
     if ((ret = gnutls_certificate_set_x509_key_file (x509_cred,      if ((ret = gnutls_certificate_set_x509_key_file (x509_cred,
                                                      vs->x509cert,                                                       vs->vd->x509cert,
                                                      vs->x509key,                                                       vs->vd->x509key,
                                                      GNUTLS_X509_FMT_PEM)) < 0) {                                                       GNUTLS_X509_FMT_PEM)) < 0) {
         VNC_DEBUG("Cannot load certificate & key %s\n", gnutls_strerror(ret));          VNC_DEBUG("Cannot load certificate & key %s\n", gnutls_strerror(ret));
         gnutls_certificate_free_credentials(x509_cred);          gnutls_certificate_free_credentials(x509_cred);
         return NULL;          return NULL;
     }      }
   
     if (vs->x509cacrl) {      if (vs->vd->x509cacrl) {
         if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred,          if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred,
                                                         vs->x509cacrl,                                                          vs->vd->x509cacrl,
                                                         GNUTLS_X509_FMT_PEM)) < 0) {                                                          GNUTLS_X509_FMT_PEM)) < 0) {
             VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret));              VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret));
             gnutls_certificate_free_credentials(x509_cred);              gnutls_certificate_free_credentials(x509_cred);
Line 1571  static int vnc_validate_certificate(stru Line 1910  static int vnc_validate_certificate(stru
   
 static int start_auth_vencrypt_subauth(VncState *vs)  static int start_auth_vencrypt_subauth(VncState *vs)
 {  {
     switch (vs->subauth) {      switch (vs->vd->subauth) {
     case VNC_AUTH_VENCRYPT_TLSNONE:      case VNC_AUTH_VENCRYPT_TLSNONE:
     case VNC_AUTH_VENCRYPT_X509NONE:      case VNC_AUTH_VENCRYPT_X509NONE:
        VNC_DEBUG("Accept TLS auth none\n");         VNC_DEBUG("Accept TLS auth none\n");
Line 1585  static int start_auth_vencrypt_subauth(V Line 1924  static int start_auth_vencrypt_subauth(V
        return start_auth_vnc(vs);         return start_auth_vnc(vs);
   
     default: /* Should not be possible, but just in case */      default: /* Should not be possible, but just in case */
        VNC_DEBUG("Reject auth %d\n", vs->auth);         VNC_DEBUG("Reject auth %d\n", vs->vd->auth);
        vnc_write_u8(vs, 1);         vnc_write_u8(vs, 1);
        if (vs->minor >= 8) {         if (vs->minor >= 8) {
            static const char err[] = "Unsupported authentication type";             static const char err[] = "Unsupported authentication type";
Line 1617  static int vnc_continue_handshake(struct Line 1956  static int vnc_continue_handshake(struct
        return -1;         return -1;
     }      }
   
     if (vs->x509verify) {      if (vs->vd->x509verify) {
         if (vnc_validate_certificate(vs) < 0) {          if (vnc_validate_certificate(vs) < 0) {
             VNC_DEBUG("Client verification failed\n");              VNC_DEBUG("Client verification failed\n");
             vnc_client_error(vs);              vnc_client_error(vs);
Line 1642  static void vnc_handshake_io(void *opaqu Line 1981  static void vnc_handshake_io(void *opaqu
 }  }
   
 #define NEED_X509_AUTH(vs)                            \  #define NEED_X509_AUTH(vs)                            \
     ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE ||   \      ((vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509NONE ||   \
      (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC ||    \       (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509VNC ||    \
      (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN)       (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509PLAIN)
   
   
 static int vnc_start_tls(struct VncState *vs) {  static int vnc_start_tls(struct VncState *vs) {
Line 1708  static int vnc_start_tls(struct VncState Line 2047  static int vnc_start_tls(struct VncState
                 vnc_client_error(vs);                  vnc_client_error(vs);
                 return -1;                  return -1;
             }              }
             if (vs->x509verify) {              if (vs->vd->x509verify) {
                 VNC_DEBUG("Requesting a client certificate\n");                  VNC_DEBUG("Requesting a client certificate\n");
                 gnutls_certificate_server_set_request (vs->tls_session, GNUTLS_CERT_REQUEST);                  gnutls_certificate_server_set_request (vs->tls_session, GNUTLS_CERT_REQUEST);
             }              }
Line 1743  static int protocol_client_vencrypt_auth Line 2082  static int protocol_client_vencrypt_auth
 {  {
     int auth = read_u32(data, 0);      int auth = read_u32(data, 0);
   
     if (auth != vs->subauth) {      if (auth != vs->vd->subauth) {
         VNC_DEBUG("Rejecting auth %d\n", auth);          VNC_DEBUG("Rejecting auth %d\n", auth);
         vnc_write_u8(vs, 0); /* Reject auth */          vnc_write_u8(vs, 0); /* Reject auth */
         vnc_flush(vs);          vnc_flush(vs);
Line 1778  static int protocol_client_vencrypt_init Line 2117  static int protocol_client_vencrypt_init
         vnc_flush(vs);          vnc_flush(vs);
         vnc_client_error(vs);          vnc_client_error(vs);
     } else {      } else {
         VNC_DEBUG("Sending allowed auth %d\n", vs->subauth);          VNC_DEBUG("Sending allowed auth %d\n", vs->vd->subauth);
         vnc_write_u8(vs, 0); /* Accept version */          vnc_write_u8(vs, 0); /* Accept version */
         vnc_write_u8(vs, 1); /* Number of sub-auths */          vnc_write_u8(vs, 1); /* Number of sub-auths */
         vnc_write_u32(vs, vs->subauth); /* The supported auth */          vnc_write_u32(vs, vs->vd->subauth); /* The supported auth */
         vnc_flush(vs);          vnc_flush(vs);
         vnc_read_when(vs, protocol_client_vencrypt_auth, 4);          vnc_read_when(vs, protocol_client_vencrypt_auth, 4);
     }      }
Line 1803  static int protocol_client_auth(VncState Line 2142  static int protocol_client_auth(VncState
 {  {
     /* We only advertise 1 auth scheme at a time, so client      /* We only advertise 1 auth scheme at a time, so client
      * must pick the one we sent. Verify this */       * must pick the one we sent. Verify this */
     if (data[0] != vs->auth) { /* Reject auth */      if (data[0] != vs->vd->auth) { /* Reject auth */
        VNC_DEBUG("Reject auth %d\n", (int)data[0]);         VNC_DEBUG("Reject auth %d\n", (int)data[0]);
        vnc_write_u32(vs, 1);         vnc_write_u32(vs, 1);
        if (vs->minor >= 8) {         if (vs->minor >= 8) {
Line 1814  static int protocol_client_auth(VncState Line 2153  static int protocol_client_auth(VncState
        vnc_client_error(vs);         vnc_client_error(vs);
     } else { /* Accept requested auth */      } else { /* Accept requested auth */
        VNC_DEBUG("Client requested auth %d\n", (int)data[0]);         VNC_DEBUG("Client requested auth %d\n", (int)data[0]);
        switch (vs->auth) {         switch (vs->vd->auth) {
        case VNC_AUTH_NONE:         case VNC_AUTH_NONE:
            VNC_DEBUG("Accept auth none\n");             VNC_DEBUG("Accept auth none\n");
            if (vs->minor >= 8) {             if (vs->minor >= 8) {
Line 1828  static int protocol_client_auth(VncState Line 2167  static int protocol_client_auth(VncState
            VNC_DEBUG("Start VNC auth\n");             VNC_DEBUG("Start VNC auth\n");
            return start_auth_vnc(vs);             return start_auth_vnc(vs);
   
 #if CONFIG_VNC_TLS  #ifdef CONFIG_VNC_TLS
        case VNC_AUTH_VENCRYPT:         case VNC_AUTH_VENCRYPT:
            VNC_DEBUG("Accept VeNCrypt auth\n");;             VNC_DEBUG("Accept VeNCrypt auth\n");;
            return start_auth_vencrypt(vs);             return start_auth_vencrypt(vs);
 #endif /* CONFIG_VNC_TLS */  #endif /* CONFIG_VNC_TLS */
   
        default: /* Should not be possible, but just in case */         default: /* Should not be possible, but just in case */
            VNC_DEBUG("Reject auth %d\n", vs->auth);             VNC_DEBUG("Reject auth %d\n", vs->vd->auth);
            vnc_write_u8(vs, 1);             vnc_write_u8(vs, 1);
            if (vs->minor >= 8) {             if (vs->minor >= 8) {
                static const char err[] = "Authentication failed";                 static const char err[] = "Authentication failed";
Line 1880  static int protocol_version(VncState *vs Line 2219  static int protocol_version(VncState *vs
         vs->minor = 3;          vs->minor = 3;
   
     if (vs->minor == 3) {      if (vs->minor == 3) {
         if (vs->auth == VNC_AUTH_NONE) {          if (vs->vd->auth == VNC_AUTH_NONE) {
             VNC_DEBUG("Tell client auth none\n");              VNC_DEBUG("Tell client auth none\n");
             vnc_write_u32(vs, vs->auth);              vnc_write_u32(vs, vs->vd->auth);
             vnc_flush(vs);              vnc_flush(vs);
             vnc_read_when(vs, protocol_client_init, 1);              vnc_read_when(vs, protocol_client_init, 1);
        } else if (vs->auth == VNC_AUTH_VNC) {         } else if (vs->vd->auth == VNC_AUTH_VNC) {
             VNC_DEBUG("Tell client VNC auth\n");              VNC_DEBUG("Tell client VNC auth\n");
             vnc_write_u32(vs, vs->auth);              vnc_write_u32(vs, vs->vd->auth);
             vnc_flush(vs);              vnc_flush(vs);
             start_auth_vnc(vs);              start_auth_vnc(vs);
        } else {         } else {
             VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->auth);              VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->vd->auth);
             vnc_write_u32(vs, VNC_AUTH_INVALID);              vnc_write_u32(vs, VNC_AUTH_INVALID);
             vnc_flush(vs);              vnc_flush(vs);
             vnc_client_error(vs);              vnc_client_error(vs);
        }         }
     } else {      } else {
         VNC_DEBUG("Telling client we support auth %d\n", vs->auth);          VNC_DEBUG("Telling client we support auth %d\n", vs->vd->auth);
         vnc_write_u8(vs, 1); /* num auth */          vnc_write_u8(vs, 1); /* num auth */
         vnc_write_u8(vs, vs->auth);          vnc_write_u8(vs, vs->vd->auth);
         vnc_read_when(vs, protocol_client_auth, 1);          vnc_read_when(vs, protocol_client_auth, 1);
         vnc_flush(vs);          vnc_flush(vs);
     }      }
Line 1907  static int protocol_version(VncState *vs Line 2246  static int protocol_version(VncState *vs
     return 0;      return 0;
 }  }
   
   static void vnc_connect(VncDisplay *vd, int csock)
   {
       VncState *vs = qemu_mallocz(sizeof(VncState));
       vs->csock = csock;
   
       VNC_DEBUG("New client on socket %d\n", csock);
       dcl->idle = 0;
       socket_set_nonblock(vs->csock);
       qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
   
       vs->vd = vd;
       vs->ds = vd->ds;
       vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs);
       vs->last_x = -1;
       vs->last_y = -1;
   
       vs->as.freq = 44100;
       vs->as.nchannels = 2;
       vs->as.fmt = AUD_FMT_S16;
       vs->as.endianness = 0;
   
       vnc_resize(vs);
       vnc_write(vs, "RFB 003.008\n", 12);
       vnc_flush(vs);
       vnc_read_when(vs, protocol_version, 12);
       memset(vs->old_data, 0, ds_get_linesize(vs->ds) * ds_get_height(vs->ds));
       memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row));
       vnc_update_client(vs);
       reset_keys(vs);
   
       vs->next = vd->clients;
       vd->clients = vs;
   }
   
 static void vnc_listen_read(void *opaque)  static void vnc_listen_read(void *opaque)
 {  {
     VncState *vs = opaque;      VncDisplay *vs = opaque;
     struct sockaddr_in addr;      struct sockaddr_in addr;
     socklen_t addrlen = sizeof(addr);      socklen_t addrlen = sizeof(addr);
   
     vs->csock = accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);      /* Catch-up */
     if (vs->csock != -1) {      vga_hw_update();
         VNC_DEBUG("New client on socket %d\n", vs->csock);  
         socket_set_nonblock(vs->csock);      int csock = accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);
         qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, opaque);      if (csock != -1) {
         vnc_write(vs, "RFB 003.008\n", 12);          vnc_connect(vs, csock);
         vnc_flush(vs);  
         vnc_read_when(vs, protocol_version, 12);  
         memset(vs->old_data, 0, vs->ds->linesize * vs->ds->height);  
         memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row));  
         vs->has_resize = 0;  
         vs->has_hextile = 0;  
         vs->ds->dpy_copy = NULL;  
     }      }
 }  }
   
 extern int parse_host_port(struct sockaddr_in *saddr, const char *str);  
   
 void vnc_display_init(DisplayState *ds)  void vnc_display_init(DisplayState *ds)
 {  {
     VncState *vs;      VncDisplay *vs;
   
     vs = qemu_mallocz(sizeof(VncState));      vs = qemu_mallocz(sizeof(VncState));
     if (!vs)      dcl = qemu_mallocz(sizeof(DisplayChangeListener));
         exit(1);  
   
     ds->opaque = vs;      ds->opaque = vs;
     vnc_state = vs;      dcl->idle = 1;
     vs->display = NULL;      vnc_display = vs;
     vs->password = NULL;  
   
     vs->lsock = -1;      vs->lsock = -1;
     vs->csock = -1;  
     vs->depth = 4;  
     vs->last_x = -1;  
     vs->last_y = -1;  
   
     vs->ds = ds;      vs->ds = ds;
   
     if (!keyboard_layout)      if (keyboard_layout)
         keyboard_layout = "en-us";          vs->kbd_layout = init_keyboard_layout(keyboard_layout);
       else
           vs->kbd_layout = init_keyboard_layout("en-us");
   
     vs->kbd_layout = init_keyboard_layout(keyboard_layout);  
     if (!vs->kbd_layout)      if (!vs->kbd_layout)
         exit(1);          exit(1);
   
     vs->ds->data = NULL;      dcl->dpy_copy = vnc_dpy_copy;
     vs->ds->dpy_update = vnc_dpy_update;      dcl->dpy_update = vnc_dpy_update;
     vs->ds->dpy_resize = vnc_dpy_resize;      dcl->dpy_resize = vnc_dpy_resize;
     vs->ds->dpy_refresh = vnc_dpy_refresh;      dcl->dpy_setdata = vnc_dpy_setdata;
       register_displaychangelistener(ds, dcl);
     memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row));  
   
     vnc_dpy_resize(vs->ds, 640, 400);  
 }  }
   
 #if CONFIG_VNC_TLS  #ifdef CONFIG_VNC_TLS
 static int vnc_set_x509_credential(VncState *vs,  static int vnc_set_x509_credential(VncDisplay *vs,
                                    const char *certdir,                                     const char *certdir,
                                    const char *filename,                                     const char *filename,
                                    char **cred,                                     char **cred,
Line 1983  static int vnc_set_x509_credential(VncSt Line 2339  static int vnc_set_x509_credential(VncSt
         *cred = NULL;          *cred = NULL;
     }      }
   
     if (!(*cred = qemu_malloc(strlen(certdir) + strlen(filename) + 2)))      *cred = qemu_malloc(strlen(certdir) + strlen(filename) + 2);
         return -1;  
   
     strcpy(*cred, certdir);      strcpy(*cred, certdir);
     strcat(*cred, "/");      strcat(*cred, "/");
Line 2002  static int vnc_set_x509_credential(VncSt Line 2357  static int vnc_set_x509_credential(VncSt
     return 0;      return 0;
 }  }
   
 static int vnc_set_x509_credential_dir(VncState *vs,  static int vnc_set_x509_credential_dir(VncDisplay *vs,
                                        const char *certdir)                                         const char *certdir)
 {  {
     if (vnc_set_x509_credential(vs, certdir, X509_CA_CERT_FILE, &vs->x509cacert, 0) < 0)      if (vnc_set_x509_credential(vs, certdir, X509_CA_CERT_FILE, &vs->x509cacert, 0) < 0)
Line 2028  static int vnc_set_x509_credential_dir(V Line 2383  static int vnc_set_x509_credential_dir(V
   
 void vnc_display_close(DisplayState *ds)  void vnc_display_close(DisplayState *ds)
 {  {
     VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;      VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
   
       if (!vs)
           return;
     if (vs->display) {      if (vs->display) {
         qemu_free(vs->display);          qemu_free(vs->display);
         vs->display = NULL;          vs->display = NULL;
Line 2039  void vnc_display_close(DisplayState *ds) Line 2396  void vnc_display_close(DisplayState *ds)
         close(vs->lsock);          close(vs->lsock);
         vs->lsock = -1;          vs->lsock = -1;
     }      }
     if (vs->csock != -1) {  
         qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);  
         closesocket(vs->csock);  
         vs->csock = -1;  
         buffer_reset(&vs->input);  
         buffer_reset(&vs->output);  
         vs->need_update = 0;  
 #if CONFIG_VNC_TLS  
         if (vs->tls_session) {  
             gnutls_deinit(vs->tls_session);  
             vs->tls_session = NULL;  
         }  
         vs->wiremode = VNC_WIREMODE_CLEAR;  
 #endif /* CONFIG_VNC_TLS */  
     }  
     vs->auth = VNC_AUTH_INVALID;      vs->auth = VNC_AUTH_INVALID;
 #if CONFIG_VNC_TLS  #ifdef CONFIG_VNC_TLS
     vs->subauth = VNC_AUTH_INVALID;      vs->subauth = VNC_AUTH_INVALID;
     vs->x509verify = 0;      vs->x509verify = 0;
 #endif  #endif
Line 2063  void vnc_display_close(DisplayState *ds) Line 2405  void vnc_display_close(DisplayState *ds)
   
 int vnc_display_password(DisplayState *ds, const char *password)  int vnc_display_password(DisplayState *ds, const char *password)
 {  {
     VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;      VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
   
     if (vs->password) {      if (vs->password) {
         qemu_free(vs->password);          qemu_free(vs->password);
Line 2079  int vnc_display_password(DisplayState *d Line 2421  int vnc_display_password(DisplayState *d
   
 int vnc_display_open(DisplayState *ds, const char *display)  int vnc_display_open(DisplayState *ds, const char *display)
 {  {
     struct sockaddr *addr;      VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
     struct sockaddr_in iaddr;  
 #ifndef _WIN32  
     struct sockaddr_un uaddr;  
 #endif  
     int reuse_addr, ret;  
     socklen_t addrlen;  
     const char *p;  
     VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;  
     const char *options;      const char *options;
     int password = 0;      int password = 0;
 #if CONFIG_VNC_TLS      int reverse = 0;
       int to_port = 0;
   #ifdef CONFIG_VNC_TLS
     int tls = 0, x509 = 0;      int tls = 0, x509 = 0;
 #endif  #endif
   
       if (!vnc_display)
           return -1;
     vnc_display_close(ds);      vnc_display_close(ds);
     if (strcmp(display, "none") == 0)      if (strcmp(display, "none") == 0)
         return 0;          return 0;
Line 2106  int vnc_display_open(DisplayState *ds, c Line 2444  int vnc_display_open(DisplayState *ds, c
         options++;          options++;
         if (strncmp(options, "password", 8) == 0) {          if (strncmp(options, "password", 8) == 0) {
             password = 1; /* Require password auth */              password = 1; /* Require password auth */
 #if CONFIG_VNC_TLS          } else if (strncmp(options, "reverse", 7) == 0) {
               reverse = 1;
           } else if (strncmp(options, "to=", 3) == 0) {
               to_port = atoi(options+3) + 5900;
   #ifdef CONFIG_VNC_TLS
         } else if (strncmp(options, "tls", 3) == 0) {          } else if (strncmp(options, "tls", 3) == 0) {
             tls = 1; /* Require TLS */              tls = 1; /* Require TLS */
         } else if (strncmp(options, "x509", 4) == 0) {          } else if (strncmp(options, "x509", 4) == 0) {
Line 2121  int vnc_display_open(DisplayState *ds, c Line 2463  int vnc_display_open(DisplayState *ds, c
             end = strchr(options, ',');              end = strchr(options, ',');
             if (start && (!end || (start < end))) {              if (start && (!end || (start < end))) {
                 int len = end ? end-(start+1) : strlen(start+1);                  int len = end ? end-(start+1) : strlen(start+1);
                 char *path = qemu_malloc(len+1);                  char *path = qemu_strndup(start + 1, len);
                 strncpy(path, start+1, len);  
                 path[len] = '\0';  
                 VNC_DEBUG("Trying certificate path '%s'\n", path);                  VNC_DEBUG("Trying certificate path '%s'\n", path);
                 if (vnc_set_x509_credential_dir(vs, path) < 0) {                  if (vnc_set_x509_credential_dir(vs, path) < 0) {
                     fprintf(stderr, "Failed to find x509 certificates/keys in %s\n", path);                      fprintf(stderr, "Failed to find x509 certificates/keys in %s\n", path);
Line 2144  int vnc_display_open(DisplayState *ds, c Line 2485  int vnc_display_open(DisplayState *ds, c
     }      }
   
     if (password) {      if (password) {
 #if CONFIG_VNC_TLS  #ifdef CONFIG_VNC_TLS
         if (tls) {          if (tls) {
             vs->auth = VNC_AUTH_VENCRYPT;              vs->auth = VNC_AUTH_VENCRYPT;
             if (x509) {              if (x509) {
Line 2158  int vnc_display_open(DisplayState *ds, c Line 2499  int vnc_display_open(DisplayState *ds, c
 #endif  #endif
             VNC_DEBUG("Initializing VNC server with password auth\n");              VNC_DEBUG("Initializing VNC server with password auth\n");
             vs->auth = VNC_AUTH_VNC;              vs->auth = VNC_AUTH_VNC;
 #if CONFIG_VNC_TLS  #ifdef CONFIG_VNC_TLS
             vs->subauth = VNC_AUTH_INVALID;              vs->subauth = VNC_AUTH_INVALID;
         }          }
 #endif  #endif
     } else {      } else {
 #if CONFIG_VNC_TLS  #ifdef CONFIG_VNC_TLS
         if (tls) {          if (tls) {
             vs->auth = VNC_AUTH_VENCRYPT;              vs->auth = VNC_AUTH_VENCRYPT;
             if (x509) {              if (x509) {
Line 2177  int vnc_display_open(DisplayState *ds, c Line 2518  int vnc_display_open(DisplayState *ds, c
 #endif  #endif
             VNC_DEBUG("Initializing VNC server with no auth\n");              VNC_DEBUG("Initializing VNC server with no auth\n");
             vs->auth = VNC_AUTH_NONE;              vs->auth = VNC_AUTH_NONE;
 #if CONFIG_VNC_TLS  #ifdef CONFIG_VNC_TLS
             vs->subauth = VNC_AUTH_INVALID;              vs->subauth = VNC_AUTH_INVALID;
         }          }
 #endif  #endif
     }      }
 #ifndef _WIN32  
     if (strstart(display, "unix:", &p)) {  
         addr = (struct sockaddr *)&uaddr;  
         addrlen = sizeof(uaddr);  
   
         vs->lsock = socket(PF_UNIX, SOCK_STREAM, 0);  
         if (vs->lsock == -1) {  
             fprintf(stderr, "Could not create socket\n");  
             free(vs->display);  
             vs->display = NULL;  
             return -1;  
         }  
   
         uaddr.sun_family = AF_UNIX;  
         memset(uaddr.sun_path, 0, 108);  
         snprintf(uaddr.sun_path, 108, "%s", p);  
   
         unlink(uaddr.sun_path);  
     } else  
 #endif  
     {  
         addr = (struct sockaddr *)&iaddr;  
         addrlen = sizeof(iaddr);  
   
         if (parse_host_port(&iaddr, display) < 0) {  
             fprintf(stderr, "Could not parse VNC address\n");  
             free(vs->display);  
             vs->display = NULL;  
             return -1;  
         }  
   
         iaddr.sin_port = htons(ntohs(iaddr.sin_port) + 5900);  
   
         vs->lsock = socket(PF_INET, SOCK_STREAM, 0);  
         if (vs->lsock == -1) {  
             fprintf(stderr, "Could not create socket\n");  
             free(vs->display);  
             vs->display = NULL;  
             return -1;  
         }  
   
         reuse_addr = 1;  
         ret = setsockopt(vs->lsock, SOL_SOCKET, SO_REUSEADDR,  
                          (const char *)&reuse_addr, sizeof(reuse_addr));  
         if (ret == -1) {  
             fprintf(stderr, "setsockopt() failed\n");  
             close(vs->lsock);  
             vs->lsock = -1;  
             free(vs->display);  
             vs->display = NULL;  
             return -1;  
         }  
     }  
   
     if (bind(vs->lsock, addr, addrlen) == -1) {      if (reverse) {
         fprintf(stderr, "bind() failed\n");          /* connect to viewer */
         close(vs->lsock);          if (strncmp(display, "unix:", 5) == 0)
         vs->lsock = -1;              vs->lsock = unix_connect(display+5);
         free(vs->display);          else
         vs->display = NULL;              vs->lsock = inet_connect(display, SOCK_STREAM);
         return -1;          if (-1 == vs->lsock) {
     }              free(vs->display);
               vs->display = NULL;
               return -1;
           } else {
               int csock = vs->lsock;
               vs->lsock = -1;
               vnc_connect(vs, csock);
           }
           return 0;
   
     if (listen(vs->lsock, 1) == -1) {      } else {
         fprintf(stderr, "listen() failed\n");          /* listen for connects */
         close(vs->lsock);          char *dpy;
         vs->lsock = -1;          dpy = qemu_malloc(256);
         free(vs->display);          if (strncmp(display, "unix:", 5) == 0) {
         vs->display = NULL;              pstrcpy(dpy, 256, "unix:");
         return -1;              vs->lsock = unix_listen(display+5, dpy+5, 256-5);
           } else {
               vs->lsock = inet_listen(display, dpy, 256, SOCK_STREAM, 5900);
           }
           if (-1 == vs->lsock) {
               free(dpy);
               return -1;
           } else {
               free(vs->display);
               vs->display = dpy;
           }
     }      }
       return qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_read, NULL, vs);
     return qemu_set_fd_handler2(vs->lsock, vnc_listen_poll, vnc_listen_read, NULL, vs);  
 }  }

Removed from v.1.1.1.4  
changed lines
  Added in v.1.1.1.5


unix.superglobalmegacorp.com