source: trunk/packages/sipb-xen-vnc-client/code/RfbProto.java @ 904

Last change on this file since 904 was 233, checked in by ecprice, 17 years ago

Actually close the connection on disconnect, fixing #3.

File size: 36.6 KB
Line 
1//
2//  Copyright (C) 2001-2004 HorizonLive.com, Inc.  All Rights Reserved.
3//  Copyright (C) 2001-2006 Constantin Kaplinsky.  All Rights Reserved.
4//  Copyright (C) 2000 Tridia Corporation.  All Rights Reserved.
5//  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
6//
7//  This 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; either version 2 of the License, or
10//  (at your option) any later version.
11//
12//  This software is distributed in the hope that it will be useful,
13//  but WITHOUT ANY WARRANTY; without even the implied warranty of
14//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15//  GNU General Public License for more details.
16//
17//  You should have received a copy of the GNU General Public License
18//  along with this software; if not, write to the Free Software
19//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
20//  USA.
21//
22
23//
24// RfbProto.java
25//
26
27import java.io.*;
28import java.awt.*;
29import java.awt.event.*;
30import java.net.Socket;
31import java.util.zip.*;
32
33class RfbProto {
34
35  final static String
36    versionMsg_3_3 = "RFB 003.003\n",
37    versionMsg_3_7 = "RFB 003.007\n",
38    versionMsg_3_8 = "RFB 003.008\n";
39
40  // Vendor signatures: standard VNC/RealVNC, TridiaVNC, and TightVNC
41  final static String
42    StandardVendor  = "STDV",
43    TridiaVncVendor = "TRDV",
44    TightVncVendor  = "TGHT";
45
46  // Security types
47  final static int
48    SecTypeInvalid = 0,
49    SecTypeNone    = 1,
50    SecTypeVncAuth = 2,
51    SecTypeTight   = 16;
52
53  // Supported tunneling types
54  final static int
55    NoTunneling = 0;
56  final static String
57    SigNoTunneling = "NOTUNNEL";
58
59  // Supported authentication types
60  final static int
61    AuthNone      = 1,
62    AuthVNC       = 2,
63    AuthUnixLogin = 129;
64  final static String
65    SigAuthNone      = "NOAUTH__",
66    SigAuthVNC       = "VNCAUTH_",
67    SigAuthUnixLogin = "ULGNAUTH";
68
69  // VNC authentication results
70  final static int
71    VncAuthOK      = 0,
72    VncAuthFailed  = 1,
73    VncAuthTooMany = 2;
74
75  // Server-to-client messages
76  final static int
77    FramebufferUpdate   = 0,
78    SetColourMapEntries = 1,
79    Bell                = 2,
80    ServerCutText       = 3;
81
82  // Client-to-server messages
83  final static int
84    SetPixelFormat           = 0,
85    FixColourMapEntries      = 1,
86    SetEncodings             = 2,
87    FramebufferUpdateRequest = 3,
88    KeyboardEvent            = 4,
89    PointerEvent             = 5,
90    ClientCutText            = 6;
91
92  // Supported encodings and pseudo-encodings
93  final static int
94    EncodingRaw            = 0,
95    EncodingCopyRect       = 1,
96    EncodingRRE            = 2,
97    EncodingCoRRE          = 4,
98    EncodingHextile        = 5,
99    EncodingZlib           = 6,
100    EncodingTight          = 7,
101    EncodingZRLE           = 16,
102    EncodingCompressLevel0 = 0xFFFFFF00,
103    EncodingQualityLevel0  = 0xFFFFFFE0,
104    EncodingXCursor        = 0xFFFFFF10,
105    EncodingRichCursor     = 0xFFFFFF11,
106    EncodingPointerPos     = 0xFFFFFF18,
107    EncodingLastRect       = 0xFFFFFF20,
108    EncodingNewFBSize      = 0xFFFFFF21;
109  final static String
110    SigEncodingRaw            = "RAW_____",
111    SigEncodingCopyRect       = "COPYRECT",
112    SigEncodingRRE            = "RRE_____",
113    SigEncodingCoRRE          = "CORRE___",
114    SigEncodingHextile        = "HEXTILE_",
115    SigEncodingZlib           = "ZLIB____",
116    SigEncodingTight          = "TIGHT___",
117    SigEncodingZRLE           = "ZRLE____",
118    SigEncodingCompressLevel0 = "COMPRLVL",
119    SigEncodingQualityLevel0  = "JPEGQLVL",
120    SigEncodingXCursor        = "X11CURSR",
121    SigEncodingRichCursor     = "RCHCURSR",
122    SigEncodingPointerPos     = "POINTPOS",
123    SigEncodingLastRect       = "LASTRECT",
124    SigEncodingNewFBSize      = "NEWFBSIZ";
125
126  final static int MaxNormalEncoding = 255;
127
128  // Contstants used in the Hextile decoder
129  final static int
130    HextileRaw                 = 1,
131    HextileBackgroundSpecified = 2,
132    HextileForegroundSpecified = 4,
133    HextileAnySubrects         = 8,
134    HextileSubrectsColoured    = 16;
135
136  // Contstants used in the Tight decoder
137  final static int TightMinToCompress = 12;
138  final static int
139    TightExplicitFilter = 0x04,
140    TightFill           = 0x08,
141    TightJpeg           = 0x09,
142    TightMaxSubencoding = 0x09,
143    TightFilterCopy     = 0x00,
144    TightFilterPalette  = 0x01,
145    TightFilterGradient = 0x02;
146
147
148  String host;
149  int port;
150  Socket sock;
151  DataInputStream is;
152  OutputStream os;
153  SessionRecorder rec;
154  boolean inNormalProtocol = false;
155  VncViewer viewer;
156
157  // Java on UNIX does not call keyPressed() on some keys, for example
158  // swedish keys To prevent our workaround to produce duplicate
159  // keypresses on JVMs that actually works, keep track of if
160  // keyPressed() for a "broken" key was called or not.
161  boolean brokenKeyPressed = false;
162
163  // This will be set to true on the first framebuffer update
164  // containing Zlib-, ZRLE- or Tight-encoded data.
165  boolean wereZlibUpdates = false;
166
167  // This will be set to false if the startSession() was called after
168  // we have received at least one Zlib-, ZRLE- or Tight-encoded
169  // framebuffer update.
170  boolean recordFromBeginning = true;
171
172  // This fields are needed to show warnings about inefficiently saved
173  // sessions only once per each saved session file.
174  boolean zlibWarningShown;
175  boolean tightWarningShown;
176
177  // Before starting to record each saved session, we set this field
178  // to 0, and increment on each framebuffer update. We don't flush
179  // the SessionRecorder data into the file before the second update.
180  // This allows us to write initial framebuffer update with zero
181  // timestamp, to let the player show initial desktop before
182  // playback.
183  int numUpdatesInSession;
184
185  // Measuring network throughput.
186  boolean timing;
187  long timeWaitedIn100us;
188  long timedKbits;
189
190  // Protocol version and TightVNC-specific protocol options.
191  int serverMajor, serverMinor;
192  int clientMajor, clientMinor;
193  boolean protocolTightVNC;
194  CapsContainer tunnelCaps, authCaps;
195  CapsContainer serverMsgCaps, clientMsgCaps;
196  CapsContainer encodingCaps;
197
198  // If true, informs that the RFB socket was closed.
199  private boolean closed;
200
201  //
202  // Constructor. Make TCP connection to RFB server.
203  //
204
205  RfbProto(String h, int p, VncViewer v) throws IOException {
206    viewer = v;
207    host = h;
208    port = p;
209
210    if (viewer.socketFactory == null) {
211        System.out.println("Null socketFactory");
212      sock = new Socket(host, port);
213    } else {
214      try {
215        Class factoryClass = Class.forName(viewer.socketFactory);
216        SocketFactory factory = (SocketFactory)factoryClass.newInstance();
217        System.out.println("Using socketFactory " + factory);
218        if (viewer.inAnApplet)
219          sock = factory.createSocket(host, port, viewer);
220        else
221          sock = factory.createSocket(host, port, viewer.mainArgs);
222      } catch(Exception e) {
223        e.printStackTrace();
224        throw new IOException(e.getMessage());
225      }
226    }
227    is = new DataInputStream(new BufferedInputStream(sock.getInputStream(),
228                                                     16384));
229    os = sock.getOutputStream();
230
231    timing = false;
232    timeWaitedIn100us = 5;
233    timedKbits = 0;
234  }
235
236
237  synchronized void close() {
238    try {
239      sock.close();
240      closed = true;
241      System.out.println("RFB socket closed " + sock);
242      if (rec != null) {
243        rec.close();
244        rec = null;
245      }
246    } catch (Exception e) {
247      e.printStackTrace();
248    }
249  }
250
251  synchronized boolean closed() {
252    return closed;
253  }
254
255  //
256  // Read server's protocol version message
257  //
258
259  void readVersionMsg() throws Exception {
260
261    byte[] b = new byte[12];
262
263    readFully(b);
264
265    if ((b[0] != 'R') || (b[1] != 'F') || (b[2] != 'B') || (b[3] != ' ')
266        || (b[4] < '0') || (b[4] > '9') || (b[5] < '0') || (b[5] > '9')
267        || (b[6] < '0') || (b[6] > '9') || (b[7] != '.')
268        || (b[8] < '0') || (b[8] > '9') || (b[9] < '0') || (b[9] > '9')
269        || (b[10] < '0') || (b[10] > '9') || (b[11] != '\n'))
270    {
271      throw new Exception("Host " + host + " port " + port +
272                          " is not an RFB server");
273    }
274
275    serverMajor = (b[4] - '0') * 100 + (b[5] - '0') * 10 + (b[6] - '0');
276    serverMinor = (b[8] - '0') * 100 + (b[9] - '0') * 10 + (b[10] - '0');
277
278    if (serverMajor < 3) {
279      throw new Exception("RFB server does not support protocol version 3");
280    }
281  }
282
283
284  //
285  // Write our protocol version message
286  //
287
288  void writeVersionMsg() throws IOException {
289    clientMajor = 3;
290    if (serverMajor > 3 || serverMinor >= 8) {
291      clientMinor = 8;
292      os.write(versionMsg_3_8.getBytes());
293    } else if (serverMinor >= 7) {
294      clientMinor = 7;
295      os.write(versionMsg_3_7.getBytes());
296    } else {
297      clientMinor = 3;
298      os.write(versionMsg_3_3.getBytes());
299    }
300    protocolTightVNC = false;
301  }
302
303
304  //
305  // Negotiate the authentication scheme.
306  //
307
308  int negotiateSecurity() throws Exception {
309    return (clientMinor >= 7) ?
310      selectSecurityType() : readSecurityType();
311  }
312
313  //
314  // Read security type from the server (protocol version 3.3).
315  //
316
317  int readSecurityType() throws Exception {
318    int secType = is.readInt();
319
320    switch (secType) {
321    case SecTypeInvalid:
322      readConnFailedReason();
323      return SecTypeInvalid;    // should never be executed
324    case SecTypeNone:
325    case SecTypeVncAuth:
326      return secType;
327    default:
328      throw new Exception("Unknown security type from RFB server: " + secType);
329    }
330  }
331
332  //
333  // Select security type from the server's list (protocol versions 3.7/3.8).
334  //
335
336  int selectSecurityType() throws Exception {
337    int secType = SecTypeInvalid;
338
339    // Read the list of secutiry types.
340    int nSecTypes = is.readUnsignedByte();
341    if (nSecTypes == 0) {
342      readConnFailedReason();
343      return SecTypeInvalid;    // should never be executed
344    }
345    byte[] secTypes = new byte[nSecTypes];
346    readFully(secTypes);
347
348    // Find out if the server supports TightVNC protocol extensions
349    for (int i = 0; i < nSecTypes; i++) {
350      if (secTypes[i] == SecTypeTight) {
351        protocolTightVNC = true;
352        os.write(SecTypeTight);
353        return SecTypeTight;
354      }
355    }
356
357    // Find first supported security type.
358    for (int i = 0; i < nSecTypes; i++) {
359      if (secTypes[i] == SecTypeNone || secTypes[i] == SecTypeVncAuth) {
360        secType = secTypes[i];
361        break;
362      }
363    }
364
365    if (secType == SecTypeInvalid) {
366      throw new Exception("Server did not offer supported security type");
367    } else {
368      os.write(secType);
369    }
370
371    return secType;
372  }
373
374  //
375  // Perform "no authentication".
376  //
377
378  void authenticateNone() throws Exception {
379    if (clientMinor >= 8)
380      readSecurityResult("No authentication");
381  }
382
383  //
384  // Perform standard VNC Authentication.
385  //
386
387  void authenticateVNC(String pw) throws Exception {
388    byte[] challenge = new byte[16];
389    readFully(challenge);
390
391    if (pw.length() > 8)
392      pw = pw.substring(0, 8);  // Truncate to 8 chars
393
394    // Truncate password on the first zero byte.
395    int firstZero = pw.indexOf(0);
396    if (firstZero != -1)
397      pw = pw.substring(0, firstZero);
398
399    byte[] key = {0, 0, 0, 0, 0, 0, 0, 0};
400    System.arraycopy(pw.getBytes(), 0, key, 0, pw.length());
401
402    DesCipher des = new DesCipher(key);
403
404    des.encrypt(challenge, 0, challenge, 0);
405    des.encrypt(challenge, 8, challenge, 8);
406
407    os.write(challenge);
408
409    readSecurityResult("VNC authentication");
410  }
411
412  //
413  // Read security result.
414  // Throws an exception on authentication failure.
415  //
416
417  void readSecurityResult(String authType) throws Exception {
418    int securityResult = is.readInt();
419
420    switch (securityResult) {
421    case VncAuthOK:
422      System.out.println(authType + ": success");
423      break;
424    case VncAuthFailed:
425      if (clientMinor >= 8)
426        readConnFailedReason();
427      throw new Exception(authType + ": failed");
428    case VncAuthTooMany:
429      throw new Exception(authType + ": failed, too many tries");
430    default:
431      throw new Exception(authType + ": unknown result " + securityResult);
432    }
433  }
434
435  //
436  // Read the string describing the reason for a connection failure,
437  // and throw an exception.
438  //
439
440  void readConnFailedReason() throws Exception {
441    int reasonLen = is.readInt();
442    byte[] reason = new byte[reasonLen];
443    readFully(reason);
444    throw new Exception(new String(reason));
445  }
446
447  //
448  // Initialize capability lists (TightVNC protocol extensions).
449  //
450
451  void initCapabilities() {
452    tunnelCaps    = new CapsContainer();
453    authCaps      = new CapsContainer();
454    serverMsgCaps = new CapsContainer();
455    clientMsgCaps = new CapsContainer();
456    encodingCaps  = new CapsContainer();
457
458    // Supported authentication methods
459    authCaps.add(AuthNone, StandardVendor, SigAuthNone,
460                 "No authentication");
461    authCaps.add(AuthVNC, StandardVendor, SigAuthVNC,
462                 "Standard VNC password authentication");
463
464    // Supported encoding types
465    encodingCaps.add(EncodingCopyRect, StandardVendor,
466                     SigEncodingCopyRect, "Standard CopyRect encoding");
467    encodingCaps.add(EncodingRRE, StandardVendor,
468                     SigEncodingRRE, "Standard RRE encoding");
469    encodingCaps.add(EncodingCoRRE, StandardVendor,
470                     SigEncodingCoRRE, "Standard CoRRE encoding");
471    encodingCaps.add(EncodingHextile, StandardVendor,
472                     SigEncodingHextile, "Standard Hextile encoding");
473    encodingCaps.add(EncodingZRLE, StandardVendor,
474                     SigEncodingZRLE, "Standard ZRLE encoding");
475    encodingCaps.add(EncodingZlib, TridiaVncVendor,
476                     SigEncodingZlib, "Zlib encoding");
477    encodingCaps.add(EncodingTight, TightVncVendor,
478                     SigEncodingTight, "Tight encoding");
479
480    // Supported pseudo-encoding types
481    encodingCaps.add(EncodingCompressLevel0, TightVncVendor,
482                     SigEncodingCompressLevel0, "Compression level");
483    encodingCaps.add(EncodingQualityLevel0, TightVncVendor,
484                     SigEncodingQualityLevel0, "JPEG quality level");
485    encodingCaps.add(EncodingXCursor, TightVncVendor,
486                     SigEncodingXCursor, "X-style cursor shape update");
487    encodingCaps.add(EncodingRichCursor, TightVncVendor,
488                     SigEncodingRichCursor, "Rich-color cursor shape update");
489    encodingCaps.add(EncodingPointerPos, TightVncVendor,
490                     SigEncodingPointerPos, "Pointer position update");
491    encodingCaps.add(EncodingLastRect, TightVncVendor,
492                     SigEncodingLastRect, "LastRect protocol extension");
493    encodingCaps.add(EncodingNewFBSize, TightVncVendor,
494                     SigEncodingNewFBSize, "Framebuffer size change");
495  }
496
497  //
498  // Setup tunneling (TightVNC protocol extensions)
499  //
500
501  void setupTunneling() throws IOException {
502    int nTunnelTypes = is.readInt();
503    if (nTunnelTypes != 0) {
504      readCapabilityList(tunnelCaps, nTunnelTypes);
505
506      // We don't support tunneling yet.
507      writeInt(NoTunneling);
508    }
509  }
510
511  //
512  // Negotiate authentication scheme (TightVNC protocol extensions)
513  //
514
515  int negotiateAuthenticationTight() throws Exception {
516    int nAuthTypes = is.readInt();
517    if (nAuthTypes == 0)
518      return AuthNone;
519
520    readCapabilityList(authCaps, nAuthTypes);
521    for (int i = 0; i < authCaps.numEnabled(); i++) {
522      int authType = authCaps.getByOrder(i);
523      if (authType == AuthNone || authType == AuthVNC) {
524        writeInt(authType);
525        return authType;
526      }
527    }
528    throw new Exception("No suitable authentication scheme found");
529  }
530
531  //
532  // Read a capability list (TightVNC protocol extensions)
533  //
534
535  void readCapabilityList(CapsContainer caps, int count) throws IOException {
536    int code;
537    byte[] vendor = new byte[4];
538    byte[] name = new byte[8];
539    for (int i = 0; i < count; i++) {
540      code = is.readInt();
541      readFully(vendor);
542      readFully(name);
543      caps.enable(new CapabilityInfo(code, vendor, name));
544    }
545  }
546
547  //
548  // Write a 32-bit integer into the output stream.
549  //
550
551  void writeInt(int value) throws IOException {
552    byte[] b = new byte[4];
553    b[0] = (byte) ((value >> 24) & 0xff);
554    b[1] = (byte) ((value >> 16) & 0xff);
555    b[2] = (byte) ((value >> 8) & 0xff);
556    b[3] = (byte) (value & 0xff);
557    os.write(b);
558  }
559
560  //
561  // Write the client initialisation message
562  //
563
564  void writeClientInit() throws IOException {
565    if (viewer.options.shareDesktop) {
566      os.write(1);
567    } else {
568      os.write(0);
569    }
570    viewer.options.disableShareDesktop();
571  }
572
573
574  //
575  // Read the server initialisation message
576  //
577
578  String desktopName;
579  int framebufferWidth, framebufferHeight;
580  int bitsPerPixel, depth;
581  boolean bigEndian, trueColour;
582  int redMax, greenMax, blueMax, redShift, greenShift, blueShift;
583
584  void readServerInit() throws IOException {
585    framebufferWidth = is.readUnsignedShort();
586    framebufferHeight = is.readUnsignedShort();
587    bitsPerPixel = is.readUnsignedByte();
588    depth = is.readUnsignedByte();
589    bigEndian = (is.readUnsignedByte() != 0);
590    trueColour = (is.readUnsignedByte() != 0);
591    redMax = is.readUnsignedShort();
592    greenMax = is.readUnsignedShort();
593    blueMax = is.readUnsignedShort();
594    redShift = is.readUnsignedByte();
595    greenShift = is.readUnsignedByte();
596    blueShift = is.readUnsignedByte();
597    byte[] pad = new byte[3];
598    readFully(pad);
599    int nameLength = is.readInt();
600    byte[] name = new byte[nameLength];
601    readFully(name);
602    desktopName = new String(name);
603
604    // Read interaction capabilities (TightVNC protocol extensions)
605    if (protocolTightVNC) {
606      int nServerMessageTypes = is.readUnsignedShort();
607      int nClientMessageTypes = is.readUnsignedShort();
608      int nEncodingTypes = is.readUnsignedShort();
609      is.readUnsignedShort();
610      readCapabilityList(serverMsgCaps, nServerMessageTypes);
611      readCapabilityList(clientMsgCaps, nClientMessageTypes);
612      readCapabilityList(encodingCaps, nEncodingTypes);
613    }
614
615    inNormalProtocol = true;
616  }
617
618
619  //
620  // Create session file and write initial protocol messages into it.
621  //
622
623  void startSession(String fname) throws IOException {
624    rec = new SessionRecorder(fname);
625    rec.writeHeader();
626    rec.write(versionMsg_3_3.getBytes());
627    rec.writeIntBE(SecTypeNone);
628    rec.writeShortBE(framebufferWidth);
629    rec.writeShortBE(framebufferHeight);
630    byte[] fbsServerInitMsg =   {
631      32, 24, 0, 1, 0,
632      (byte)0xFF, 0, (byte)0xFF, 0, (byte)0xFF,
633      16, 8, 0, 0, 0, 0
634    };
635    rec.write(fbsServerInitMsg);
636    rec.writeIntBE(desktopName.length());
637    rec.write(desktopName.getBytes());
638    numUpdatesInSession = 0;
639
640    // FIXME: If there were e.g. ZRLE updates only, that should not
641    //        affect recording of Zlib and Tight updates. So, actually
642    //        we should maintain separate flags for Zlib, ZRLE and
643    //        Tight, instead of one ``wereZlibUpdates'' variable.
644    //
645    if (wereZlibUpdates)
646      recordFromBeginning = false;
647
648    zlibWarningShown = false;
649    tightWarningShown = false;
650  }
651
652  //
653  // Close session file.
654  //
655
656  void closeSession() throws IOException {
657    if (rec != null) {
658      rec.close();
659      rec = null;
660    }
661  }
662
663
664  //
665  // Set new framebuffer size
666  //
667
668  void setFramebufferSize(int width, int height) {
669    framebufferWidth = width;
670    framebufferHeight = height;
671  }
672
673
674  //
675  // Read the server message type
676  //
677
678  int readServerMessageType() throws IOException {
679    int msgType = is.readUnsignedByte();
680
681    // If the session is being recorded:
682    if (rec != null) {
683      if (msgType == Bell) {    // Save Bell messages in session files.
684        rec.writeByte(msgType);
685        if (numUpdatesInSession > 0)
686          rec.flush();
687      }
688    }
689
690    return msgType;
691  }
692
693
694  //
695  // Read a FramebufferUpdate message
696  //
697
698  int updateNRects;
699
700  void readFramebufferUpdate() throws IOException {
701    is.readByte();
702    updateNRects = is.readUnsignedShort();
703
704    // If the session is being recorded:
705    if (rec != null) {
706      rec.writeByte(FramebufferUpdate);
707      rec.writeByte(0);
708      rec.writeShortBE(updateNRects);
709    }
710
711    numUpdatesInSession++;
712  }
713
714  // Read a FramebufferUpdate rectangle header
715
716  int updateRectX, updateRectY, updateRectW, updateRectH, updateRectEncoding;
717
718  void readFramebufferUpdateRectHdr() throws Exception {
719    updateRectX = is.readUnsignedShort();
720    updateRectY = is.readUnsignedShort();
721    updateRectW = is.readUnsignedShort();
722    updateRectH = is.readUnsignedShort();
723    updateRectEncoding = is.readInt();
724
725    if (updateRectEncoding == EncodingZlib ||
726        updateRectEncoding == EncodingZRLE ||
727        updateRectEncoding == EncodingTight)
728      wereZlibUpdates = true;
729
730    // If the session is being recorded:
731    if (rec != null) {
732      if (numUpdatesInSession > 1)
733        rec.flush();            // Flush the output on each rectangle.
734      rec.writeShortBE(updateRectX);
735      rec.writeShortBE(updateRectY);
736      rec.writeShortBE(updateRectW);
737      rec.writeShortBE(updateRectH);
738      if (updateRectEncoding == EncodingZlib && !recordFromBeginning) {
739        // Here we cannot write Zlib-encoded rectangles because the
740        // decoder won't be able to reproduce zlib stream state.
741        if (!zlibWarningShown) {
742          System.out.println("Warning: Raw encoding will be used " +
743                             "instead of Zlib in recorded session.");
744          zlibWarningShown = true;
745        }
746        rec.writeIntBE(EncodingRaw);
747      } else {
748        rec.writeIntBE(updateRectEncoding);
749        if (updateRectEncoding == EncodingTight && !recordFromBeginning &&
750            !tightWarningShown) {
751          System.out.println("Warning: Re-compressing Tight-encoded " +
752                             "updates for session recording.");
753          tightWarningShown = true;
754        }
755      }
756    }
757
758    if (updateRectEncoding < 0 || updateRectEncoding > MaxNormalEncoding)
759      return;
760
761    if (updateRectX + updateRectW > framebufferWidth ||
762        updateRectY + updateRectH > framebufferHeight) {
763      throw new Exception("Framebuffer update rectangle too large: " +
764                          updateRectW + "x" + updateRectH + " at (" +
765                          updateRectX + "," + updateRectY + ")");
766    }
767  }
768
769  // Read CopyRect source X and Y.
770
771  int copyRectSrcX, copyRectSrcY;
772
773  void readCopyRect() throws IOException {
774    copyRectSrcX = is.readUnsignedShort();
775    copyRectSrcY = is.readUnsignedShort();
776
777    // If the session is being recorded:
778    if (rec != null) {
779      rec.writeShortBE(copyRectSrcX);
780      rec.writeShortBE(copyRectSrcY);
781    }
782  }
783
784
785  //
786  // Read a ServerCutText message
787  //
788
789  String readServerCutText() throws IOException {
790    byte[] pad = new byte[3];
791    readFully(pad);
792    int len = is.readInt();
793    byte[] text = new byte[len];
794    readFully(text);
795    return new String(text);
796  }
797
798
799  //
800  // Read an integer in compact representation (1..3 bytes).
801  // Such format is used as a part of the Tight encoding.
802  // Also, this method records data if session recording is active and
803  // the viewer's recordFromBeginning variable is set to true.
804  //
805
806  int readCompactLen() throws IOException {
807    int[] portion = new int[3];
808    portion[0] = is.readUnsignedByte();
809    int byteCount = 1;
810    int len = portion[0] & 0x7F;
811    if ((portion[0] & 0x80) != 0) {
812      portion[1] = is.readUnsignedByte();
813      byteCount++;
814      len |= (portion[1] & 0x7F) << 7;
815      if ((portion[1] & 0x80) != 0) {
816        portion[2] = is.readUnsignedByte();
817        byteCount++;
818        len |= (portion[2] & 0xFF) << 14;
819      }
820    }
821
822    if (rec != null && recordFromBeginning)
823      for (int i = 0; i < byteCount; i++)
824        rec.writeByte(portion[i]);
825
826    return len;
827  }
828
829
830  //
831  // Write a FramebufferUpdateRequest message
832  //
833
834  void writeFramebufferUpdateRequest(int x, int y, int w, int h,
835                                     boolean incremental)
836       throws IOException
837  {
838    byte[] b = new byte[10];
839
840    b[0] = (byte) FramebufferUpdateRequest;
841    b[1] = (byte) (incremental ? 1 : 0);
842    b[2] = (byte) ((x >> 8) & 0xff);
843    b[3] = (byte) (x & 0xff);
844    b[4] = (byte) ((y >> 8) & 0xff);
845    b[5] = (byte) (y & 0xff);
846    b[6] = (byte) ((w >> 8) & 0xff);
847    b[7] = (byte) (w & 0xff);
848    b[8] = (byte) ((h >> 8) & 0xff);
849    b[9] = (byte) (h & 0xff);
850
851    os.write(b);
852  }
853
854
855  //
856  // Write a SetPixelFormat message
857  //
858
859  void writeSetPixelFormat(int bitsPerPixel, int depth, boolean bigEndian,
860                           boolean trueColour,
861                           int redMax, int greenMax, int blueMax,
862                           int redShift, int greenShift, int blueShift)
863       throws IOException
864  {
865    byte[] b = new byte[20];
866
867    b[0]  = (byte) SetPixelFormat;
868    b[4]  = (byte) bitsPerPixel;
869    b[5]  = (byte) depth;
870    b[6]  = (byte) (bigEndian ? 1 : 0);
871    b[7]  = (byte) (trueColour ? 1 : 0);
872    b[8]  = (byte) ((redMax >> 8) & 0xff);
873    b[9]  = (byte) (redMax & 0xff);
874    b[10] = (byte) ((greenMax >> 8) & 0xff);
875    b[11] = (byte) (greenMax & 0xff);
876    b[12] = (byte) ((blueMax >> 8) & 0xff);
877    b[13] = (byte) (blueMax & 0xff);
878    b[14] = (byte) redShift;
879    b[15] = (byte) greenShift;
880    b[16] = (byte) blueShift;
881
882    os.write(b);
883  }
884
885
886  //
887  // Write a FixColourMapEntries message.  The values in the red, green and
888  // blue arrays are from 0 to 65535.
889  //
890
891  void writeFixColourMapEntries(int firstColour, int nColours,
892                                int[] red, int[] green, int[] blue)
893       throws IOException
894  {
895    byte[] b = new byte[6 + nColours * 6];
896
897    b[0] = (byte) FixColourMapEntries;
898    b[2] = (byte) ((firstColour >> 8) & 0xff);
899    b[3] = (byte) (firstColour & 0xff);
900    b[4] = (byte) ((nColours >> 8) & 0xff);
901    b[5] = (byte) (nColours & 0xff);
902
903    for (int i = 0; i < nColours; i++) {
904      b[6 + i * 6]     = (byte) ((red[i] >> 8) & 0xff);
905      b[6 + i * 6 + 1] = (byte) (red[i] & 0xff);
906      b[6 + i * 6 + 2] = (byte) ((green[i] >> 8) & 0xff);
907      b[6 + i * 6 + 3] = (byte) (green[i] & 0xff);
908      b[6 + i * 6 + 4] = (byte) ((blue[i] >> 8) & 0xff);
909      b[6 + i * 6 + 5] = (byte) (blue[i] & 0xff);
910    }
911 
912    os.write(b);
913  }
914
915
916  //
917  // Write a SetEncodings message
918  //
919
920  void writeSetEncodings(int[] encs, int len) throws IOException {
921    byte[] b = new byte[4 + 4 * len];
922
923    b[0] = (byte) SetEncodings;
924    b[2] = (byte) ((len >> 8) & 0xff);
925    b[3] = (byte) (len & 0xff);
926
927    for (int i = 0; i < len; i++) {
928      b[4 + 4 * i] = (byte) ((encs[i] >> 24) & 0xff);
929      b[5 + 4 * i] = (byte) ((encs[i] >> 16) & 0xff);
930      b[6 + 4 * i] = (byte) ((encs[i] >> 8) & 0xff);
931      b[7 + 4 * i] = (byte) (encs[i] & 0xff);
932    }
933
934    os.write(b);
935  }
936
937
938  //
939  // Write a ClientCutText message
940  //
941
942  void writeClientCutText(String text) throws IOException {
943    byte[] b = new byte[8 + text.length()];
944
945    b[0] = (byte) ClientCutText;
946    b[4] = (byte) ((text.length() >> 24) & 0xff);
947    b[5] = (byte) ((text.length() >> 16) & 0xff);
948    b[6] = (byte) ((text.length() >> 8) & 0xff);
949    b[7] = (byte) (text.length() & 0xff);
950
951    System.arraycopy(text.getBytes(), 0, b, 8, text.length());
952
953    os.write(b);
954  }
955
956
957  //
958  // A buffer for putting pointer and keyboard events before being sent.  This
959  // is to ensure that multiple RFB events generated from a single Java Event
960  // will all be sent in a single network packet.  The maximum possible
961  // length is 4 modifier down events, a single key event followed by 4
962  // modifier up events i.e. 9 key events or 72 bytes.
963  //
964
965  byte[] eventBuf = new byte[72];
966  int eventBufLen;
967
968
969  // Useful shortcuts for modifier masks.
970
971  final static int CTRL_MASK  = InputEvent.CTRL_MASK;
972  final static int SHIFT_MASK = InputEvent.SHIFT_MASK;
973  final static int META_MASK  = InputEvent.META_MASK;
974  final static int ALT_MASK   = InputEvent.ALT_MASK;
975
976
977  //
978  // Write a pointer event message.  We may need to send modifier key events
979  // around it to set the correct modifier state.
980  //
981
982  int pointerMask = 0;
983
984  void writePointerEvent(MouseEvent evt) throws IOException {
985    int modifiers = evt.getModifiers();
986
987    int mask2 = 2;
988    int mask3 = 4;
989    if (viewer.options.reverseMouseButtons2And3) {
990      mask2 = 4;
991      mask3 = 2;
992    }
993
994    // Note: For some reason, AWT does not set BUTTON1_MASK on left
995    // button presses. Here we think that it was the left button if
996    // modifiers do not include BUTTON2_MASK or BUTTON3_MASK.
997
998    if (evt.getID() == MouseEvent.MOUSE_PRESSED) {
999      if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {
1000        pointerMask = mask2;
1001        modifiers &= ~ALT_MASK;
1002      } else if ((modifiers & InputEvent.BUTTON3_MASK) != 0) {
1003        pointerMask = mask3;
1004        modifiers &= ~META_MASK;
1005      } else {
1006        pointerMask = 1;
1007      }
1008    } else if (evt.getID() == MouseEvent.MOUSE_RELEASED) {
1009      pointerMask = 0;
1010      if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {
1011        modifiers &= ~ALT_MASK;
1012      } else if ((modifiers & InputEvent.BUTTON3_MASK) != 0) {
1013        modifiers &= ~META_MASK;
1014      }
1015    }
1016
1017    eventBufLen = 0;
1018    writeModifierKeyEvents(modifiers);
1019
1020    int x = evt.getX();
1021    int y = evt.getY();
1022
1023    if (x < 0) x = 0;
1024    if (y < 0) y = 0;
1025
1026    eventBuf[eventBufLen++] = (byte) PointerEvent;
1027    eventBuf[eventBufLen++] = (byte) pointerMask;
1028    eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff);
1029    eventBuf[eventBufLen++] = (byte) (x & 0xff);
1030    eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff);
1031    eventBuf[eventBufLen++] = (byte) (y & 0xff);
1032
1033    //
1034    // Always release all modifiers after an "up" event
1035    //
1036
1037    if (pointerMask == 0) {
1038      writeModifierKeyEvents(0);
1039    }
1040
1041    os.write(eventBuf, 0, eventBufLen);
1042  }
1043
1044
1045  //
1046  // Write a key event message.  We may need to send modifier key events
1047  // around it to set the correct modifier state.  Also we need to translate
1048  // from the Java key values to the X keysym values used by the RFB protocol.
1049  //
1050
1051  void writeKeyEvent(KeyEvent evt) throws IOException {
1052
1053    int keyChar = evt.getKeyChar();
1054
1055    //
1056    // Ignore event if only modifiers were pressed.
1057    //
1058
1059    // Some JVMs return 0 instead of CHAR_UNDEFINED in getKeyChar().
1060    if (keyChar == 0)
1061      keyChar = KeyEvent.CHAR_UNDEFINED;
1062
1063    if (keyChar == KeyEvent.CHAR_UNDEFINED) {
1064      int code = evt.getKeyCode();
1065      if (code == KeyEvent.VK_CONTROL || code == KeyEvent.VK_SHIFT ||
1066          code == KeyEvent.VK_META || code == KeyEvent.VK_ALT)
1067        return;
1068    }
1069
1070    //
1071    // Key press or key release?
1072    //
1073
1074    boolean down = (evt.getID() == KeyEvent.KEY_PRESSED);
1075
1076    int key;
1077    if (evt.isActionKey()) {
1078
1079      //
1080      // An action key should be one of the following.
1081      // If not then just ignore the event.
1082      //
1083
1084      switch(evt.getKeyCode()) {
1085      case KeyEvent.VK_HOME:      key = 0xff50; break;
1086      case KeyEvent.VK_LEFT:      key = 0xff51; break;
1087      case KeyEvent.VK_UP:        key = 0xff52; break;
1088      case KeyEvent.VK_RIGHT:     key = 0xff53; break;
1089      case KeyEvent.VK_DOWN:      key = 0xff54; break;
1090      case KeyEvent.VK_PAGE_UP:   key = 0xff55; break;
1091      case KeyEvent.VK_PAGE_DOWN: key = 0xff56; break;
1092      case KeyEvent.VK_END:       key = 0xff57; break;
1093      case KeyEvent.VK_INSERT:    key = 0xff63; break;
1094      case KeyEvent.VK_F1:        key = 0xffbe; break;
1095      case KeyEvent.VK_F2:        key = 0xffbf; break;
1096      case KeyEvent.VK_F3:        key = 0xffc0; break;
1097      case KeyEvent.VK_F4:        key = 0xffc1; break;
1098      case KeyEvent.VK_F5:        key = 0xffc2; break;
1099      case KeyEvent.VK_F6:        key = 0xffc3; break;
1100      case KeyEvent.VK_F7:        key = 0xffc4; break;
1101      case KeyEvent.VK_F8:        key = 0xffc5; break;
1102      case KeyEvent.VK_F9:        key = 0xffc6; break;
1103      case KeyEvent.VK_F10:       key = 0xffc7; break;
1104      case KeyEvent.VK_F11:       key = 0xffc8; break;
1105      case KeyEvent.VK_F12:       key = 0xffc9; break;
1106      default:
1107        return;
1108      }
1109
1110    } else {
1111
1112      //
1113      // A "normal" key press.  Ordinary ASCII characters go straight through.
1114      // For CTRL-<letter>, CTRL is sent separately so just send <letter>.
1115      // Backspace, tab, return, escape and delete have special keysyms.
1116      // Anything else we ignore.
1117      //
1118
1119      key = keyChar;
1120
1121      if (key < 0x20) {
1122        if (evt.isControlDown()) {
1123          key += 0x60;
1124        } else {
1125          switch(key) {
1126          case KeyEvent.VK_BACK_SPACE: key = 0xff08; break;
1127          case KeyEvent.VK_TAB:        key = 0xff09; break;
1128          case KeyEvent.VK_ENTER:      key = 0xff0d; break;
1129          case KeyEvent.VK_ESCAPE:     key = 0xff1b; break;
1130          }
1131        }
1132      } else if (key == 0x7f) {
1133        // Delete
1134        key = 0xffff;
1135      } else if (key > 0xff) {
1136        // JDK1.1 on X incorrectly passes some keysyms straight through,
1137        // so we do too.  JDK1.1.4 seems to have fixed this.
1138        // The keysyms passed are 0xff00 .. XK_BackSpace .. XK_Delete
1139        // Also, we pass through foreign currency keysyms (0x20a0..0x20af).
1140        if ((key < 0xff00 || key > 0xffff) &&
1141            !(key >= 0x20a0 && key <= 0x20af))
1142          return;
1143      }
1144    }
1145
1146    // Fake keyPresses for keys that only generates keyRelease events
1147    if ((key == 0xe5) || (key == 0xc5) || // XK_aring / XK_Aring
1148        (key == 0xe4) || (key == 0xc4) || // XK_adiaeresis / XK_Adiaeresis
1149        (key == 0xf6) || (key == 0xd6) || // XK_odiaeresis / XK_Odiaeresis
1150        (key == 0xa7) || (key == 0xbd) || // XK_section / XK_onehalf
1151        (key == 0xa3)) {                  // XK_sterling
1152      // Make sure we do not send keypress events twice on platforms
1153      // with correct JVMs (those that actually report KeyPress for all
1154      // keys) 
1155      if (down)
1156        brokenKeyPressed = true;
1157
1158      if (!down && !brokenKeyPressed) {
1159        // We've got a release event for this key, but haven't received
1160        // a press. Fake it.
1161        eventBufLen = 0;
1162        writeModifierKeyEvents(evt.getModifiers());
1163        writeKeyEvent(key, true);
1164        os.write(eventBuf, 0, eventBufLen);
1165      }
1166
1167      if (!down)
1168        brokenKeyPressed = false; 
1169    }
1170
1171    eventBufLen = 0;
1172    writeModifierKeyEvents(evt.getModifiers());
1173    writeKeyEvent(key, down);
1174
1175    // Always release all modifiers after an "up" event
1176    if (!down)
1177      writeModifierKeyEvents(0);
1178
1179    os.write(eventBuf, 0, eventBufLen);
1180  }
1181
1182
1183  //
1184  // Add a raw key event with the given X keysym to eventBuf.
1185  //
1186
1187  void writeKeyEvent(int keysym, boolean down) {
1188    eventBuf[eventBufLen++] = (byte) KeyboardEvent;
1189    eventBuf[eventBufLen++] = (byte) (down ? 1 : 0);
1190    eventBuf[eventBufLen++] = (byte) 0;
1191    eventBuf[eventBufLen++] = (byte) 0;
1192    eventBuf[eventBufLen++] = (byte) ((keysym >> 24) & 0xff);
1193    eventBuf[eventBufLen++] = (byte) ((keysym >> 16) & 0xff);
1194    eventBuf[eventBufLen++] = (byte) ((keysym >> 8) & 0xff);
1195    eventBuf[eventBufLen++] = (byte) (keysym & 0xff);
1196  }
1197
1198
1199  //
1200  // Write key events to set the correct modifier state.
1201  //
1202
1203  int oldModifiers = 0;
1204
1205  void writeModifierKeyEvents(int newModifiers) {
1206    if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK))
1207      writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0);
1208
1209    if ((newModifiers & SHIFT_MASK) != (oldModifiers & SHIFT_MASK))
1210      writeKeyEvent(0xffe1, (newModifiers & SHIFT_MASK) != 0);
1211
1212    if ((newModifiers & META_MASK) != (oldModifiers & META_MASK))
1213      writeKeyEvent(0xffe7, (newModifiers & META_MASK) != 0);
1214
1215    if ((newModifiers & ALT_MASK) != (oldModifiers & ALT_MASK))
1216      writeKeyEvent(0xffe9, (newModifiers & ALT_MASK) != 0);
1217
1218    oldModifiers = newModifiers;
1219  }
1220
1221
1222  //
1223  // Compress and write the data into the recorded session file. This
1224  // method assumes the recording is on (rec != null).
1225  //
1226
1227  void recordCompressedData(byte[] data, int off, int len) throws IOException {
1228    Deflater deflater = new Deflater();
1229    deflater.setInput(data, off, len);
1230    int bufSize = len + len / 100 + 12;
1231    byte[] buf = new byte[bufSize];
1232    deflater.finish();
1233    int compressedSize = deflater.deflate(buf);
1234    recordCompactLen(compressedSize);
1235    rec.write(buf, 0, compressedSize);
1236  }
1237
1238  void recordCompressedData(byte[] data) throws IOException {
1239    recordCompressedData(data, 0, data.length);
1240  }
1241
1242  //
1243  // Write an integer in compact representation (1..3 bytes) into the
1244  // recorded session file. This method assumes the recording is on
1245  // (rec != null).
1246  //
1247
1248  void recordCompactLen(int len) throws IOException {
1249    byte[] buf = new byte[3];
1250    int bytes = 0;
1251    buf[bytes++] = (byte)(len & 0x7F);
1252    if (len > 0x7F) {
1253      buf[bytes-1] |= 0x80;
1254      buf[bytes++] = (byte)(len >> 7 & 0x7F);
1255      if (len > 0x3FFF) {
1256        buf[bytes-1] |= 0x80;
1257        buf[bytes++] = (byte)(len >> 14 & 0xFF);
1258      }
1259    }
1260    rec.write(buf, 0, bytes);
1261  }
1262
1263  public void startTiming() {
1264    timing = true;
1265
1266    // Carry over up to 1s worth of previous rate for smoothing.
1267
1268    if (timeWaitedIn100us > 10000) {
1269      timedKbits = timedKbits * 10000 / timeWaitedIn100us;
1270      timeWaitedIn100us = 10000;
1271    }
1272  }
1273
1274  public void stopTiming() {
1275    timing = false; 
1276    if (timeWaitedIn100us < timedKbits/2)
1277      timeWaitedIn100us = timedKbits/2; // upper limit 20Mbit/s
1278  }
1279
1280  public long kbitsPerSecond() {
1281    return timedKbits * 10000 / timeWaitedIn100us;
1282  }
1283
1284  public long timeWaited() {
1285    return timeWaitedIn100us;
1286  }
1287
1288  public void readFully(byte b[]) throws IOException {
1289    readFully(b, 0, b.length);
1290  }
1291
1292  public void readFully(byte b[], int off, int len) throws IOException {
1293    long before = 0;
1294    if (timing)
1295      before = System.currentTimeMillis();
1296
1297    is.readFully(b, off, len);
1298
1299    if (timing) {
1300      long after = System.currentTimeMillis();
1301      long newTimeWaited = (after - before) * 10;
1302      int newKbits = len * 8 / 1000;
1303
1304      // limit rate to between 10kbit/s and 40Mbit/s
1305
1306      if (newTimeWaited > newKbits*1000) newTimeWaited = newKbits*1000;
1307      if (newTimeWaited < newKbits/4)    newTimeWaited = newKbits/4;
1308
1309      timeWaitedIn100us += newTimeWaited;
1310      timedKbits += newKbits;
1311    }
1312  }
1313
1314}
Note: See TracBrowser for help on using the repository browser.