diff options
Diffstat (limited to 'libvncclient')
-rw-r--r-- | libvncclient/Makefile | 17 | ||||
-rw-r--r-- | libvncclient/corre.c | 70 | ||||
-rw-r--r-- | libvncclient/cursor.c | 175 | ||||
-rw-r--r-- | libvncclient/hextile.c | 120 | ||||
-rw-r--r-- | libvncclient/listen.c | 94 | ||||
-rw-r--r-- | libvncclient/rfbproto.c | 1052 | ||||
-rw-r--r-- | libvncclient/rre.c | 68 | ||||
-rw-r--r-- | libvncclient/sockets.c | 424 | ||||
-rw-r--r-- | libvncclient/tight.c | 606 | ||||
-rw-r--r-- | libvncclient/vncviewer.c | 240 | ||||
-rw-r--r-- | libvncclient/zlib.c | 158 |
11 files changed, 3024 insertions, 0 deletions
diff --git a/libvncclient/Makefile b/libvncclient/Makefile new file mode 100644 index 0000000..026b5b2 --- /dev/null +++ b/libvncclient/Makefile @@ -0,0 +1,17 @@ +CFLAGS=-g -I.. -I. -Wall +LDLIBS=-lz -ljpeg + +OBJS=cursor.o listen.o rfbproto.o sockets.o vncviewer.o ../libvncserver.a + +all: libvncclient.a + +rfbproto.o: rfbproto.c corre.c hextile.c rre.c tight.c zlib.c + +$(OBJS): ../rfb/rfbclient.h + +libvncclient.a: $(OBJS) + $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) + +clean: + rm *.o *.a + diff --git a/libvncclient/corre.c b/libvncclient/corre.c new file mode 100644 index 0000000..d6d3d0d --- /dev/null +++ b/libvncclient/corre.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * corre.c - handle CoRRE encoding. + * + * This file shouldn't be compiled directly. It is included multiple times by + * rfbproto.c, each time with a different definition of the macro BPP. For + * each value of BPP, this file defines a function which handles a CoRRE + * encoded rectangle with BPP bits per pixel. + */ + +#define HandleCoRREBPP CONCAT2E(HandleCoRRE,BPP) +#define CARDBPP CONCAT3E(uint,BPP,_t) + +static Bool +HandleCoRREBPP (rfbClient* client, int rx, int ry, int rw, int rh) +{ + rfbRREHeader hdr; + int i; + CARDBPP pix; + uint8_t *ptr; + int x, y, w, h; + + if (!ReadFromRFBServer(client, (char *)&hdr, sz_rfbRREHeader)) + return FALSE; + + hdr.nSubrects = Swap32IfLE(hdr.nSubrects); + + if (!ReadFromRFBServer(client, (char *)&pix, sizeof(pix))) + return FALSE; + + FillRectangle(client, rx, ry, rw, rh, pix); + + if (!ReadFromRFBServer(client, client->buffer, hdr.nSubrects * (4 + (BPP / 8)))) + return FALSE; + + ptr = (uint8_t *)client->buffer; + + for (i = 0; i < hdr.nSubrects; i++) { + pix = *(CARDBPP *)ptr; + ptr += BPP/8; + x = *ptr++; + y = *ptr++; + w = *ptr++; + h = *ptr++; + + FillRectangle(client, rx+x, ry+y, w, h, pix); + } + + return TRUE; +} + +#undef CARDBPP diff --git a/libvncclient/cursor.c b/libvncclient/cursor.c new file mode 100644 index 0000000..f6a7816 --- /dev/null +++ b/libvncclient/cursor.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2001,2002 Constantin Kaplinsky. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * cursor.c - code to support cursor shape updates (XCursor and + * RichCursor preudo-encodings). + */ + +#include <rfb/rfbclient.h> + + +#define OPER_SAVE 0 +#define OPER_RESTORE 1 + +#define RGB24_TO_PIXEL(bpp,r,g,b) \ + ((((uint##bpp##_t)(r) & 0xFF) * client->format.redMax + 127) / 255 \ + << client->format.redShift | \ + (((uint##bpp##_t)(g) & 0xFF) * client->format.greenMax + 127) / 255 \ + << client->format.greenShift | \ + (((uint##bpp##_t)(b) & 0xFF) * client->format.blueMax + 127) / 255 \ + << client->format.blueShift) + + +/********************************************************************* + * HandleCursorShape(). Support for XCursor and RichCursor shape + * updates. We emulate cursor operating on the frame buffer (that is + * why we call it "software cursor"). + ********************************************************************/ + +Bool HandleCursorShape(rfbClient* client,int xhot, int yhot, int width, int height, uint32_t enc) +{ + int bytesPerPixel; + size_t bytesPerRow, bytesMaskData; + rfbXCursorColors rgb; + uint32_t colors[2]; + char *buf; + uint8_t *ptr; + int x, y, b; + + bytesPerPixel = client->format.bitsPerPixel / 8; + bytesPerRow = (width + 7) / 8; + bytesMaskData = bytesPerRow * height; + + if (width * height == 0) + return TRUE; + + /* Allocate memory for pixel data and temporary mask data. */ + if(client->rcSource) + free(client->rcSource); + + client->rcSource = malloc(width * height * bytesPerPixel); + if (client->rcSource == NULL) + return FALSE; + + buf = malloc(bytesMaskData); + if (buf == NULL) { + free(client->rcSource); + client->rcSource = NULL; + return FALSE; + } + + /* Read and decode cursor pixel data, depending on the encoding type. */ + + if (enc == rfbEncodingXCursor) { + /* Read and convert background and foreground colors. */ + if (!ReadFromRFBServer(client, (char *)&rgb, sz_rfbXCursorColors)) { + free(client->rcSource); + client->rcSource = NULL; + free(buf); + return FALSE; + } + colors[0] = RGB24_TO_PIXEL(32, rgb.backRed, rgb.backGreen, rgb.backBlue); + colors[1] = RGB24_TO_PIXEL(32, rgb.foreRed, rgb.foreGreen, rgb.foreBlue); + + /* Read 1bpp pixel data into a temporary buffer. */ + if (!ReadFromRFBServer(client, buf, bytesMaskData)) { + free(client->rcSource); + client->rcSource = NULL; + free(buf); + return FALSE; + } + + /* Convert 1bpp data to byte-wide color indices. */ + ptr = client->rcSource; + for (y = 0; y < height; y++) { + for (x = 0; x < width / 8; x++) { + for (b = 7; b >= 0; b--) { + *ptr = buf[y * bytesPerRow + x] >> b & 1; + ptr += bytesPerPixel; + } + } + for (b = 7; b > 7 - width % 8; b--) { + *ptr = buf[y * bytesPerRow + x] >> b & 1; + ptr += bytesPerPixel; + } + } + + /* Convert indices into the actual pixel values. */ + switch (bytesPerPixel) { + case 1: + for (x = 0; x < width * height; x++) + client->rcSource[x] = (uint8_t)colors[client->rcSource[x]]; + break; + case 2: + for (x = 0; x < width * height; x++) + ((uint16_t *)client->rcSource)[x] = (uint16_t)colors[client->rcSource[x * 2]]; + break; + case 4: + for (x = 0; x < width * height; x++) + ((uint32_t *)client->rcSource)[x] = colors[client->rcSource[x * 4]]; + break; + } + + } else { /* enc == rfbEncodingRichCursor */ + + if (!ReadFromRFBServer(client, (char *)client->rcSource, width * height * bytesPerPixel)) { + free(client->rcSource); + client->rcSource = NULL; + free(buf); + return FALSE; + } + + } + + /* Read and decode mask data. */ + + if (!ReadFromRFBServer(client, buf, bytesMaskData)) { + free(client->rcSource); + client->rcSource = NULL; + free(buf); + return FALSE; + } + + client->rcMask = malloc(width * height); + if (client->rcMask == NULL) { + free(client->rcSource); + client->rcSource = NULL; + free(buf); + return FALSE; + } + + ptr = client->rcMask; + for (y = 0; y < height; y++) { + for (x = 0; x < width / 8; x++) { + for (b = 7; b >= 0; b--) { + *ptr++ = buf[y * bytesPerRow + x] >> b & 1; + } + } + for (b = 7; b > 7 - width % 8; b--) { + *ptr++ = buf[y * bytesPerRow + x] >> b & 1; + } + } + + free(buf); + + return TRUE; +} + + diff --git a/libvncclient/hextile.c b/libvncclient/hextile.c new file mode 100644 index 0000000..e09fbc9 --- /dev/null +++ b/libvncclient/hextile.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * hextile.c - handle hextile encoding. + * + * This file shouldn't be compiled directly. It is included multiple times by + * rfbproto.c, each time with a different definition of the macro BPP. For + * each value of BPP, this file defines a function which handles a hextile + * encoded rectangle with BPP bits per pixel. + */ + +#define HandleHextileBPP CONCAT2E(HandleHextile,BPP) +#define CARDBPP CONCAT3E(uint,BPP,_t) +#define GET_PIXEL CONCAT2E(GET_PIXEL,BPP) + +static Bool +HandleHextileBPP (rfbClient* client, int rx, int ry, int rw, int rh) +{ + CARDBPP bg, fg; + int i; + uint8_t *ptr; + int x, y, w, h; + int sx, sy, sw, sh; + uint8_t subencoding; + uint8_t nSubrects; + + for (y = ry; y < ry+rh; y += 16) { + for (x = rx; x < rx+rw; x += 16) { + w = h = 16; + if (rx+rw - x < 16) + w = rx+rw - x; + if (ry+rh - y < 16) + h = ry+rh - y; + + if (!ReadFromRFBServer(client, (char *)&subencoding, 1)) + return FALSE; + + if (subencoding & rfbHextileRaw) { + if (!ReadFromRFBServer(client, client->buffer, w * h * (BPP / 8))) + return FALSE; + + CopyRectangle(client, client->buffer, x, y, w, h); + + continue; + } + + if (subencoding & rfbHextileBackgroundSpecified) + if (!ReadFromRFBServer(client, (char *)&bg, sizeof(bg))) + return FALSE; + + FillRectangle(client, x, y, w, h, fg); + + if (subencoding & rfbHextileForegroundSpecified) + if (!ReadFromRFBServer(client, (char *)&fg, sizeof(fg))) + return FALSE; + + if (!(subencoding & rfbHextileAnySubrects)) { + continue; + } + + if (!ReadFromRFBServer(client, (char *)&nSubrects, 1)) + return FALSE; + + ptr = (uint8_t*)client->buffer; + + if (subencoding & rfbHextileSubrectsColoured) { + if (!ReadFromRFBServer(client, client->buffer, nSubrects * (2 + (BPP / 8)))) + return FALSE; + + for (i = 0; i < nSubrects; i++) { + GET_PIXEL(fg, ptr); + sx = rfbHextileExtractX(*ptr); + sy = rfbHextileExtractY(*ptr); + ptr++; + sw = rfbHextileExtractW(*ptr); + sh = rfbHextileExtractH(*ptr); + ptr++; + + FillRectangle(client, x+sx, y+sy, sw, sh, fg); + } + + } else { + if (!ReadFromRFBServer(client, client->buffer, nSubrects * 2)) + return FALSE; + + for (i = 0; i < nSubrects; i++) { + sx = rfbHextileExtractX(*ptr); + sy = rfbHextileExtractY(*ptr); + ptr++; + sw = rfbHextileExtractW(*ptr); + sh = rfbHextileExtractH(*ptr); + ptr++; + + FillRectangle(client, x+sx, y+sy, sw, sh, fg); + } + } + } + } + + return TRUE; +} + +#undef CARDBPP diff --git a/libvncclient/listen.c b/libvncclient/listen.c new file mode 100644 index 0000000..c11aef7 --- /dev/null +++ b/libvncclient/listen.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * listen.c - listen for incoming connections + */ + +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/time.h> +#include <sys/utsname.h> +#include <rfb/rfbclient.h> + +/* + * listenForIncomingConnections() - listen for incoming connections from + * servers, and fork a new process to deal with each connection. + */ + +void +listenForIncomingConnections(rfbClient* client) +{ + int listenSocket; + fd_set fds; + + client->listenSpecified = TRUE; + + listenSocket = ListenAtTcpPort(client->listenPort); + + if ((listenSocket < 0)) exit(1); + + fprintf(stderr,"%s -listen: Listening on port %d\n", + client->programName,client->listenPort); + fprintf(stderr,"%s -listen: Command line errors are not reported until " + "a connection comes in.\n", client->programName); + + while (TRUE) { + + /* reap any zombies */ + int status, pid; + while ((pid= wait3(&status, WNOHANG, (struct rusage *)0))>0); + + /* TODO: callback for discard any events (like X11 events) */ + + FD_ZERO(&fds); + + FD_SET(listenSocket, &fds); + + select(FD_SETSIZE, &fds, NULL, NULL, NULL); + + if (FD_ISSET(listenSocket, &fds)) { + client->sock = AcceptTcpConnection(listenSocket); + if (client->sock < 0) exit(1); + if (!SetNonBlocking(client->sock)) exit(1); + + /* Now fork off a new process to deal with it... */ + + switch (fork()) { + + case -1: + perror("fork"); + exit(1); + + case 0: + /* child - return to caller */ + close(listenSocket); + return; + + default: + /* parent - go round and listen again */ + close(client->sock); + break; + } + } + } +} + + diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c new file mode 100644 index 0000000..e654fcc --- /dev/null +++ b/libvncclient/rfbproto.c @@ -0,0 +1,1052 @@ +/* + * Copyright (C) 2000-2002 Constantin Kaplinsky. All Rights Reserved. + * Copyright (C) 2000 Tridia Corporation. All Rights Reserved. + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * rfbproto.c - functions to deal with client side of RFB protocol. + */ + +#include <unistd.h> +#include <errno.h> +#include <pwd.h> +#include <rfb/rfbclient.h> +#ifdef HAVE_LIBZ +#include <zlib.h> +#endif +#ifdef HAVE_LIBJPEG +#include <jpeglib.h> +#endif + +void FillRectangle(rfbClient* client, int x, int y, int w, int h, uint32_t colour) { + int i,j; + +#define FILL_RECT(BPP) \ + for(j=y*client->width;j<(y+h)*client->width;j+=client->width) \ + for(i=x;i<x+w;i++) \ + ((uint##BPP##_t*)client->frameBuffer)[j+i]=colour; + + switch(client->format.bitsPerPixel) { + case 8: FILL_RECT(8); break; + case 16: FILL_RECT(16); break; + case 32: FILL_RECT(32); break; + default: + fprintf(stderr,"Unsupported bitsPerPixel: %d\n",client->format.bitsPerPixel); + } +} + +void CopyRectangle(rfbClient* client, uint8_t* buffer, int x, int y, int w, int h) { + int i,j; + +#define COPY_RECT(BPP) \ + { \ + uint##BPP##_t* _buffer=(uint##BPP##_t*)buffer; \ + for(j=y*client->width;j<(y+h)*client->width;j+=client->width) { \ + for(i=x;i<x+w;i++,_buffer++) \ + ((uint##BPP##_t*)client->frameBuffer)[j+i]=*_buffer; \ + } \ + } + + switch(client->format.bitsPerPixel) { + case 8: COPY_RECT(8); break; + case 16: COPY_RECT(16); break; + case 32: COPY_RECT(32); break; + default: + fprintf(stderr,"Unsupported bitsPerPixel: %d\n",client->format.bitsPerPixel); + } +} + +/* TODO: test */ +void CopyRectangleFromRectangle(rfbClient* client, int src_x, int src_y, int w, int h, int dest_x, int dest_y) { + int i,j; + +#define COPY_RECT_FROM_RECT(BPP) \ + { \ + uint##BPP##_t* _buffer=((uint##BPP##_t*)client->frameBuffer)+(src_y-dest_y)*client->width+src_x-dest_x; \ + for(j=dest_y*client->width;j<(dest_y+h)*client->width;j+=client->width) { \ + for(i=dest_x;i<dest_x+w;i++) \ + ((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \ + } \ + } + + switch(client->format.bitsPerPixel) { + case 8: COPY_RECT_FROM_RECT(8); break; + case 16: COPY_RECT_FROM_RECT(16); break; + case 32: COPY_RECT_FROM_RECT(32); break; + default: + fprintf(stderr,"Unsupported bitsPerPixel: %d\n",client->format.bitsPerPixel); + } +} + +static Bool HandleRRE8(rfbClient* client, int rx, int ry, int rw, int rh); +static Bool HandleRRE16(rfbClient* client, int rx, int ry, int rw, int rh); +static Bool HandleRRE32(rfbClient* client, int rx, int ry, int rw, int rh); +static Bool HandleCoRRE8(rfbClient* client, int rx, int ry, int rw, int rh); +static Bool HandleCoRRE16(rfbClient* client, int rx, int ry, int rw, int rh); +static Bool HandleCoRRE32(rfbClient* client, int rx, int ry, int rw, int rh); +static Bool HandleHextile8(rfbClient* client, int rx, int ry, int rw, int rh); +static Bool HandleHextile16(rfbClient* client, int rx, int ry, int rw, int rh); +static Bool HandleHextile32(rfbClient* client, int rx, int ry, int rw, int rh); +static Bool HandleZlib8(rfbClient* client, int rx, int ry, int rw, int rh); +static Bool HandleZlib16(rfbClient* client, int rx, int ry, int rw, int rh); +static Bool HandleZlib32(rfbClient* client, int rx, int ry, int rw, int rh); +static Bool HandleTight8(rfbClient* client, int rx, int ry, int rw, int rh); +static Bool HandleTight16(rfbClient* client, int rx, int ry, int rw, int rh); +static Bool HandleTight32(rfbClient* client, int rx, int ry, int rw, int rh); + +static long ReadCompactLen (rfbClient* client); + +static void JpegInitSource(j_decompress_ptr cinfo); +static boolean JpegFillInputBuffer(j_decompress_ptr cinfo); +static void JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes); +static void JpegTermSource(j_decompress_ptr cinfo); +static void JpegSetSrcManager(j_decompress_ptr cinfo, uint8_t *compressedData, + int compressedLen); + +/* The zlib encoding requires expansion/decompression/deflation of the + compressed data in the "buffer" above into another, result buffer. + However, the size of the result buffer can be determined precisely + based on the bitsPerPixel, height and width of the rectangle. We + allocate this buffer one time to be the full size of the buffer. */ + +static int raw_buffer_size = -1; +static char *raw_buffer; + +static z_stream decompStream; +static Bool decompStreamInited = FALSE; + + +/* + * Variables for the ``tight'' encoding implementation. + */ + +/* Separate buffer for compressed data. */ +#define ZLIB_BUFFER_SIZE 512 +static char zlib_buffer[ZLIB_BUFFER_SIZE]; + +/* Four independent compression streams for zlib library. */ +static z_stream zlibStream[4]; +static Bool zlibStreamActive[4] = { + FALSE, FALSE, FALSE, FALSE +}; + +/* Filter stuff. Should be initialized by filter initialization code. */ +static Bool cutZeros; +static int rectWidth, rectColors; +static char tightPalette[256*4]; +static uint8_t tightPrevRow[2048*3*sizeof(uint16_t)]; + +/* JPEG decoder state. */ +static Bool jpegError; + + +/* + * ConnectToRFBServer. + */ + +Bool +ConnectToRFBServer(rfbClient* client,const char *hostname, int port) +{ + unsigned int host; + + if (!StringToIPAddr(hostname, &host)) { + fprintf(stderr,"Couldn't convert '%s' to host address\n", hostname); + return FALSE; + } + + client->sock = ConnectToTcpAddr(host, port); + + if (client->sock < 0) { + fprintf(stderr,"Unable to connect to VNC server\n"); + return FALSE; + } + + return SetNonBlocking(client->sock); +} + +static void rfbEncryptBytes(unsigned char *bytes, char *passwd); + +/* + * InitialiseRFBConnection. + */ + +Bool +InitialiseRFBConnection(rfbClient* client) +{ + rfbProtocolVersionMsg pv; + int major,minor; + uint32_t authScheme, reasonLen, authResult; + char *reason; + uint8_t challenge[CHALLENGESIZE]; + char *passwd; + int i; + rfbClientInitMsg ci; + + /* if the connection is immediately closed, don't report anything, so + that pmw's monitor can make test connections */ + + if (client->listenSpecified) + errorMessageOnReadFailure = FALSE; + + if (!ReadFromRFBServer(client, pv, sz_rfbProtocolVersionMsg)) return FALSE; + + errorMessageOnReadFailure = TRUE; + + pv[sz_rfbProtocolVersionMsg] = 0; + + if (sscanf(pv,rfbProtocolVersionFormat,&major,&minor) != 2) { + fprintf(stderr,"Not a valid VNC server\n"); + return FALSE; + } + + fprintf(stderr,"VNC server supports protocol version %d.%d (viewer %d.%d)\n", + major, minor, rfbProtocolMajorVersion, rfbProtocolMinorVersion); + + major = rfbProtocolMajorVersion; + minor = rfbProtocolMinorVersion; + + sprintf(pv,rfbProtocolVersionFormat,major,minor); + + if (!WriteExact(client, pv, sz_rfbProtocolVersionMsg)) return FALSE; + + if (!ReadFromRFBServer(client, (char *)&authScheme, 4)) return FALSE; + + authScheme = Swap32IfLE(authScheme); + + switch (authScheme) { + + case rfbConnFailed: + if (!ReadFromRFBServer(client, (char *)&reasonLen, 4)) return FALSE; + reasonLen = Swap32IfLE(reasonLen); + + reason = malloc(reasonLen); + + if (!ReadFromRFBServer(client, reason, reasonLen)) return FALSE; + + fprintf(stderr,"VNC connection failed: %.*s\n",(int)reasonLen, reason); + return FALSE; + + case rfbNoAuth: + fprintf(stderr,"No authentication needed\n"); + break; + + case rfbVncAuth: + if (!ReadFromRFBServer(client, (char *)challenge, CHALLENGESIZE)) return FALSE; + + if (client->GetPassword) + passwd = client->GetPassword(client); + + if ((!passwd) || (strlen(passwd) == 0)) { + fprintf(stderr,"Reading password failed\n"); + return FALSE; + } + if (strlen(passwd) > 8) { + passwd[8] = '\0'; + } + + rfbEncryptBytes(challenge, passwd); + + /* Lose the password from memory */ + for (i = strlen(passwd); i >= 0; i--) { + passwd[i] = '\0'; + } + + if (!WriteExact(client, (char *)challenge, CHALLENGESIZE)) return FALSE; + + if (!ReadFromRFBServer(client, (char *)&authResult, 4)) return FALSE; + + authResult = Swap32IfLE(authResult); + + switch (authResult) { + case rfbVncAuthOK: + fprintf(stderr,"VNC authentication succeeded\n"); + break; + case rfbVncAuthFailed: + fprintf(stderr,"VNC authentication failed\n"); + return FALSE; + case rfbVncAuthTooMany: + fprintf(stderr,"VNC authentication failed - too many tries\n"); + return FALSE; + default: + fprintf(stderr,"Unknown VNC authentication result: %d\n", + (int)authResult); + return FALSE; + } + break; + + default: + fprintf(stderr,"Unknown authentication scheme from VNC server: %d\n", + (int)authScheme); + return FALSE; + } + + ci.shared = (client->appData.shareDesktop ? 1 : 0); + + if (!WriteExact(client, (char *)&ci, sz_rfbClientInitMsg)) return FALSE; + + if (!ReadFromRFBServer(client, (char *)&client->si, sz_rfbServerInitMsg)) return FALSE; + + client->si.framebufferWidth = Swap16IfLE(client->si.framebufferWidth); + client->si.framebufferHeight = Swap16IfLE(client->si.framebufferHeight); + client->si.format.redMax = Swap16IfLE(client->si.format.redMax); + client->si.format.greenMax = Swap16IfLE(client->si.format.greenMax); + client->si.format.blueMax = Swap16IfLE(client->si.format.blueMax); + client->si.nameLength = Swap32IfLE(client->si.nameLength); + + client->desktopName = malloc(client->si.nameLength + 1); + if (!client->desktopName) { + fprintf(stderr, "Error allocating memory for desktop name, %lu bytes\n", + (unsigned long)client->si.nameLength); + return FALSE; + } + + if (!ReadFromRFBServer(client, client->desktopName, client->si.nameLength)) return FALSE; + + client->desktopName[client->si.nameLength] = 0; + + fprintf(stderr,"Desktop name \"%s\"\n",client->desktopName); + + fprintf(stderr,"Connected to VNC server, using protocol version %d.%d\n", + rfbProtocolMajorVersion, rfbProtocolMinorVersion); + + fprintf(stderr,"VNC server default format:\n"); + PrintPixelFormat(&client->si.format); + + return TRUE; +} + + +/* + * SetFormatAndEncodings. + */ + +Bool +SetFormatAndEncodings(rfbClient* client) +{ + rfbSetPixelFormatMsg spf; + char buf[sz_rfbSetEncodingsMsg + MAX_ENCODINGS * 4]; + rfbSetEncodingsMsg *se = (rfbSetEncodingsMsg *)buf; + uint32_t *encs = (uint32_t *)(&buf[sz_rfbSetEncodingsMsg]); + int len = 0; + Bool requestCompressLevel = FALSE; + Bool requestQualityLevel = FALSE; + Bool requestLastRectEncoding = FALSE; + + spf.type = rfbSetPixelFormat; + spf.format = client->format; + spf.format.redMax = Swap16IfLE(spf.format.redMax); + spf.format.greenMax = Swap16IfLE(spf.format.greenMax); + spf.format.blueMax = Swap16IfLE(spf.format.blueMax); + + if (!WriteExact(client, (char *)&spf, sz_rfbSetPixelFormatMsg)) + return FALSE; + + se->type = rfbSetEncodings; + se->nEncodings = 0; + + if (client->appData.encodingsString) { + const char *encStr = client->appData.encodingsString; + int encStrLen; + do { + const char *nextEncStr = strchr(encStr, ' '); + if (nextEncStr) { + encStrLen = nextEncStr - encStr; + nextEncStr++; + } else { + encStrLen = strlen(encStr); + } + + if (strncasecmp(encStr,"raw",encStrLen) == 0) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRaw); + } else if (strncasecmp(encStr,"copyrect",encStrLen) == 0) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCopyRect); + } else if (strncasecmp(encStr,"tight",encStrLen) == 0) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingTight); + requestLastRectEncoding = TRUE; + if (client->appData.compressLevel >= 0 && client->appData.compressLevel <= 9) + requestCompressLevel = TRUE; + if (client->appData.enableJPEG) + requestQualityLevel = TRUE; + } else if (strncasecmp(encStr,"hextile",encStrLen) == 0) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingHextile); + } else if (strncasecmp(encStr,"zlib",encStrLen) == 0) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingZlib); + if (client->appData.compressLevel >= 0 && client->appData.compressLevel <= 9) + requestCompressLevel = TRUE; + } else if (strncasecmp(encStr,"corre",encStrLen) == 0) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCoRRE); + } else if (strncasecmp(encStr,"rre",encStrLen) == 0) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRRE); + } else { + fprintf(stderr,"Unknown encoding '%.*s'\n",encStrLen,encStr); + } + + encStr = nextEncStr; + } while (encStr && se->nEncodings < MAX_ENCODINGS); + + if (se->nEncodings < MAX_ENCODINGS && requestCompressLevel) { + encs[se->nEncodings++] = Swap32IfLE(client->appData.compressLevel + + rfbEncodingCompressLevel0); + } + + if (se->nEncodings < MAX_ENCODINGS && requestQualityLevel) { + if (client->appData.qualityLevel < 0 || client->appData.qualityLevel > 9) + client->appData.qualityLevel = 5; + encs[se->nEncodings++] = Swap32IfLE(client->appData.qualityLevel + + rfbEncodingQualityLevel0); + } + + if (client->appData.useRemoteCursor) { + if (se->nEncodings < MAX_ENCODINGS) + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingXCursor); + if (se->nEncodings < MAX_ENCODINGS) + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRichCursor); + if (se->nEncodings < MAX_ENCODINGS) + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingPointerPos); + } + + if (se->nEncodings < MAX_ENCODINGS && requestLastRectEncoding) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingLastRect); + } + } + else { + if (SameMachine(client->sock)) { + /* TODO: + if (!tunnelSpecified) { + */ + fprintf(stderr,"Same machine: preferring raw encoding\n"); + /* TODO: */ + //encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRaw); + /* + } else { + fprintf(stderr,"Tunneling active: preferring tight encoding\n"); + } + */ + } + + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCopyRect); + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingTight); + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingHextile); + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingZlib); + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCoRRE); + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRRE); + + if (client->appData.compressLevel >= 0 && client->appData.compressLevel <= 9) { + encs[se->nEncodings++] = Swap32IfLE(client->appData.compressLevel + + rfbEncodingCompressLevel0); + } else /* if (!tunnelSpecified) */ { + /* If -tunnel option was provided, we assume that server machine is + not in the local network so we use default compression level for + tight encoding instead of fast compression. Thus we are + requesting level 1 compression only if tunneling is not used. */ + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCompressLevel1); + } + + if (client->appData.enableJPEG) { + if (client->appData.qualityLevel < 0 || client->appData.qualityLevel > 9) + client->appData.qualityLevel = 5; + encs[se->nEncodings++] = Swap32IfLE(client->appData.qualityLevel + + rfbEncodingQualityLevel0); + } + + if (client->appData.useRemoteCursor) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingXCursor); + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRichCursor); + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingPointerPos); + } + + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingLastRect); + } + + len = sz_rfbSetEncodingsMsg + se->nEncodings * 4; + + se->nEncodings = Swap16IfLE(se->nEncodings); + + if (!WriteExact(client, buf, len)) return FALSE; + + return TRUE; +} + + +/* + * SendIncrementalFramebufferUpdateRequest. + */ + +Bool +SendIncrementalFramebufferUpdateRequest(rfbClient* client) +{ + return SendFramebufferUpdateRequest(client, 0, 0, client->si.framebufferWidth, + client->si.framebufferHeight, TRUE); +} + + +/* + * SendFramebufferUpdateRequest. + */ + +Bool +SendFramebufferUpdateRequest(rfbClient* client, int x, int y, int w, int h, Bool incremental) +{ + rfbFramebufferUpdateRequestMsg fur; + + fur.type = rfbFramebufferUpdateRequest; + fur.incremental = incremental ? 1 : 0; + fur.x = Swap16IfLE(x); + fur.y = Swap16IfLE(y); + fur.w = Swap16IfLE(w); + fur.h = Swap16IfLE(h); + + if (!WriteExact(client, (char *)&fur, sz_rfbFramebufferUpdateRequestMsg)) + return FALSE; + + return TRUE; +} + + +/* + * SendPointerEvent. + */ + +Bool +SendPointerEvent(rfbClient* client,int x, int y, int buttonMask) +{ + rfbPointerEventMsg pe; + + pe.type = rfbPointerEvent; + pe.buttonMask = buttonMask; + if (x < 0) x = 0; + if (y < 0) y = 0; + + pe.x = Swap16IfLE(x); + pe.y = Swap16IfLE(y); + return WriteExact(client, (char *)&pe, sz_rfbPointerEventMsg); +} + + +/* + * SendKeyEvent. + */ + +Bool +SendKeyEvent(rfbClient* client, uint32_t key, Bool down) +{ + rfbKeyEventMsg ke; + + ke.type = rfbKeyEvent; + ke.down = down ? 1 : 0; + ke.key = Swap32IfLE(key); + return WriteExact(client, (char *)&ke, sz_rfbKeyEventMsg); +} + + +/* + * SendClientCutText. + */ + +Bool +SendClientCutText(rfbClient* client, char *str, int len) +{ + rfbClientCutTextMsg cct; + + if (client->serverCutText) + free(client->serverCutText); + client->serverCutText = NULL; + + cct.type = rfbClientCutText; + cct.length = Swap32IfLE(len); + return (WriteExact(client, (char *)&cct, sz_rfbClientCutTextMsg) && + WriteExact(client, str, len)); +} + + + +/* + * HandleRFBServerMessage. + */ + +Bool +HandleRFBServerMessage(rfbClient* client) +{ + rfbServerToClientMsg msg; + + if (!ReadFromRFBServer(client, (char *)&msg, 1)) + return FALSE; + + switch (msg.type) { + + case rfbSetColourMapEntries: + { + /* TODO: + int i; + uint16_t rgb[3]; + XColor xc; + + if (!ReadFromRFBServer(client, ((char *)&msg) + 1, + sz_rfbSetColourMapEntriesMsg - 1)) + return FALSE; + + msg.scme.firstColour = Swap16IfLE(msg.scme.firstColour); + msg.scme.nColours = Swap16IfLE(msg.scme.nColours); + + for (i = 0; i < msg.scme.nColours; i++) { + if (!ReadFromRFBServer(client, (char *)rgb, 6)) + return FALSE; + xc.pixel = msg.scme.firstColour + i; + xc.red = Swap16IfLE(rgb[0]); + xc.green = Swap16IfLE(rgb[1]); + xc.blue = Swap16IfLE(rgb[2]); + xc.flags = DoRed|DoGreen|DoBlue; + XStoreColor(dpy, cmap, &xc); + } + */ + + break; + } + + case rfbFramebufferUpdate: + { + rfbFramebufferUpdateRectHeader rect; + int linesToRead; + int bytesPerLine; + int i; + + if (!ReadFromRFBServer(client, ((char *)&msg.fu) + 1, + sz_rfbFramebufferUpdateMsg - 1)) + return FALSE; + + msg.fu.nRects = Swap16IfLE(msg.fu.nRects); + + for (i = 0; i < msg.fu.nRects; i++) { + if (!ReadFromRFBServer(client, (char *)&rect, sz_rfbFramebufferUpdateRectHeader)) + return FALSE; + + rect.encoding = Swap32IfLE(rect.encoding); + if (rect.encoding == rfbEncodingLastRect) + break; + + rect.r.x = Swap16IfLE(rect.r.x); + rect.r.y = Swap16IfLE(rect.r.y); + rect.r.w = Swap16IfLE(rect.r.w); + rect.r.h = Swap16IfLE(rect.r.h); + + if (rect.encoding == rfbEncodingXCursor || + rect.encoding == rfbEncodingRichCursor) { + if (!HandleCursorShape(client, + rect.r.x, rect.r.y, rect.r.w, rect.r.h, + rect.encoding)) { + return FALSE; + } + continue; + } + + if (rect.encoding == rfbEncodingPointerPos) { + if (!client->HandleCursorPos(client,rect.r.x, rect.r.y)) { + return FALSE; + } + continue; + } + + if ((rect.r.x + rect.r.w > client->si.framebufferWidth) || + (rect.r.y + rect.r.h > client->si.framebufferHeight)) + { + fprintf(stderr,"Rect too large: %dx%d at (%d, %d)\n", + rect.r.w, rect.r.h, rect.r.x, rect.r.y); + return FALSE; + } + + if (rect.r.h * rect.r.w == 0) { + fprintf(stderr,"Zero size rect - ignoring\n"); + continue; + } + + /* If RichCursor encoding is used, we should prevent collisions + between framebuffer updates and cursor drawing operations. */ + client->SoftCursorLockArea(client, rect.r.x, rect.r.y, rect.r.w, rect.r.h); + + switch (rect.encoding) { + + case rfbEncodingRaw: + + bytesPerLine = rect.r.w * client->format.bitsPerPixel / 8; + linesToRead = BUFFER_SIZE / bytesPerLine; + + while (rect.r.h > 0) { + if (linesToRead > rect.r.h) + linesToRead = rect.r.h; + + if (!ReadFromRFBServer(client, client->buffer,bytesPerLine * linesToRead)) + return FALSE; + + CopyRectangle(client, client->buffer, + rect.r.x, rect.r.y, rect.r.w,linesToRead); + + rect.r.h -= linesToRead; + rect.r.y += linesToRead; + + } + break; + + case rfbEncodingCopyRect: + { + rfbCopyRect cr; + + if (!ReadFromRFBServer(client, (char *)&cr, sz_rfbCopyRect)) + return FALSE; + + cr.srcX = Swap16IfLE(cr.srcX); + cr.srcY = Swap16IfLE(cr.srcY); + + /* If RichCursor encoding is used, we should extend our + "cursor lock area" (previously set to destination + rectangle) to the source rectangle as well. */ + client->SoftCursorLockArea(client, + cr.srcX, cr.srcY, rect.r.w, rect.r.h); + + CopyRectangleFromRectangle(client, + cr.srcX, cr.srcY, rect.r.w, rect.r.h, + rect.r.x, rect.r.y); + + break; + } + + case rfbEncodingRRE: + { + switch (client->format.bitsPerPixel) { + case 8: + if (!HandleRRE8(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return FALSE; + break; + case 16: + if (!HandleRRE16(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return FALSE; + break; + case 32: + if (!HandleRRE32(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return FALSE; + break; + } + break; + } + + case rfbEncodingCoRRE: + { + switch (client->format.bitsPerPixel) { + case 8: + if (!HandleCoRRE8(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return FALSE; + break; + case 16: + if (!HandleCoRRE16(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return FALSE; + break; + case 32: + if (!HandleCoRRE32(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return FALSE; + break; + } + break; + } + + case rfbEncodingHextile: + { + switch (client->format.bitsPerPixel) { + case 8: + if (!HandleHextile8(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return FALSE; + break; + case 16: + if (!HandleHextile16(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return FALSE; + break; + case 32: + if (!HandleHextile32(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return FALSE; + break; + } + break; + } + + case rfbEncodingZlib: + { + switch (client->format.bitsPerPixel) { + case 8: + if (!HandleZlib8(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return FALSE; + break; + case 16: + if (!HandleZlib16(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return FALSE; + break; + case 32: + if (!HandleZlib32(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return FALSE; + break; + } + break; + } + + case rfbEncodingTight: + { + switch (client->format.bitsPerPixel) { + case 8: + if (!HandleTight8(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return FALSE; + break; + case 16: + if (!HandleTight16(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return FALSE; + break; + case 32: + if (!HandleTight32(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return FALSE; + break; + } + break; + } + + default: + fprintf(stderr,"Unknown rect encoding %d\n", + (int)rect.encoding); + return FALSE; + } + + /* Now we may discard "soft cursor locks". */ + client->SoftCursorUnlockScreen(client); + + client->FramebufferUpdateReceived(client, rect.r.x, rect.r.y, rect.r.w, rect.r.h); + } + +#ifdef MITSHM + /* if using shared memory PutImage, make sure that the X server has + updated its framebuffer before we reuse the shared memory. This is + mainly to avoid copyrect using invalid screen contents - not sure + if we'd need it otherwise. */ + + if (client->appData.useShm) + XSync(dpy, FALSE); +#endif + + if (!SendIncrementalFramebufferUpdateRequest(client)) + return FALSE; + + break; + } + + case rfbBell: + { + client->Bell(client); + + break; + } + + case rfbServerCutText: + { + if (!ReadFromRFBServer(client, ((char *)&msg) + 1, + sz_rfbServerCutTextMsg - 1)) + return FALSE; + + msg.sct.length = Swap32IfLE(msg.sct.length); + + if (client->serverCutText) + free(client->serverCutText); + + client->serverCutText = malloc(msg.sct.length+1); + + if (!ReadFromRFBServer(client, client->serverCutText, msg.sct.length)) + return FALSE; + + client->serverCutText[msg.sct.length] = 0; + + client->newServerCutText = TRUE; + + break; + } + + default: + fprintf(stderr,"Unknown message type %d from VNC server\n",msg.type); + return FALSE; + } + + return TRUE; +} + + +#define GET_PIXEL8(pix, ptr) ((pix) = *(ptr)++) + +#define GET_PIXEL16(pix, ptr) (((uint8_t*)&(pix))[0] = *(ptr)++, \ + ((uint8_t*)&(pix))[1] = *(ptr)++) + +#define GET_PIXEL32(pix, ptr) (((uint8_t*)&(pix))[0] = *(ptr)++, \ + ((uint8_t*)&(pix))[1] = *(ptr)++, \ + ((uint8_t*)&(pix))[2] = *(ptr)++, \ + ((uint8_t*)&(pix))[3] = *(ptr)++) + +/* CONCAT2 concatenates its two arguments. CONCAT2E does the same but also + expands its arguments if they are macros */ + +#define CONCAT2(a,b) a##b +#define CONCAT2E(a,b) CONCAT2(a,b) +#define CONCAT3(a,b,c) a##b##c +#define CONCAT3E(a,b,c) CONCAT3(a,b,c) + +#define BPP 8 +#include "rre.c" +#include "corre.c" +#include "hextile.c" +#include "zlib.c" +#include "tight.c" +#undef BPP +#define BPP 16 +#include "rre.c" +#include "corre.c" +#include "hextile.c" +#include "zlib.c" +#include "tight.c" +#undef BPP +#define BPP 32 +#include "rre.c" +#include "corre.c" +#include "hextile.c" +#include "zlib.c" +#include "tight.c" +#undef BPP + + +/* + * PrintPixelFormat. + */ + +void +PrintPixelFormat(format) + rfbPixelFormat *format; +{ + if (format->bitsPerPixel == 1) { + fprintf(stderr," Single bit per pixel.\n"); + fprintf(stderr, + " %s significant bit in each byte is leftmost on the screen.\n", + (format->bigEndian ? "Most" : "Least")); + } else { + fprintf(stderr," %d bits per pixel.\n",format->bitsPerPixel); + if (format->bitsPerPixel != 8) { + fprintf(stderr," %s significant byte first in each pixel.\n", + (format->bigEndian ? "Most" : "Least")); + } + if (format->trueColour) { + fprintf(stderr," TRUE colour: max red %d green %d blue %d", + format->redMax, format->greenMax, format->blueMax); + fprintf(stderr,", shift red %d green %d blue %d\n", + format->redShift, format->greenShift, format->blueShift); + } else { + fprintf(stderr," Colour map (not true colour).\n"); + } + } +} + +static long +ReadCompactLen (rfbClient* client) +{ + long len; + uint8_t b; + + if (!ReadFromRFBServer(client, (char *)&b, 1)) + return -1; + len = (int)b & 0x7F; + if (b & 0x80) { + if (!ReadFromRFBServer(client, (char *)&b, 1)) + return -1; + len |= ((int)b & 0x7F) << 7; + if (b & 0x80) { + if (!ReadFromRFBServer(client, (char *)&b, 1)) + return -1; + len |= ((int)b & 0xFF) << 14; + } + } + return len; +} + +/* + * JPEG source manager functions for JPEG decompression in Tight decoder. + */ + +static struct jpeg_source_mgr jpegSrcManager; +static JOCTET *jpegBufferPtr; +static size_t jpegBufferLen; + +static void +JpegInitSource(j_decompress_ptr cinfo) +{ + jpegError = FALSE; +} + +static boolean +JpegFillInputBuffer(j_decompress_ptr cinfo) +{ + jpegError = TRUE; + jpegSrcManager.bytes_in_buffer = jpegBufferLen; + jpegSrcManager.next_input_byte = (JOCTET *)jpegBufferPtr; + + return TRUE; +} + +static void +JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes) +{ + if (num_bytes < 0 || num_bytes > jpegSrcManager.bytes_in_buffer) { + jpegError = TRUE; + jpegSrcManager.bytes_in_buffer = jpegBufferLen; + jpegSrcManager.next_input_byte = (JOCTET *)jpegBufferPtr; + } else { + jpegSrcManager.next_input_byte += (size_t) num_bytes; + jpegSrcManager.bytes_in_buffer -= (size_t) num_bytes; + } +} + +static void +JpegTermSource(j_decompress_ptr cinfo) +{ + /* No work necessary here. */ +} + +static void +JpegSetSrcManager(j_decompress_ptr cinfo, uint8_t *compressedData, + int compressedLen) +{ + jpegBufferPtr = (JOCTET *)compressedData; + jpegBufferLen = (size_t)compressedLen; + + jpegSrcManager.init_source = JpegInitSource; + jpegSrcManager.fill_input_buffer = JpegFillInputBuffer; + jpegSrcManager.skip_input_data = JpegSkipInputData; + jpegSrcManager.resync_to_restart = jpeg_resync_to_restart; + jpegSrcManager.term_source = JpegTermSource; + jpegSrcManager.next_input_byte = jpegBufferPtr; + jpegSrcManager.bytes_in_buffer = jpegBufferLen; + + cinfo->src = &jpegSrcManager; +} + +/* avoid name clashes with LibVNCServer */ + +#define vncEncryptBytes rfbEncryptBytes +#define vncEncryptAndStorePasswd rfbEncryptAndStorePasswdUnused +#define vncDecryptPasswdFromFile rfbDecryptPasswdFromFileUnused +#define vncRandomBytes rfbRandomBytesUnused + +#include "../vncauth.c" +#include "../d3des.c" diff --git a/libvncclient/rre.c b/libvncclient/rre.c new file mode 100644 index 0000000..fba3767 --- /dev/null +++ b/libvncclient/rre.c @@ -0,0 +1,68 @@ +/* + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * rre.c - handle RRE encoding. + * + * This file shouldn't be compiled directly. It is included multiple times by + * rfbproto.c, each time with a different definition of the macro BPP. For + * each value of BPP, this file defines a function which handles an RRE + * encoded rectangle with BPP bits per pixel. + */ + +#define HandleRREBPP CONCAT2E(HandleRRE,BPP) +#define CARDBPP CONCAT3E(uint,BPP,_t) + +static Bool +HandleRREBPP (rfbClient* client, int rx, int ry, int rw, int rh) +{ + rfbRREHeader hdr; + int i; + CARDBPP pix; + rfbRectangle subrect; + + if (!ReadFromRFBServer(client, (char *)&hdr, sz_rfbRREHeader)) + return FALSE; + + hdr.nSubrects = Swap32IfLE(hdr.nSubrects); + + if (!ReadFromRFBServer(client, (char *)&pix, sizeof(pix))) + return FALSE; + + FillRectangle(client, rx, ry, rw, rh, pix); + + for (i = 0; i < hdr.nSubrects; i++) { + if (!ReadFromRFBServer(client, (char *)&pix, sizeof(pix))) + return FALSE; + + if (!ReadFromRFBServer(client, (char *)&subrect, sz_rfbRectangle)) + return FALSE; + + subrect.x = Swap16IfLE(subrect.x); + subrect.y = Swap16IfLE(subrect.y); + subrect.w = Swap16IfLE(subrect.w); + subrect.h = Swap16IfLE(subrect.h); + + FillRectangle(client, rx+subrect.x, ry+subrect.y, subrect.w, subrect.h, pix); + } + + return TRUE; +} + +#undef CARDBPP diff --git a/libvncclient/sockets.c b/libvncclient/sockets.c new file mode 100644 index 0000000..5e6cd69 --- /dev/null +++ b/libvncclient/sockets.c @@ -0,0 +1,424 @@ +/* + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * sockets.c - functions to deal with sockets. + */ + +#include <unistd.h> +#include <sys/socket.h> +#include <errno.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <fcntl.h> +#include <assert.h> +#include <rfb/rfbclient.h> + +void PrintInHex(char *buf, int len); + +Bool errorMessageOnReadFailure = TRUE; + +#define BUF_SIZE 8192 +static char buf[BUF_SIZE]; +static char *bufoutptr = buf; +static int buffered = 0; + +/* + * ReadFromRFBServer is called whenever we want to read some data from the RFB + * server. It is non-trivial for two reasons: + * + * 1. For efficiency it performs some intelligent buffering, avoiding invoking + * the read() system call too often. For small chunks of data, it simply + * copies the data out of an internal buffer. For large amounts of data it + * reads directly into the buffer provided by the caller. + * + * 2. Whenever read() would block, it invokes the Xt event dispatching + * mechanism to process X events. In fact, this is the only place these + * events are processed, as there is no XtAppMainLoop in the program. + */ + +Bool +ReadFromRFBServer(rfbClient* client, char *out, unsigned int n) +{ + if (n <= buffered) { + memcpy(out, bufoutptr, n); + bufoutptr += n; + buffered -= n; + return TRUE; + } + + memcpy(out, bufoutptr, buffered); + + out += buffered; + n -= buffered; + + bufoutptr = buf; + buffered = 0; + + if (n <= BUF_SIZE) { + + while (buffered < n) { + int i = read(client->sock, buf + buffered, BUF_SIZE - buffered); + if (i <= 0) { + if (i < 0) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { + /* TODO: + ProcessXtEvents(); + */ + i = 0; + } else { + perror("read"); + return FALSE; + } + } else { + if (errorMessageOnReadFailure) { + fprintf(stderr,"VNC server closed connection\n"); + } + return FALSE; + } + } + buffered += i; + } + + memcpy(out, bufoutptr, n); + bufoutptr += n; + buffered -= n; + return TRUE; + + } else { + + while (n > 0) { + int i = read(client->sock, out, n); + if (i <= 0) { + if (i < 0) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { + /* TODO: + ProcessXtEvents(); + */ + i = 0; + } else { + perror("read"); + return FALSE; + } + } else { + if (errorMessageOnReadFailure) { + fprintf(stderr,"VNC server closed connection\n"); + } + return FALSE; + } + } + out += i; + n -= i; + } + + return TRUE; + } +} + + +/* + * Write an exact number of bytes, and don't return until you've sent them. + */ + +Bool +WriteExact(rfbClient* client, char *buf, int n) +{ + fd_set fds; + int i = 0; + int j; + + while (i < n) { + j = write(client->sock, buf + i, (n - i)); + if (j <= 0) { + if (j < 0) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { + FD_ZERO(&fds); + FD_SET(client->sock,&fds); + + if (select(client->sock+1, NULL, &fds, NULL, NULL) <= 0) { + perror("select"); + return FALSE; + } + j = 0; + } else { + perror("write"); + return FALSE; + } + } else { + fprintf(stderr,"write failed\n"); + return FALSE; + } + } + i += j; + } + return TRUE; +} + + +/* + * ConnectToTcpAddr connects to the given TCP port. + */ + +int +ConnectToTcpAddr(unsigned int host, int port) +{ + int sock; + struct sockaddr_in addr; + int one = 1; + + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = host; + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + perror("ConnectToTcpAddr: socket"); + return -1; + } + + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("ConnectToTcpAddr: connect"); + close(sock); + return -1; + } + + if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, + (char *)&one, sizeof(one)) < 0) { + perror("ConnectToTcpAddr: setsockopt"); + close(sock); + return -1; + } + + return sock; +} + + + +/* + * FindFreeTcpPort tries to find unused TCP port in the range + * (TUNNEL_PORT_OFFSET, TUNNEL_PORT_OFFSET + 99]. Returns 0 on failure. + */ + +int +FindFreeTcpPort(void) +{ + int sock, port; + struct sockaddr_in addr; + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + perror(": FindFreeTcpPort: socket"); + return 0; + } + + for (port = TUNNEL_PORT_OFFSET + 99; port > TUNNEL_PORT_OFFSET; port--) { + addr.sin_port = htons((unsigned short)port); + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) { + close(sock); + return port; + } + } + + close(sock); + return 0; +} + + +/* + * ListenAtTcpPort starts listening at the given TCP port. + */ + +int +ListenAtTcpPort(int port) +{ + int sock; + struct sockaddr_in addr; + int one = 1; + + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = INADDR_ANY; + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + perror("ListenAtTcpPort: socket"); + return -1; + } + + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + (const char *)&one, sizeof(one)) < 0) { + perror("ListenAtTcpPort: setsockopt"); + close(sock); + return -1; + } + + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("ListenAtTcpPort: bind"); + close(sock); + return -1; + } + + if (listen(sock, 5) < 0) { + perror("ListenAtTcpPort: listen"); + close(sock); + return -1; + } + + return sock; +} + + +/* + * AcceptTcpConnection accepts a TCP connection. + */ + +int +AcceptTcpConnection(int listenSock) +{ + int sock; + struct sockaddr_in addr; + int addrlen = sizeof(addr); + int one = 1; + + sock = accept(listenSock, (struct sockaddr *) &addr, &addrlen); + if (sock < 0) { + perror("AcceptTcpConnection: accept"); + return -1; + } + + if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, + (char *)&one, sizeof(one)) < 0) { + perror("AcceptTcpConnection: setsockopt"); + close(sock); + return -1; + } + + return sock; +} + + +/* + * SetNonBlocking sets a socket into non-blocking mode. + */ + +Bool +SetNonBlocking(int sock) +{ + if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { + perror("AcceptTcpConnection: fcntl"); + return FALSE; + } + return TRUE; +} + + +/* + * StringToIPAddr - convert a host string to an IP address. + */ + +Bool +StringToIPAddr(const char *str, unsigned int *addr) +{ + struct hostent *hp; + + if (strcmp(str,"") == 0) { + *addr = 0; /* local */ + return TRUE; + } + + *addr = inet_addr(str); + + if (*addr != -1) + return TRUE; + + hp = gethostbyname(str); + + if (hp) { + *addr = *(unsigned int *)hp->h_addr; + return TRUE; + } + + return FALSE; +} + + +/* + * Test if the other end of a socket is on the same machine. + */ + +Bool +SameMachine(int sock) +{ + struct sockaddr_in peeraddr, myaddr; + int addrlen = sizeof(struct sockaddr_in); + + getpeername(sock, (struct sockaddr *)&peeraddr, &addrlen); + getsockname(sock, (struct sockaddr *)&myaddr, &addrlen); + + return (peeraddr.sin_addr.s_addr == myaddr.sin_addr.s_addr); +} + + +/* + * Print out the contents of a packet for debugging. + */ + +void +PrintInHex(char *buf, int len) +{ + int i, j; + char c, str[17]; + + str[16] = 0; + + fprintf(stderr,"ReadExact: "); + + for (i = 0; i < len; i++) + { + if ((i % 16 == 0) && (i != 0)) { + fprintf(stderr," "); + } + c = buf[i]; + str[i % 16] = (((c > 31) && (c < 127)) ? c : '.'); + fprintf(stderr,"%02x ",(unsigned char)c); + if ((i % 4) == 3) + fprintf(stderr," "); + if ((i % 16) == 15) + { + fprintf(stderr,"%s\n",str); + } + } + if ((i % 16) != 0) + { + for (j = i % 16; j < 16; j++) + { + fprintf(stderr," "); + if ((j % 4) == 3) fprintf(stderr," "); + } + str[i % 16] = 0; + fprintf(stderr,"%s\n",str); + } + + fflush(stderr); +} diff --git a/libvncclient/tight.c b/libvncclient/tight.c new file mode 100644 index 0000000..c858392 --- /dev/null +++ b/libvncclient/tight.c @@ -0,0 +1,606 @@ +/* + * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * tight.c - handle ``tight'' encoding. + * + * This file shouldn't be compiled directly. It is included multiple + * times by rfbproto.c, each time with a different definition of the + * macro BPP. For each value of BPP, this file defines a function + * which handles a tight-encoded rectangle with BPP bits per pixel. + * + */ + +#define TIGHT_MIN_TO_COMPRESS 12 + +#define CARDBPP CONCAT3E(uint,BPP,_t) +#define filterPtrBPP CONCAT2E(filterPtr,BPP) + +#define HandleTightBPP CONCAT2E(HandleTight,BPP) +#define InitFilterCopyBPP CONCAT2E(InitFilterCopy,BPP) +#define InitFilterPaletteBPP CONCAT2E(InitFilterPalette,BPP) +#define InitFilterGradientBPP CONCAT2E(InitFilterGradient,BPP) +#define FilterCopyBPP CONCAT2E(FilterCopy,BPP) +#define FilterPaletteBPP CONCAT2E(FilterPalette,BPP) +#define FilterGradientBPP CONCAT2E(FilterGradient,BPP) + +#if BPP != 8 +#define DecompressJpegRectBPP CONCAT2E(DecompressJpegRect,BPP) +#endif + +#ifndef RGB_TO_PIXEL + +#define RGB_TO_PIXEL(bpp,r,g,b) \ + (((CARD##bpp)(r) & client->format.redMax) << client->format.redShift | \ + ((CARD##bpp)(g) & client->format.greenMax) << client->format.greenShift | \ + ((CARD##bpp)(b) & client->format.blueMax) << client->format.blueShift) + +#define RGB24_TO_PIXEL(bpp,r,g,b) \ + ((((CARD##bpp)(r) & 0xFF) * client->format.redMax + 127) / 255 \ + << client->format.redShift | \ + (((CARD##bpp)(g) & 0xFF) * client->format.greenMax + 127) / 255 \ + << client->format.greenShift | \ + (((CARD##bpp)(b) & 0xFF) * client->format.blueMax + 127) / 255 \ + << client->format.blueShift) + +#define RGB24_TO_PIXEL32(r,g,b) \ + (((uint32_t)(r) & 0xFF) << client->format.redShift | \ + ((uint32_t)(g) & 0xFF) << client->format.greenShift | \ + ((uint32_t)(b) & 0xFF) << client->format.blueShift) + +#endif + +/* Type declarations */ + +typedef void (*filterPtrBPP)(rfbClient* client, int, CARDBPP *); + +/* Prototypes */ + +static int InitFilterCopyBPP (rfbClient* client, int rw, int rh); +static int InitFilterPaletteBPP (rfbClient* client, int rw, int rh); +static int InitFilterGradientBPP (rfbClient* client, int rw, int rh); +static void FilterCopyBPP (rfbClient* client, int numRows, CARDBPP *destBuffer); +static void FilterPaletteBPP (rfbClient* client, int numRows, CARDBPP *destBuffer); +static void FilterGradientBPP (rfbClient* client, int numRows, CARDBPP *destBuffer); + +#if BPP != 8 +static Bool DecompressJpegRectBPP(rfbClient* client, int x, int y, int w, int h); +#endif + +/* Definitions */ + +static Bool +HandleTightBPP (rfbClient* client, int rx, int ry, int rw, int rh) +{ + CARDBPP fill_colour; + uint8_t comp_ctl; + uint8_t filter_id; + filterPtrBPP filterFn; + z_streamp zs; + char *buffer2; + int err, stream_id, compressedLen, bitsPixel; + int bufferSize, rowSize, numRows, portionLen, rowsProcessed, extraBytes; + + if (!ReadFromRFBServer(client, (char *)&comp_ctl, 1)) + return FALSE; + + /* Flush zlib streams if we are told by the server to do so. */ + for (stream_id = 0; stream_id < 4; stream_id++) { + if ((comp_ctl & 1) && zlibStreamActive[stream_id]) { + if (inflateEnd (&zlibStream[stream_id]) != Z_OK && + zlibStream[stream_id].msg != NULL) + fprintf(stderr, "inflateEnd: %s\n", zlibStream[stream_id].msg); + zlibStreamActive[stream_id] = FALSE; + } + comp_ctl >>= 1; + } + + /* Handle solid rectangles. */ + if (comp_ctl == rfbTightFill) { +#if BPP == 32 + if (client->format.depth == 24 && client->format.redMax == 0xFF && + client->format.greenMax == 0xFF && client->format.blueMax == 0xFF) { + if (!ReadFromRFBServer(client, client->buffer, 3)) + return FALSE; + fill_colour = RGB24_TO_PIXEL32(client->buffer[0], client->buffer[1], client->buffer[2]); + } else { + if (!ReadFromRFBServer(client, (char*)&fill_colour, sizeof(fill_colour))) + return FALSE; + } +#else + if (!ReadFromRFBServer(client, (char*)&fill_colour, sizeof(fill_colour))) + return FALSE; +#endif + + FillRectangle(client, rx, ry, rw, rh, fill_colour); + + return TRUE; + } + +#if BPP == 8 + if (comp_ctl == rfbTightJpeg) { + fprintf(stderr, "Tight encoding: JPEG is not supported in 8 bpp mode.\n"); + return FALSE; + } +#else + if (comp_ctl == rfbTightJpeg) { + return DecompressJpegRectBPP(client, rx, ry, rw, rh); + } +#endif + + /* Quit on unsupported subencoding value. */ + if (comp_ctl > rfbTightMaxSubencoding) { + fprintf(stderr, "Tight encoding: bad subencoding value received.\n"); + return FALSE; + } + + /* + * Here primary compression mode handling begins. + * Data was processed with optional filter + zlib compression. + */ + + /* First, we should identify a filter to use. */ + if ((comp_ctl & rfbTightExplicitFilter) != 0) { + if (!ReadFromRFBServer(client, (char*)&filter_id, 1)) + return FALSE; + + switch (filter_id) { + case rfbTightFilterCopy: + filterFn = FilterCopyBPP; + bitsPixel = InitFilterCopyBPP(client, rw, rh); + break; + case rfbTightFilterPalette: + filterFn = FilterPaletteBPP; + bitsPixel = InitFilterPaletteBPP(client, rw, rh); + break; + case rfbTightFilterGradient: + filterFn = FilterGradientBPP; + bitsPixel = InitFilterGradientBPP(client, rw, rh); + break; + default: + fprintf(stderr, "Tight encoding: unknown filter code received.\n"); + return FALSE; + } + } else { + filterFn = FilterCopyBPP; + bitsPixel = InitFilterCopyBPP(client, rw, rh); + } + if (bitsPixel == 0) { + fprintf(stderr, "Tight encoding: error receiving palette.\n"); + return FALSE; + } + + /* Determine if the data should be decompressed or just copied. */ + rowSize = (rw * bitsPixel + 7) / 8; + if (rh * rowSize < TIGHT_MIN_TO_COMPRESS) { + if (!ReadFromRFBServer(client, (char*)client->buffer, rh * rowSize)) + return FALSE; + + buffer2 = &client->buffer[TIGHT_MIN_TO_COMPRESS * 4]; + filterFn(client, rh, (CARDBPP *)buffer2); + + CopyRectangle(client, buffer2, rx, ry, rw, rh); + + return TRUE; + } + + /* Read the length (1..3 bytes) of compressed data following. */ + compressedLen = (int)ReadCompactLen(client); + if (compressedLen <= 0) { + fprintf(stderr, "Incorrect data received from the server.\n"); + return FALSE; + } + + /* Now let's initialize compression stream if needed. */ + stream_id = comp_ctl & 0x03; + zs = &zlibStream[stream_id]; + if (!zlibStreamActive[stream_id]) { + zs->zalloc = Z_NULL; + zs->zfree = Z_NULL; + zs->opaque = Z_NULL; + err = inflateInit(zs); + if (err != Z_OK) { + if (zs->msg != NULL) + fprintf(stderr, "InflateInit error: %s.\n", zs->msg); + return FALSE; + } + zlibStreamActive[stream_id] = TRUE; + } + + /* Read, decode and draw actual pixel data in a loop. */ + + bufferSize = BUFFER_SIZE * bitsPixel / (bitsPixel + BPP) & 0xFFFFFFFC; + buffer2 = &client->buffer[bufferSize]; + if (rowSize > bufferSize) { + /* Should be impossible when BUFFER_SIZE >= 16384 */ + fprintf(stderr, "Internal error: incorrect buffer size.\n"); + return FALSE; + } + + rowsProcessed = 0; + extraBytes = 0; + + while (compressedLen > 0) { + if (compressedLen > ZLIB_BUFFER_SIZE) + portionLen = ZLIB_BUFFER_SIZE; + else + portionLen = compressedLen; + + if (!ReadFromRFBServer(client, (char*)zlib_buffer, portionLen)) + return FALSE; + + compressedLen -= portionLen; + + zs->next_in = (Bytef *)zlib_buffer; + zs->avail_in = portionLen; + + do { + zs->next_out = (Bytef *)&client->buffer[extraBytes]; + zs->avail_out = bufferSize - extraBytes; + + err = inflate(zs, Z_SYNC_FLUSH); + if (err == Z_BUF_ERROR) /* Input exhausted -- no problem. */ + break; + if (err != Z_OK && err != Z_STREAM_END) { + if (zs->msg != NULL) { + fprintf(stderr, "Inflate error: %s.\n", zs->msg); + } else { + fprintf(stderr, "Inflate error: %d.\n", err); + } + return FALSE; + } + + numRows = (bufferSize - zs->avail_out) / rowSize; + + filterFn(client, numRows, (CARDBPP *)buffer2); + + extraBytes = bufferSize - zs->avail_out - numRows * rowSize; + if (extraBytes > 0) + memcpy(client->buffer, &client->buffer[numRows * rowSize], extraBytes); + + CopyRectangle(client, buffer2, rx, ry+rowsProcessed, rw, numRows); + + rowsProcessed += numRows; + } + while (zs->avail_out == 0); + } + + if (rowsProcessed != rh) { + fprintf(stderr, "Incorrect number of scan lines after decompression.\n"); + return FALSE; + } + + return TRUE; +} + +/*---------------------------------------------------------------------------- + * + * Filter stuff. + * + */ + +/* + The following variables are defined in rfbproto.c: + static Bool cutZeros; + static int rectWidth, rectColors; + static uint8_t tightPalette[256*4]; + static uint8_t tightPrevRow[2048*3*sizeof(CARD16)]; +*/ + +static int +InitFilterCopyBPP (rfbClient* client, int rw, int rh) +{ + rectWidth = rw; + +#if BPP == 32 + if (client->format.depth == 24 && client->format.redMax == 0xFF && + client->format.greenMax == 0xFF && client->format.blueMax == 0xFF) { + cutZeros = TRUE; + return 24; + } else { + cutZeros = FALSE; + } +#endif + + return BPP; +} + +static void +FilterCopyBPP (rfbClient* client, int numRows, CARDBPP *dst) +{ + +#if BPP == 32 + int x, y; + + if (cutZeros) { + for (y = 0; y < numRows; y++) { + for (x = 0; x < rectWidth; x++) { + dst[y*rectWidth+x] = + RGB24_TO_PIXEL32(client->buffer[(y*rectWidth+x)*3], + client->buffer[(y*rectWidth+x)*3+1], + client->buffer[(y*rectWidth+x)*3+2]); + } + } + return; + } +#endif + + memcpy (dst, client->buffer, numRows * rectWidth * (BPP / 8)); +} + +static int +InitFilterGradientBPP (rfbClient* client, int rw, int rh) +{ + int bits; + + bits = InitFilterCopyBPP(client, rw, rh); + if (cutZeros) + memset(tightPrevRow, 0, rw * 3); + else + memset(tightPrevRow, 0, rw * 3 * sizeof(uint16_t)); + + return bits; +} + +#if BPP == 32 + +static void +FilterGradient24 (rfbClient* client, int numRows, uint32_t *dst) +{ + int x, y, c; + uint8_t thisRow[2048*3]; + uint8_t pix[3]; + int est[3]; + + for (y = 0; y < numRows; y++) { + + /* First pixel in a row */ + for (c = 0; c < 3; c++) { + pix[c] = tightPrevRow[c] + client->buffer[y*rectWidth*3+c]; + thisRow[c] = pix[c]; + } + dst[y*rectWidth] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]); + + /* Remaining pixels of a row */ + for (x = 1; x < rectWidth; x++) { + for (c = 0; c < 3; c++) { + est[c] = (int)tightPrevRow[x*3+c] + (int)pix[c] - + (int)tightPrevRow[(x-1)*3+c]; + if (est[c] > 0xFF) { + est[c] = 0xFF; + } else if (est[c] < 0x00) { + est[c] = 0x00; + } + pix[c] = (uint8_t)est[c] + client->buffer[(y*rectWidth+x)*3+c]; + thisRow[x*3+c] = pix[c]; + } + dst[y*rectWidth+x] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]); + } + + memcpy(tightPrevRow, thisRow, rectWidth * 3); + } +} + +#endif + +static void +FilterGradientBPP (rfbClient* client, int numRows, CARDBPP *dst) +{ + int x, y, c; + CARDBPP *src = (CARDBPP *)client->buffer; + uint16_t *thatRow = (uint16_t *)tightPrevRow; + uint16_t thisRow[2048*3]; + uint16_t pix[3]; + uint16_t max[3]; + int shift[3]; + int est[3]; + +#if BPP == 32 + if (cutZeros) { + FilterGradient24(client, numRows, dst); + return; + } +#endif + + max[0] = client->format.redMax; + max[1] = client->format.greenMax; + max[2] = client->format.blueMax; + + shift[0] = client->format.redShift; + shift[1] = client->format.greenShift; + shift[2] = client->format.blueShift; + + for (y = 0; y < numRows; y++) { + + /* First pixel in a row */ + for (c = 0; c < 3; c++) { + pix[c] = (uint16_t)(((src[y*rectWidth] >> shift[c]) + thatRow[c]) & max[c]); + thisRow[c] = pix[c]; + } + dst[y*rectWidth] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]); + + /* Remaining pixels of a row */ + for (x = 1; x < rectWidth; x++) { + for (c = 0; c < 3; c++) { + est[c] = (int)thatRow[x*3+c] + (int)pix[c] - (int)thatRow[(x-1)*3+c]; + if (est[c] > (int)max[c]) { + est[c] = (int)max[c]; + } else if (est[c] < 0) { + est[c] = 0; + } + pix[c] = (uint16_t)(((src[y*rectWidth+x] >> shift[c]) + est[c]) & max[c]); + thisRow[x*3+c] = pix[c]; + } + dst[y*rectWidth+x] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]); + } + memcpy(thatRow, thisRow, rectWidth * 3 * sizeof(uint16_t)); + } +} + +static int +InitFilterPaletteBPP (rfbClient* client, int rw, int rh) +{ + uint8_t numColors; +#if BPP == 32 + int i; + CARDBPP *palette = (CARDBPP *)tightPalette; +#endif + + rectWidth = rw; + + if (!ReadFromRFBServer(client, (char*)&numColors, 1)) + return 0; + + rectColors = (int)numColors; + if (++rectColors < 2) + return 0; + +#if BPP == 32 + if (client->format.depth == 24 && client->format.redMax == 0xFF && + client->format.greenMax == 0xFF && client->format.blueMax == 0xFF) { + if (!ReadFromRFBServer(client, (char*)&tightPalette, rectColors * 3)) + return 0; + for (i = rectColors - 1; i >= 0; i--) { + palette[i] = RGB24_TO_PIXEL32(tightPalette[i*3], + tightPalette[i*3+1], + tightPalette[i*3+2]); + } + return (rectColors == 2) ? 1 : 8; + } +#endif + + if (!ReadFromRFBServer(client, (char*)&tightPalette, rectColors * (BPP / 8))) + return 0; + + return (rectColors == 2) ? 1 : 8; +} + +static void +FilterPaletteBPP (rfbClient* client, int numRows, CARDBPP *dst) +{ + int x, y, b, w; + uint8_t *src = (uint8_t *)client->buffer; + CARDBPP *palette = (CARDBPP *)tightPalette; + + if (rectColors == 2) { + w = (rectWidth + 7) / 8; + for (y = 0; y < numRows; y++) { + for (x = 0; x < rectWidth / 8; x++) { + for (b = 7; b >= 0; b--) + dst[y*rectWidth+x*8+7-b] = palette[src[y*w+x] >> b & 1]; + } + for (b = 7; b >= 8 - rectWidth % 8; b--) { + dst[y*rectWidth+x*8+7-b] = palette[src[y*w+x] >> b & 1]; + } + } + } else { + for (y = 0; y < numRows; y++) + for (x = 0; x < rectWidth; x++) + dst[y*rectWidth+x] = palette[(int)src[y*rectWidth+x]]; + } +} + +#if BPP != 8 + +/*---------------------------------------------------------------------------- + * + * JPEG decompression. + * + */ + +/* + The following variables are defined in rfbproto.c: + static Bool jpegError; + static struct jpeg_source_mgr jpegSrcManager; + static JOCTET *jpegBufferPtr; + static size_t *jpegBufferLen; +*/ + +static Bool +DecompressJpegRectBPP(rfbClient* client, int x, int y, int w, int h) +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + int compressedLen; + uint8_t *compressedData; + CARDBPP *pixelPtr; + JSAMPROW rowPointer[1]; + int dx, dy; + + compressedLen = (int)ReadCompactLen(client); + if (compressedLen <= 0) { + fprintf(stderr, "Incorrect data received from the server.\n"); + return FALSE; + } + + compressedData = malloc(compressedLen); + if (compressedData == NULL) { + fprintf(stderr, "Memory allocation error.\n"); + return FALSE; + } + + if (!ReadFromRFBServer(client, (char*)compressedData, compressedLen)) { + free(compressedData); + return FALSE; + } + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_decompress(&cinfo); + + JpegSetSrcManager(&cinfo, compressedData, compressedLen); + + jpeg_read_header(&cinfo, TRUE); + cinfo.out_color_space = JCS_RGB; + + jpeg_start_decompress(&cinfo); + if (cinfo.output_width != w || cinfo.output_height != h || + cinfo.output_components != 3) { + fprintf(stderr, "Tight Encoding: Wrong JPEG data received.\n"); + jpeg_destroy_decompress(&cinfo); + free(compressedData); + return FALSE; + } + + rowPointer[0] = (JSAMPROW)client->buffer; + dy = 0; + while (cinfo.output_scanline < cinfo.output_height) { + jpeg_read_scanlines(&cinfo, rowPointer, 1); + if (jpegError) { + break; + } + pixelPtr = (CARDBPP *)&client->buffer[BUFFER_SIZE / 2]; + for (dx = 0; dx < w; dx++) { + *pixelPtr++ = + RGB24_TO_PIXEL(BPP, client->buffer[dx*3], client->buffer[dx*3+1], client->buffer[dx*3+2]); + } + CopyRectangle(client, &client->buffer[BUFFER_SIZE / 2], x, y + dy, w, 1); + dy++; + } + + if (!jpegError) + jpeg_finish_decompress(&cinfo); + + jpeg_destroy_decompress(&cinfo); + free(compressedData); + + return !jpegError; +} + +#endif + diff --git a/libvncclient/vncviewer.c b/libvncclient/vncviewer.c new file mode 100644 index 0000000..3c9c13c --- /dev/null +++ b/libvncclient/vncviewer.c @@ -0,0 +1,240 @@ +/* + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * vncviewer.c - the Xt-based VNC viewer. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <rfb/rfbclient.h> + +static void Dummy(rfbClient* client) { +} +static Bool DummyPoint(rfbClient* client, int x, int y) { + return TRUE; +} +static void DummyRect(rfbClient* client, int x, int y, int w, int h) { +} +static char* NoPassword(rfbClient* client) { + return ""; +} +static Bool MallocFrameBuffer(rfbClient* client) { + if(client->frameBuffer) + free(client->frameBuffer); + client->frameBuffer=malloc(client->width*client->height*client->format.bitsPerPixel/8); + return client->frameBuffer?TRUE:FALSE; +} + +rfbClient* rfbGetClient(int* argc,char** argv, + int bitsPerSample,int samplesPerPixel, + int bytesPerPixel) { + rfbClient* client=(rfbClient*)calloc(sizeof(rfbClient),1); + client->programName = argv[0]; + client->endianTest = 1; + + client->format.bitsPerPixel = bytesPerPixel*8; + client->format.depth = bitsPerSample*samplesPerPixel; + client->format.bigEndian = *(char *)&client->endianTest?FALSE:TRUE; + client->format.trueColour = TRUE; + + if (client->format.bitsPerPixel == 8) { + client->format.redMax = 7; + client->format.greenMax = 7; + client->format.blueMax = 3; + client->format.redShift = 0; + client->format.greenShift = 3; + client->format.blueShift = 6; + } else { + client->format.redMax = (1 << bitsPerSample) - 1; + client->format.greenMax = (1 << bitsPerSample) - 1; + client->format.blueMax = (1 << bitsPerSample) - 1; + if(!client->format.bigEndian) { + client->format.redShift = 0; + client->format.greenShift = bitsPerSample; + client->format.blueShift = bitsPerSample * 2; + } else { + if(client->format.bitsPerPixel==8*3) { + client->format.redShift = bitsPerSample*2; + client->format.greenShift = bitsPerSample*1; + client->format.blueShift = 0; + } else { + client->format.redShift = bitsPerSample*3; + client->format.greenShift = bitsPerSample*2; + client->format.blueShift = bitsPerSample; + } + } + } + + client->HandleCursorPos = DummyPoint; + client->SoftCursorLockArea = DummyRect; + client->SoftCursorUnlockScreen = Dummy; + client->FramebufferUpdateReceived = DummyRect; + client->GetPassword = NoPassword; + client->MallocFrameBuffer = MallocFrameBuffer; + client->Bell = Dummy; + + return client; +} + +void PrintRect(rfbClient* client, int x, int y, int w, int h) { + fprintf(stderr,"Received an update for %d,%d,%d,%d.\n",x,y,w,h); +} + +void SaveFramebufferAsPGM(rfbClient* client, int x, int y, int w, int h) { + static time_t t=0,t1; + FILE* f; + int i,j; + int bpp=client->format.bitsPerPixel/8; + int row_stride=client->width*bpp; + + /* save one picture only if the last is older than 2 seconds */ + t1=time(0); + if(t1-t>2) + t=t1; + else + return; + + /* assert bpp=4 */ + if(bpp!=4) { + fprintf(stderr,"bpp = %d (!=4)\n",bpp); + return; + } + + f=fopen("/tmp/framebuffer.ppm","wb"); + + fprintf(f,"P6\n# %s\n%d %d\n255\n",client->desktopName,client->width,client->height); + for(j=0;j<client->height*row_stride;j+=row_stride) + for(i=0;i<client->width*bpp;i+=bpp) { + if(client->format.bigEndian) { + fputc(client->frameBuffer[j+i+bpp-1],f); + fputc(client->frameBuffer[j+i+bpp-2],f); + fputc(client->frameBuffer[j+i+bpp-3],f); + } else { + fputc(client->frameBuffer[j+i+bpp+0],f); + fputc(client->frameBuffer[j+i+bpp+1],f); + fputc(client->frameBuffer[j+i+bpp+2],f); + } + } + fclose(f); +} + +void +vncEncryptBytes(unsigned char *bytes, char *passwd); + +int +main(int argc, char **argv) +{ + int i; + rfbClient* client = rfbGetClient(&argc,argv,8,3,4); + const char* vncServerHost=""; + int vncServerPort=5900; + + char buf1[]="pass",buf2[]="pass"; + vncEncryptBytes(buf1,buf2); + + client->FramebufferUpdateReceived = PrintRect; + client->FramebufferUpdateReceived = SaveFramebufferAsPGM; + + /* The -listen option is used to make us a daemon process which listens for + incoming connections from servers, rather than actively connecting to a + given server. The -tunnel and -via options are useful to create + connections tunneled via SSH port forwarding. We must test for the + -listen option before invoking any Xt functions - this is because we use + forking, and Xt doesn't seem to cope with forking very well. For -listen + option, when a successful incoming connection has been accepted, + listenForIncomingConnections() returns, setting the listenSpecified + flag. */ + + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "-listen") == 0) { + listenForIncomingConnections(client); + break; + } else { + char* colon=strchr(argv[i],':'); + + vncServerHost=argv[i]; + if(colon) { + *colon=0; + vncServerPort=atoi(colon+1); + } else + vncServerPort=0; + vncServerPort+=5900; + } + + /* TODO: + if (strcmp(argv[i], "-tunnel") == 0 || strcmp(argv[i], "-via") == 0) { + if (!createTunnel(&argc, argv, i)) + exit(1); + break; + } + */ + } + + /* Call the main Xt initialisation function. It parses command-line options, + generating appropriate resource specs, and makes a connection to the X + display. */ + + /* TODO: cmdline args + toplevel = XtVaAppInitialize(&appContext, "Vncviewer", + cmdLineOptions, numCmdLineOptions, + &argc, argv, fallback_resources, + XtNborderWidth, 0, NULL); + + dpy = XtDisplay(toplevel); + */ + + /* Interpret resource specs and process any remaining command-line arguments + (i.e. the VNC server name). If the server name isn't specified on the + command line, getArgsAndResources() will pop up a dialog box and wait + for one to be entered. */ + + /* + GetArgsAndResources(argc, argv); + */ + + /* Unless we accepted an incoming connection, make a TCP connection to the + given VNC server */ + + if (!client->listenSpecified) { + if (!ConnectToRFBServer(client,vncServerHost, vncServerPort)) exit(1); + } + + /* Initialise the VNC connection, including reading the password */ + + if (!InitialiseRFBConnection(client)) exit(1); + + SetFormatAndEncodings(client); + + client->width=client->si.framebufferWidth; + client->height=client->si.framebufferHeight; + client->MallocFrameBuffer(client); + SendFramebufferUpdateRequest(client,0,0,client->width,client->height,FALSE); + + /* Now enter the main loop, processing VNC messages. X events will + automatically be processed whenever the VNC connection is idle. */ + + while (1) { + if (!HandleRFBServerMessage(client)) + break; + } + + return 0; +} diff --git a/libvncclient/zlib.c b/libvncclient/zlib.c new file mode 100644 index 0000000..1fa67b5 --- /dev/null +++ b/libvncclient/zlib.c @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2000 Tridia Corporation. All Rights Reserved. + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * zlib.c - handle zlib encoding. + * + * This file shouldn't be compiled directly. It is included multiple times by + * rfbproto.c, each time with a different definition of the macro BPP. For + * each value of BPP, this file defines a function which handles an zlib + * encoded rectangle with BPP bits per pixel. + */ + +#define HandleZlibBPP CONCAT2E(HandleZlib,BPP) +#define CARDBPP CONCAT2E(uint,BPP,_t) + +static Bool +HandleZlibBPP (rfbClient* client, int rx, int ry, int rw, int rh) +{ + rfbZlibHeader hdr; + int remaining; + int inflateResult; + int toRead; + + /* First make sure we have a large enough raw buffer to hold the + * decompressed data. In practice, with a fixed BPP, fixed frame + * buffer size and the first update containing the entire frame + * buffer, this buffer allocation should only happen once, on the + * first update. + */ + if ( raw_buffer_size < (( rw * rh ) * ( BPP / 8 ))) { + + if ( raw_buffer != NULL ) { + + free( raw_buffer ); + + } + + raw_buffer_size = (( rw * rh ) * ( BPP / 8 )); + raw_buffer = (char*) malloc( raw_buffer_size ); + + } + + if (!ReadFromRFBServer(client, (char *)&hdr, sz_rfbZlibHeader)) + return FALSE; + + remaining = Swap32IfLE(hdr.nBytes); + + /* Need to initialize the decompressor state. */ + decompStream.next_in = ( Bytef * )client->buffer; + decompStream.avail_in = 0; + decompStream.next_out = ( Bytef * )raw_buffer; + decompStream.avail_out = raw_buffer_size; + decompStream.data_type = Z_BINARY; + + /* Initialize the decompression stream structures on the first invocation. */ + if ( decompStreamInited == FALSE ) { + + inflateResult = inflateInit( &decompStream ); + + if ( inflateResult != Z_OK ) { + fprintf(stderr, + "inflateInit returned error: %d, msg: %s\n", + inflateResult, + decompStream.msg); + return FALSE; + } + + decompStreamInited = TRUE; + + } + + inflateResult = Z_OK; + + /* Process buffer full of data until no more to process, or + * some type of inflater error, or Z_STREAM_END. + */ + while (( remaining > 0 ) && + ( inflateResult == Z_OK )) { + + if ( remaining > BUFFER_SIZE ) { + toRead = BUFFER_SIZE; + } + else { + toRead = remaining; + } + + /* Fill the buffer, obtaining data from the server. */ + if (!ReadFromRFBServer(client, client->buffer,toRead)) + return FALSE; + + decompStream.next_in = ( Bytef * )client->buffer; + decompStream.avail_in = toRead; + + /* Need to uncompress buffer full. */ + inflateResult = inflate( &decompStream, Z_SYNC_FLUSH ); + + /* We never supply a dictionary for compression. */ + if ( inflateResult == Z_NEED_DICT ) { + fprintf(stderr,"zlib inflate needs a dictionary!\n"); + return FALSE; + } + if ( inflateResult < 0 ) { + fprintf(stderr, + "zlib inflate returned error: %d, msg: %s\n", + inflateResult, + decompStream.msg); + return FALSE; + } + + /* Result buffer allocated to be at least large enough. We should + * never run out of space! + */ + if (( decompStream.avail_in > 0 ) && + ( decompStream.avail_out <= 0 )) { + fprintf(stderr,"zlib inflate ran out of space!\n"); + return FALSE; + } + + remaining -= toRead; + + } /* while ( remaining > 0 ) */ + + if ( inflateResult == Z_OK ) { + + /* Put the uncompressed contents of the update on the screen. */ + CopyRectangle(client, raw_buffer, rx, ry, rw, rh); + } + else { + + fprintf(stderr, + "zlib inflate returned error: %d, msg: %s\n", + inflateResult, + decompStream.msg); + return FALSE; + + } + + return TRUE; +} + +#undef CARDBPP |