From ccdbe8f3256c3c776a1cc1a0517a38437b9e2c65 Mon Sep 17 00:00:00 2001 From: steven_carr Date: Mon, 15 May 2006 05:37:39 +0000 Subject: The great UltraVNC Compatibility Commit --- libvncserver/auth.c | 7 +- libvncserver/corre.c | 8 +- libvncserver/cursor.c | 11 +- libvncserver/hextile.c | 17 +- libvncserver/main.c | 32 +- libvncserver/rfbserver.c | 1013 ++++++++++++++++++-- libvncserver/rre.c | 8 +- libvncserver/scale.c | 8 +- libvncserver/sockets.c | 22 +- libvncserver/stats.c | 476 +++++++-- libvncserver/tight.c | 32 +- libvncserver/tightvnc-filetransfer/rfbtightproto.h | 6 + libvncserver/ultra.c | 4 +- libvncserver/zlib.c | 6 +- libvncserver/zrle.c | 5 +- 15 files changed, 1426 insertions(+), 229 deletions(-) (limited to 'libvncserver') diff --git a/libvncserver/auth.c b/libvncserver/auth.c index fd4c487..6e7a617 100755 --- a/libvncserver/auth.c +++ b/libvncserver/auth.c @@ -317,7 +317,12 @@ rfbAuthProcessClientMessage(rfbClientPtr cl) if (rfbWriteExact(cl, (char *)&authResult, 4) < 0) { rfbLogPerror("rfbAuthProcessClientMessage: write"); } - rfbCloseClient(cl); + /* support RFB 3.8 clients, they expect a reason *why* it was disconnected */ + if (cl->protocolMinorVersion > 7) { + rfbClientConnFailed(cl, "password check failed!"); + } + else + rfbCloseClient(cl); return; } diff --git a/libvncserver/corre.c b/libvncserver/corre.c index c1164c0..75f1211 100755 --- a/libvncserver/corre.c +++ b/libvncserver/corre.c @@ -41,7 +41,7 @@ static char *rreBeforeBuf = NULL; static int rreAfterBufSize = 0; static char *rreAfterBuf = NULL; -static int rreAfterBufLen; +static int rreAfterBufLen = 0; static int subrectEncode8(uint8_t *data, int w, int h); static int subrectEncode16(uint16_t *data, int w, int h); @@ -145,9 +145,9 @@ rfbSendSmallRectEncodingCoRRE(rfbClientPtr cl, return rfbSendRectEncodingRaw(cl, x, y, w, h); } - cl->rectanglesSent[rfbEncodingCoRRE]++; - cl->bytesSent[rfbEncodingCoRRE] += (sz_rfbFramebufferUpdateRectHeader - + sz_rfbRREHeader + rreAfterBufLen); + rfbStatRecordEncodingSent(cl,rfbEncodingCoRRE, + sz_rfbFramebufferUpdateRectHeader + sz_rfbRREHeader + rreAfterBufLen, + sz_rfbFramebufferUpdateRectHeader + w * h * (cl->format.bitsPerPixel / 8)); if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbRREHeader > UPDATE_BUF_SIZE) diff --git a/libvncserver/cursor.c b/libvncserver/cursor.c index 2accc25..8cc5ecb 100644 --- a/libvncserver/cursor.c +++ b/libvncserver/cursor.c @@ -78,9 +78,6 @@ rfbSendCursorShape(rfbClientPtr cl) sz_rfbFramebufferUpdateRectHeader); cl->ublen += sz_rfbFramebufferUpdateRectHeader; - cl->cursorShapeBytesSent += sz_rfbFramebufferUpdateRectHeader; - cl->cursorShapeUpdatesSent++; - if (!rfbSendUpdateBuf(cl)) return FALSE; @@ -167,9 +164,8 @@ rfbSendCursorShape(rfbClientPtr cl) } /* Send everything we have prepared in the cl->updateBuf[]. */ - - cl->cursorShapeBytesSent += (cl->ublen - saved_ublen); - cl->cursorShapeUpdatesSent++; + rfbStatRecordMessageSent(cl, (cl->useRichCursorEncoding ? rfbEncodingRichCursor : rfbEncodingXCursor), + sz_rfbFramebufferUpdateRectHeader + (cl->ublen - saved_ublen), sz_rfbFramebufferUpdateRectHeader + (cl->ublen - saved_ublen)); if (!rfbSendUpdateBuf(cl)) return FALSE; @@ -201,8 +197,7 @@ rfbSendCursorPos(rfbClientPtr cl) sz_rfbFramebufferUpdateRectHeader); cl->ublen += sz_rfbFramebufferUpdateRectHeader; - cl->cursorPosBytesSent += sz_rfbFramebufferUpdateRectHeader; - cl->cursorPosUpdatesSent++; + rfbStatRecordMessageSent(cl, rfbEncodingPointerPos, sz_rfbFramebufferUpdateRectHeader, sz_rfbFramebufferUpdateRectHeader); if (!rfbSendUpdateBuf(cl)) return FALSE; diff --git a/libvncserver/hextile.c b/libvncserver/hextile.c index cd598ac..52920d8 100755 --- a/libvncserver/hextile.c +++ b/libvncserver/hextile.c @@ -44,7 +44,7 @@ rfbSendRectEncodingHextile(rfbClientPtr cl, int h) { rfbFramebufferUpdateRectHeader rect; - + if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) { if (!rfbSendUpdateBuf(cl)) return FALSE; @@ -60,8 +60,9 @@ rfbSendRectEncodingHextile(rfbClientPtr cl, sz_rfbFramebufferUpdateRectHeader); cl->ublen += sz_rfbFramebufferUpdateRectHeader; - cl->rectanglesSent[rfbEncodingHextile]++; - cl->bytesSent[rfbEncodingHextile] += sz_rfbFramebufferUpdateRectHeader; + rfbStatRecordEncodingSent(cl, rfbEncodingHextile, + sz_rfbFramebufferUpdateRectHeader, + sz_rfbFramebufferUpdateRectHeader + w * (cl->format.bitsPerPixel / 8) * h); switch (cl->format.bitsPerPixel) { case 8: @@ -136,6 +137,7 @@ sendHextiles##bpp(rfbClientPtr cl, int rx, int ry, int rw, int rh) { startUblen = cl->ublen; \ cl->updateBuf[startUblen] = 0; \ cl->ublen++; \ + rfbStatRecordEncodingSentAdd(cl, rfbEncodingHextile, 1); \ \ testColours##bpp(clientPixelData, w * h, \ &mono, &solid, &newBg, &newFg); \ @@ -148,7 +150,6 @@ sendHextiles##bpp(rfbClientPtr cl, int rx, int ry, int rw, int rh) { } \ \ if (solid) { \ - cl->bytesSent[rfbEncodingHextile] += cl->ublen - startUblen; \ continue; \ } \ \ @@ -175,15 +176,15 @@ sendHextiles##bpp(rfbClientPtr cl, int rx, int ry, int rw, int rh) { (*cl->translateFn)(cl->translateLookupTable, \ &(cl->screen->serverFormat), &cl->format, fbptr, \ (char *)clientPixelData, \ - cl->scaledScreen->paddedWidthInBytes, w, h); \ + cl->scaledScreen->paddedWidthInBytes, w, h); \ \ memcpy(&cl->updateBuf[cl->ublen], (char *)clientPixelData, \ w * h * (bpp/8)); \ \ cl->ublen += w * h * (bpp/8); \ + rfbStatRecordEncodingSentAdd(cl, rfbEncodingHextile, \ + w * h * (bpp/8)); \ } \ - \ - cl->bytesSent[rfbEncodingHextile] += cl->ublen - startUblen; \ } \ } \ \ @@ -210,6 +211,7 @@ subrectEncode##bpp(rfbClientPtr cl, uint##bpp##_t *data, int w, int h, \ nSubrectsUblen = cl->ublen; \ cl->ublen++; \ + rfbStatRecordEncodingSentAdd(cl, rfbEncodingHextile, 1); \ \ for (y=0; yupdateBuf[cl->ublen++] = rfbHextilePackXY(thex,they); \ cl->updateBuf[cl->ublen++] = rfbHextilePackWH(thew,theh); \ + rfbStatRecordEncodingSentAdd(cl, rfbEncodingHextile, 1); \ \ /* \ * Now mark the subrect as done. \ diff --git a/libvncserver/main.c b/libvncserver/main.c index 6a34980..cded456 100644 --- a/libvncserver/main.c +++ b/libvncserver/main.c @@ -495,22 +495,40 @@ clientInput(void *data) pthread_create(&output_thread, NULL, clientOutput, (void *)cl); while (1) { - fd_set fds; + fd_set rfds, wfds, efds; struct timeval tv; int n; - FD_ZERO(&fds); - FD_SET(cl->sock, &fds); + FD_ZERO(&rfds); + FD_SET(cl->sock, &rfds); + FD_ZERO(&efds); + FD_SET(cl->sock, &efds); + + /* Are we transferring a file in the background? */ + FD_ZERO(&wfds); + if ((cl->fileTransfer.fd!=-1) && (cl->fileTransfer.sending==1)) + FD_SET(cl->sock, &wfds); + tv.tv_sec = 60; /* 1 minute */ tv.tv_usec = 0; - n = select(cl->sock + 1, &fds, NULL, &fds, &tv); + n = select(cl->sock + 1, &rfds, &wfds, &efds, &tv); if (n < 0) { rfbLogPerror("ReadExact: select"); break; } if (n == 0) /* timeout */ + { + rfbSendFileTransferChunk(cl); continue; - rfbProcessClientMessage(cl); + } + + /* We have some space on the transmit queue, send some data */ + if (FD_ISSET(cl->sock, &wfds)) + rfbSendFileTransferChunk(cl); + + if (FD_ISSET(cl->sock, &rfds) || FD_ISSET(cl->sock, &efds)) + rfbProcessClientMessage(cl); + if (cl->sock == -1) { /* Client has disconnected. */ break; @@ -818,6 +836,10 @@ rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv, screen->handleEventsEagerly = FALSE; + /* Emulate UltraVNC Server by default */ + screen->protocolMajorVersion = 3; + screen->protocolMinorVersion = 6; + if(!rfbProcessArguments(screen,argc,argv)) { free(screen); return NULL; diff --git a/libvncserver/rfbserver.c b/libvncserver/rfbserver.c index 1945ae1..ab4e04f 100644 --- a/libvncserver/rfbserver.c +++ b/libvncserver/rfbserver.c @@ -66,6 +66,14 @@ #endif #include #include +/* stst() */ +#include +#include +#include +/* readdir() */ +#include +/* errno */ +#include static void rfbProcessClientProtocolVersion(rfbClientPtr cl); static void rfbProcessClientNormalMessage(rfbClientPtr cl); @@ -229,6 +237,20 @@ rfbReverseConnection(rfbScreenInfoPtr rfbScreen, } +void +rfbSetProtocolVersion(rfbScreenInfoPtr rfbScreen, int major_, int minor_) +{ + /* Permit the server to set the version to report */ + /* TODO: sanity checking */ + if ((major_==3) && (minor_ > 2 && minor_ < 9)) + { + rfbScreen->protocolMajorVersion = major_; + rfbScreen->protocolMinorVersion = minor_; + } + else + rfbLog("rfbSetProtocolVersion(%d,%d) set to invalid values\n", major_, minor_); +} + /* * rfbNewClient is called when a new connection has been made by whatever * means. @@ -348,6 +370,8 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen, #endif #endif + cl->fileTransfer.fd = -1; + cl->enableCursorShapeUpdates = FALSE; cl->enableCursorPosUpdates = FALSE; cl->useRichCursorEncoding = FALSE; @@ -378,8 +402,8 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen, cl->lastPtrX = -1; - sprintf(pv,rfbProtocolVersionFormat,rfbProtocolMajorVersion, - rfbProtocolMinorVersion); + sprintf(pv,rfbProtocolVersionFormat,rfbScreen->protocolMajorVersion, + rfbScreen->protocolMinorVersion); if (rfbWriteExact(cl, pv, sz_rfbProtocolVersionMsg) < 0) { rfbLogPerror("rfbNewClient: write"); @@ -583,7 +607,7 @@ rfbProcessClientProtocolVersion(rfbClientPtr cl) free(cl->host); cl->host=strdup(name); } - rfbLog("Protocol version %d.%d\n", major_, minor_); + rfbLog("Client Protocol Version %d.%d\n", major_, minor_); if (major_ != rfbProtocolMajorVersion) { /* Major version mismatch - send a ConnFailed message */ @@ -591,23 +615,24 @@ rfbProcessClientProtocolVersion(rfbClientPtr cl) rfbErr("Major version mismatch\n"); sprintf(failureReason, "RFB protocol version mismatch - server %d.%d, client %d.%d", - rfbProtocolMajorVersion,rfbProtocolMinorVersion,major_,minor_); + cl->screen->protocolMajorVersion, cl->screen->protocolMinorVersion, + major_,minor_); rfbClientConnFailed(cl, failureReason); return; } - /* Chk for the minor version use either of the two standard version of RFB */ + /* Check for the minor version use either of the two standard version of RFB */ + /* + * UltraVNC Viewer detects FileTransfer compatible servers via rfb versions + * 3.4, 3.6, 3.14, 3.16 + * It's a bad method, but it is what they use to enable features... + * maintaining RFB version compatibility across multiple servers is a pain + * Should use something like ServerIdentity encoding + */ cl->protocolMinorVersion = minor_; - if (minor_ > rfbProtocolMinorVersion) { - cl->protocolMinorVersion = rfbProtocolMinorVersion; - } else if (minor_ < rfbProtocolMinorVersion) { - cl->protocolMinorVersion = rfbProtocolFallbackMinorVersion; - } - if (minor_ != rfbProtocolMinorVersion && - minor_ != rfbProtocolFallbackMinorVersion) { - rfbLog("Non-standard protocol version %d.%d, using %d.%d instead\n", + + rfbLog("Protocol version sent %d.%d, using %d.%d\n", major_, minor_, rfbProtocolMajorVersion, cl->protocolMinorVersion); - } rfbAuthNewClient(cl); } @@ -775,8 +800,7 @@ rfbSendKeyboardLedState(rfbClientPtr cl) sz_rfbFramebufferUpdateRectHeader); cl->ublen += sz_rfbFramebufferUpdateRectHeader; - cl->cursorPosBytesSent += sz_rfbFramebufferUpdateRectHeader; - cl->cursorPosUpdatesSent++; + rfbStatRecordEncodingSent(cl, rfbEncodingKeyboardLedState, sz_rfbFramebufferUpdateRectHeader, sz_rfbFramebufferUpdateRectHeader); if (!rfbSendUpdateBuf(cl)) return FALSE; @@ -806,7 +830,7 @@ rfbSendSupportedMessages(rfbClientPtr cl) rect.encoding = Swap32IfLE(rfbEncodingSupportedMessages); rect.r.x = 0; rect.r.y = 0; - rect.r.w = Swap16IfLE(sz_rfbFramebufferUpdateRectHeader); + rect.r.w = Swap16IfLE(sz_rfbSupportedMessages); rect.r.h = 0; memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, @@ -823,10 +847,10 @@ rfbSendSupportedMessages(rfbClientPtr cl) rfbSetBit(msgs.client2server, rfbClientCutText); rfbSetBit(msgs.client2server, rfbFileTransfer); rfbSetBit(msgs.client2server, rfbSetScale); - //rfbSetBit(msgs.client2server, rfbSetServerInput); - //rfbSetBit(msgs.client2server, rfbSetSW); - //rfbSetBit(msgs.client2server, rfbTextChat); - //rfbSetBit(msgs.client2server, rfbKeyFrameRequest); + /*rfbSetBit(msgs.client2server, rfbSetServerInput); */ + /*rfbSetBit(msgs.client2server, rfbSetSW); */ + /*rfbSetBit(msgs.client2server, rfbTextChat); */ + /*rfbSetBit(msgs.client2server, rfbKeyFrameRequest); */ rfbSetBit(msgs.client2server, rfbPalmVNCSetScaleFactor); rfbSetBit(msgs.server2client, rfbFramebufferUpdate); @@ -834,12 +858,15 @@ rfbSendSupportedMessages(rfbClientPtr cl) rfbSetBit(msgs.server2client, rfbBell); rfbSetBit(msgs.server2client, rfbServerCutText); rfbSetBit(msgs.server2client, rfbResizeFrameBuffer); - //rfbSetBit(msgs.server2client, rfbKeyFrameUpdate); + /*rfbSetBit(msgs.server2client, rfbKeyFrameUpdate); */ rfbSetBit(msgs.server2client, rfbPalmVNCReSizeFrameBuffer); memcpy(&cl->updateBuf[cl->ublen], (char *)&msgs, sz_rfbSupportedMessages); cl->ublen += sz_rfbSupportedMessages; + rfbStatRecordEncodingSent(cl, rfbEncodingSupportedMessages, + sz_rfbFramebufferUpdateRectHeader+sz_rfbSupportedMessages, + sz_rfbFramebufferUpdateRectHeader+sz_rfbSupportedMessages); if (!rfbSendUpdateBuf(cl)) return FALSE; @@ -918,6 +945,10 @@ rfbSendSupportedEncodings(rfbClientPtr cl) rfbSendSupporteddEncodings_SendEncoding(cl, rfbEncodingSupportedEncodings); rfbSendSupporteddEncodings_SendEncoding(cl, rfbEncodingServerIdentity); + rfbStatRecordEncodingSent(cl, rfbEncodingSupportedEncodings, + sz_rfbFramebufferUpdateRectHeader+(nEncodings * sizeof(uint32_t)), + sz_rfbFramebufferUpdateRectHeader+(nEncodings * sizeof(uint32_t))); + if (!rfbSendUpdateBuf(cl)) return FALSE; @@ -973,16 +1004,654 @@ rfbSendServerIdentity(rfbClientPtr cl) memcpy(&cl->updateBuf[cl->ublen], buffer, strlen(buffer)+1); cl->ublen += strlen(buffer)+1; + rfbStatRecordEncodingSent(cl, rfbEncodingServerIdentity, + sz_rfbFramebufferUpdateRectHeader+strlen(buffer)+1, + sz_rfbFramebufferUpdateRectHeader+strlen(buffer)+1); + + if (!rfbSendUpdateBuf(cl)) return FALSE; return TRUE; } +rfbBool rfbSendFileTransferMessage(rfbClientPtr cl, uint8_t contentType, uint8_t contentParam, uint32_t size, uint32_t length, char *buffer) +{ + rfbFileTransferMsg ft; + ft.type = rfbFileTransfer; + ft.contentType = contentType; + ft.contentParam = contentParam; + ft.pad = 0; /* UltraVNC did not Swap16LE(ft.contentParam) (Looks like it might be BigEndian) */ + ft.size = Swap32IfLE(size); + ft.length = Swap32IfLE(length); + + /* + rfbLog("rfbSendFileTransferMessage( %dtype, %dparam, %dsize, %dlen, %p)\n", contentType, contentParam, size, length, buffer); + */ + if (rfbWriteExact(cl, (char *)&ft, sz_rfbFileTransferMsg) < 0) { + rfbLogPerror("rfbSendFileTransferMessage: write"); + rfbCloseClient(cl); + return FALSE; + } + if (length>0) + { + if (rfbWriteExact(cl, buffer, length) < 0) { + rfbLogPerror("rfbSendFileTransferMessage: write"); + rfbCloseClient(cl); + return FALSE; + } + } + + rfbStatRecordMessageSent(cl, rfbFileTransfer, sz_rfbFileTransferMsg+length, sz_rfbFileTransferMsg+length); + return TRUE; +} +/* + * UltraVNC uses Windows Structures + */ +#define MAX_PATH 260 + +typedef struct _FILETIME { + uint32_t dwLowDateTime; + uint32_t dwHighDateTime; +} FILETIME; + +typedef struct _WIN32_FIND_DATA { + uint32_t dwFileAttributes; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + uint32_t nFileSizeHigh; + uint32_t nFileSizeLow; + uint32_t dwReserved0; + uint32_t dwReserved1; + uint8_t cFileName[ MAX_PATH ]; + uint8_t cAlternateFileName[ 14 ]; +} WIN32_FIND_DATA; + +#define FILE_ATTRIBUTE_READONLY 0x1 +#define FILE_ATTRIBUTE_HIDDEN 0x2 +#define FILE_ATTRIBUTE_SYSTEM 0x4 +#define FILE_ATTRIBUTE_DIRECTORY 0x10 +#define FILE_ATTRIBUTE_ARCHIVE 0x20 +#define FILE_ATTRIBUTE_NORMAL 0x80 +#define FILE_ATTRIBUTE_TEMPORARY 0x100 +#define FILE_ATTRIBUTE_COMPRESSED 0x800 + +rfbBool rfbFilenameTranslate2UNIX(rfbClientPtr cl, char *path, char *unixPath) +{ + int x; + char *home=NULL; + /* C: */ + if (path[0]=='C' && path[1]==':') + strcpy(unixPath, &path[2]); + else + { + home = getenv("HOME"); + if (home!=NULL) + { + strcpy(unixPath, home); + strcat(unixPath,"/"); + strcat(unixPath, path); + } + else + strcpy(unixPath, path); + } + for (x=0;x\"%s\"\n",buffer, path); +// */ + dirp=opendir(path); + if (dirp==NULL) + return rfbSendFileTransferMessage(cl, rfbDirPacket, rfbADirectory, 0, 0, NULL); + /* send back the path name (necessary for links) */ + if (rfbSendFileTransferMessage(cl, rfbDirPacket, rfbADirectory, 0, length, buffer)==FALSE) return FALSE; + for (direntp=readdir(dirp); direntp!=NULL; direntp=readdir(dirp)) + { + /* get stats */ + snprintf(retfilename,sizeof(retfilename),"%s/%s", path, direntp->d_name); + retval = stat(retfilename, &statbuf); + + if (retval==0) + { + memset((char *)&win32filename, 0, sizeof(win32filename)); + win32filename.dwFileAttributes = Swap32IfBE(FILE_ATTRIBUTE_NORMAL); + if (S_ISDIR(statbuf.st_mode)) + win32filename.dwFileAttributes = Swap32IfBE(FILE_ATTRIBUTE_DIRECTORY); + win32filename.ftCreationTime.dwLowDateTime = Swap32IfBE(statbuf.st_ctime); /* Intel Order */ + win32filename.ftCreationTime.dwHighDateTime = 0; + win32filename.ftLastAccessTime.dwLowDateTime = Swap32IfBE(statbuf.st_atime); /* Intel Order */ + win32filename.ftLastAccessTime.dwHighDateTime = 0; + win32filename.ftLastWriteTime.dwLowDateTime = Swap32IfBE(statbuf.st_mtime); /* Intel Order */ + win32filename.ftLastWriteTime.dwHighDateTime = 0; + win32filename.nFileSizeLow = Swap32IfBE(statbuf.st_size); /* Intel Order */ + win32filename.nFileSizeHigh = 0; + win32filename.dwReserved0 = 0; + win32filename.dwReserved1 = 0; + + /* If this had the full path, we would need to translate to DOS format ("C:\") */ + /* rfbFilenameTranslate2DOS(cl, retfilename, win32filename.cFileName); */ + strcpy((char *)win32filename.cFileName, direntp->d_name); + + /* Do not show hidden files (but show how to move up the tree) */ + if ((strcmp(direntp->d_name, "..")==0) || (direntp->d_name[0]!='.')) + { + nOptLen = sizeof(WIN32_FIND_DATA) - MAX_PATH - 14 + strlen((char *)win32filename.cFileName); + /* + rfbLog("rfbProcessFileTransfer() rfbDirContentRequest: rfbRDirContent: Sending \"%s\"\n", (char *)win32filename.cFileName); + */ + if (rfbSendFileTransferMessage(cl, rfbDirPacket, rfbADirectory, 0, nOptLen, (char *)&win32filename)==FALSE) return FALSE; + } + } + } + closedir(dirp); + /* End of the transfer */ + return rfbSendFileTransferMessage(cl, rfbDirPacket, 0, 0, 0, NULL); +} + + +char *rfbProcessFileTransferReadBuffer(rfbClientPtr cl, uint32_t length) +{ + char *buffer=NULL; + int n=0; + /* + rfbLog("rfbProcessFileTransferReadBuffer(%dlen)\n", length); + */ + if (length>0) { + buffer=malloc(length+1); + if (buffer!=NULL) { + if ((n = rfbReadExact(cl, (char *)buffer, length)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessFileTransferReadBuffer: read"); + rfbCloseClient(cl); + /* NOTE: don't forget to free(buffer) if you return early! */ + if (buffer!=NULL) free(buffer); + return NULL; + } + /* Null Terminate */ + buffer[length]=0; + } + } + return buffer; +} + + +rfbBool rfbSendFileTransferChunk(rfbClientPtr cl) +{ + /* Allocate buffer for compression */ + unsigned char readBuf[sz_rfbBlockSize]; + int bytesRead=0; + int retval=0; + fd_set wfds; + struct timeval tv; + int n; +#ifdef LIBVNCSERVER_HAVE_LIBZ + unsigned char compBuf[sz_rfbBlockSize + 1024]; + unsigned long nMaxCompSize = sizeof(compBuf); + int nRetC = 0; +#endif + + /* If not sending, or no file open... Return as if we sent something! */ + if ((cl->fileTransfer.fd!=-1) && (cl->fileTransfer.sending==1)) + { + FD_ZERO(&wfds); + FD_SET(cl->sock, &wfds); + + /* return immediately */ + tv.tv_sec = 0; + tv.tv_usec = 0; + n = select(cl->sock + 1, NULL, &wfds, NULL, &tv); + + if (n<1) + rfbLog("rfbSendFileTransferChunk() select failed: %s\n", strerror(errno)); + /* We have space on the transmit queue */ + if (n > 0) + { + bytesRead = read(cl->fileTransfer.fd, readBuf, sz_rfbBlockSize); + switch (bytesRead) { + case 0: + /* + rfbLog("rfbSendFileTransferChunk(): End-Of-File Encountered\n"); + */ + retval = rfbSendFileTransferMessage(cl, rfbEndOfFile, 0, 0, 0, NULL); + close(cl->fileTransfer.fd); + cl->fileTransfer.fd = -1; + cl->fileTransfer.sending = 0; + cl->fileTransfer.receiving = 0; + return retval; + case -1: + /* TODO : send an error msg to the client... */ + rfbLog("rfbSendFileTransferChunk(): %s\n",strerror(errno)); + retval = rfbSendFileTransferMessage(cl, rfbAbortFileTransfer, 0, 0, 0, NULL); + close(cl->fileTransfer.fd); + cl->fileTransfer.fd = -1; + cl->fileTransfer.sending = 0; + cl->fileTransfer.receiving = 0; + return retval; + default: + /* + rfbLog("rfbSendFileTransferChunk(): Read %d bytes\n", bytesRead); + */ + if (!cl->fileTransfer.compressionEnabled) + return rfbSendFileTransferMessage(cl, rfbFilePacket, 0, 0, bytesRead, (char *)readBuf); + else + { +#ifdef LIBVNCSERVER_HAVE_LIBZ + nRetC = compress(compBuf, &nMaxCompSize, readBuf, bytesRead); + /* + rfbLog("Compressed the packet from %d -> %d bytes\n", nMaxCompSize, bytesRead); + */ + + if ((nRetC==0) && (nMaxCompSizeD:\....Z:\ + * + * We replace the "\" char following the drive letter and ":" + * with a char corresponding to the type of drive + * We obtain something like "C:lD:c....Z:n\" + * Isn't it ugly ? + * DRIVE_FIXED = 'l' (local?) + * DRIVE_REMOVABLE = 'f' (floppy?) + * DRIVE_CDROM = 'c' + * DRIVE_REMOTE = 'n' + */ + + /* in unix, there are no 'drives' (We could list mount points though) + * We fake the root as a "C:" for the Winblows users + */ + filename2[0]='C'; + filename2[1]=':'; + filename2[2]='l'; + filename2[3]=0; + filename2[4]=0; + retval = rfbSendFileTransferMessage(cl, rfbDirPacket, rfbADrivesList, 0, 5, filename2); + if (buffer!=NULL) free(buffer); + return retval; + break; + case rfbRDirContent: /* Client requests the content of a directory */ + /* + rfbLog("rfbProcessFileTransfer() rfbDirContentRequest: rfbRDirContent\n"); + */ + if ((buffer = rfbProcessFileTransferReadBuffer(cl, length))==NULL) return FALSE; + retval = rfbSendDirContent(cl, length, buffer); + if (buffer!=NULL) free(buffer); + return retval; + } + break; + + case rfbDirPacket: + rfbLog("rfbProcessFileTransfer() rfbDirPacket\n"); + break; + case rfbFileAcceptHeader: + rfbLog("rfbProcessFileTransfer() rfbFileAcceptHeader\n"); + break; + case rfbCommandReturn: + rfbLog("rfbProcessFileTransfer() rfbCommandReturn\n"); + break; + case rfbFileChecksums: + /* Destination file already exists - the viewer sends the checksums */ + rfbLog("rfbProcessFileTransfer() rfbFileChecksums\n"); + break; + case rfbFileTransferAccess: + rfbLog("rfbProcessFileTransfer() rfbFileTransferAccess\n"); + break; + + /* + * sending from the server to the viewer + */ + + case rfbFileTransferRequest: + /* + rfbLog("rfbProcessFileTransfer() rfbFileTransferRequest:\n"); + */ + /* add some space to the end of the buffer as we will be adding a timespec to it */ + if ((buffer = rfbProcessFileTransferReadBuffer(cl, length))==NULL) return FALSE; + /* The client requests a File */ + rfbFilenameTranslate2UNIX(cl, buffer, filename1); + cl->fileTransfer.fd=open(filename1, O_RDONLY, 0744); + + /* + rfbLog("rfbProcessFileTransfer() rfbFileTransferRequest(\"%s\"->\"%s\") Open: %s\n", buffer, filename1, (cl->fileTransfer.fd==-1?"Failed":"Success")); + */ + + if (cl->fileTransfer.fd!=-1) { + if (fstat(cl->fileTransfer.fd, &statbuf)!=0) { + close(cl->fileTransfer.fd); + cl->fileTransfer.fd=-1; + } + else + { + /* Add the File Time Stamp to the filename */ + strftime(timespec, sizeof(timespec), "%m/%d/%Y %H:%M",gmtime(&statbuf.st_ctime)); + buffer=realloc(buffer, length + strlen(timespec) + 2); /* comma, and Null term */ + if (buffer==NULL) { + rfbLog("rfbProcessFileTransfer() rfbFileTransferRequest: Failed to malloc %d bytes\n", length + strlen(timespec) + 2); + return FALSE; + } + strcat(buffer,","); + strcat(buffer, timespec); + length = strlen(buffer); + } + } + + /* The viewer supports compression if size==1 */ + cl->fileTransfer.compressionEnabled = (size==1); + + /* + rfbLog("rfbProcessFileTransfer() rfbFileTransferRequest(\"%s\"->\"%s\")%s\n", buffer, filename1, (size==1?" ":"")); + */ + + /* File Size in bytes, 0xFFFFFFFF (-1) means error */ + retval = rfbSendFileTransferMessage(cl, rfbFileHeader, 0, (cl->fileTransfer.fd==-1 ? -1 : statbuf.st_size), length, buffer); + + if (cl->fileTransfer.fd==-1) + { + if (buffer!=NULL) free(buffer); + return retval; + } + /* setup filetransfer stuff */ + cl->fileTransfer.fileSize = statbuf.st_size; + cl->fileTransfer.numPackets = statbuf.st_size / sz_rfbBlockSize; + cl->fileTransfer.receiving = 0; + cl->fileTransfer.sending = 0; /* set when we receive a rfbFileHeader: */ + + /* TODO: finish 64-bit file size support */ + sizeHtmp = 0; + if (rfbWriteExact(cl, (char *)&sizeHtmp, 4) < 0) { + rfbLogPerror("rfbProcessFileTransfer: write"); + rfbCloseClient(cl); + if (buffer!=NULL) free(buffer); + return FALSE; + } + break; + + case rfbFileHeader: + /* Destination file (viewer side) is ready for reception (size > 0) or not (size = -1) */ + if (size==-1) { + rfbLog("rfbProcessFileTransfer() rfbFileHeader (error, aborting)\n"); + close(cl->fileTransfer.fd); + cl->fileTransfer.fd=-1; + return TRUE; + } + + /* + rfbLog("rfbProcessFileTransfer() rfbFileHeader (%d bytes of a file)\n", size); + */ + + /* Starts the transfer! */ + cl->fileTransfer.sending=1; + return rfbSendFileTransferChunk(cl); + break; + + + /* + * sending from the viewer to the server + */ + + case rfbFileTransferOffer: + /* client is sending a file to us */ + /* buffer contains full path name (plus FileTime) */ + /* size contains size of the file */ + /* + rfbLog("rfbProcessFileTransfer() rfbFileTransferOffer:\n"); + */ + if ((buffer = rfbProcessFileTransferReadBuffer(cl, length))==NULL) return FALSE; + + /* Parse the FileTime */ + p = strrchr(buffer, ','); + if (p!=NULL) { + *p = '\0'; + strcpy(szFileTime, p+1); + } else + szFileTime[0]=0; + + + + /* Need to read in sizeHtmp */ + if ((n = rfbReadExact(cl, (char *)&sizeHtmp, 4)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessFileTransfer: read sizeHtmp"); + rfbCloseClient(cl); + /* NOTE: don't forget to free(buffer) if you return early! */ + if (buffer!=NULL) free(buffer); + return FALSE; + } + sizeHtmp = Swap32IfLE(sizeHtmp); + + rfbFilenameTranslate2UNIX(cl, buffer, filename1); + + /* If the file exists... We can send a rfbFileChecksums back to the client before we send an rfbFileAcceptHeader */ + /* TODO: Delta Transfer */ + + cl->fileTransfer.fd=open(filename1, O_CREAT|O_WRONLY|O_TRUNC, 0744); + /* + rfbLog("rfbProcessFileTransfer() rfbFileTransferOffer(\"%s\"->\"%s\") %s %s\n", buffer, filename1, (cl->fileTransfer.fd==-1?"Failed":"Success"), (cl->fileTransfer.fd==-1?strerror(errno):"")); + */ + + /* File Size in bytes, 0xFFFFFFFF (-1) means error */ + retval = rfbSendFileTransferMessage(cl, rfbFileAcceptHeader, 0, (cl->fileTransfer.fd==-1 ? -1 : 0), length, buffer); + if (cl->fileTransfer.fd==-1) { + free(buffer); + return retval; + } + + /* setup filetransfer stuff */ + cl->fileTransfer.fileSize = size; + cl->fileTransfer.numPackets = size / sz_rfbBlockSize; + cl->fileTransfer.receiving = 1; + cl->fileTransfer.sending = 0; + break; + + case rfbFilePacket: + /* + rfbLog("rfbProcessFileTransfer() rfbFilePacket:\n"); + */ + if ((buffer = rfbProcessFileTransferReadBuffer(cl, length))==NULL) return FALSE; + if (cl->fileTransfer.fd!=-1) { + /* buffer contains the contents of the file */ + if (size==0) + retval=write(cl->fileTransfer.fd, buffer, length); + else + { +#ifdef LIBVNCSERVER_HAVE_LIBZ + /* compressed packet */ + nRet = uncompress(compBuff,&nRawBytes,(const unsigned char*)buffer, length); + retval=write(cl->fileTransfer.fd, compBuff, nRawBytes); +#else + /* Write the file out as received... */ + retval=write(cl->fileTransfer.fd, buffer, length); +#endif + } + if (retval==-1) + { + close(cl->fileTransfer.fd); + cl->fileTransfer.fd=-1; + cl->fileTransfer.sending = 0; + cl->fileTransfer.receiving = 0; + } + } + break; + + case rfbEndOfFile: + /* + rfbLog("rfbProcessFileTransfer() rfbEndOfFile\n"); + */ + if (cl->fileTransfer.fd!=-1) + close(cl->fileTransfer.fd); + cl->fileTransfer.fd=-1; + cl->fileTransfer.sending = 0; + cl->fileTransfer.receiving = 0; + break; + + case rfbAbortFileTransfer: + /* + rfbLog("rfbProcessFileTransfer() rfbAbortFileTransfer\n"); + */ + if (cl->fileTransfer.fd!=-1) + { + close(cl->fileTransfer.fd); + cl->fileTransfer.fd=-1; + cl->fileTransfer.sending = 0; + cl->fileTransfer.receiving = 0; + } + else + { + /* We use this message for FileTransfer rights (<=RC18 versions) + * The client asks for FileTransfer permission + */ + if (contentParam == 0) + { + rfbLog("rfbProcessFileTransfer() File Transfer Permission DENIED! (Client Version <=RC18)\n"); + /* Old method for FileTransfer handshake perimssion (<=RC18) (Deny it)*/ + return rfbSendFileTransferMessage(cl, rfbAbortFileTransfer, 0, -1, 0, ""); + } + /* New method is allowed */ + if (cl->screen->getFileTransferPermission!=NULL) + { + if (cl->screen->getFileTransferPermission(cl)==TRUE) + { + rfbLog("rfbProcessFileTransfer() File Transfer Permission Granted!\n"); + return rfbSendFileTransferMessage(cl, rfbFileTransferAccess, 0, 1 , 0, ""); /* Permit */ + } + else + { + rfbLog("rfbProcessFileTransfer() File Transfer Permission DENIED!\n"); + return rfbSendFileTransferMessage(cl, rfbFileTransferAccess, 0, -1 , 0, ""); /* Deny */ + } + } + rfbLog("rfbProcessFileTransfer() File Transfer Permission DENIED by default!\n"); + return rfbSendFileTransferMessage(cl, rfbFileTransferAccess, 0, -1 , 0, ""); /* DEFAULT: DENY (for security) */ + } + break; + + + case rfbCommand: + /* + rfbLog("rfbProcessFileTransfer() rfbCommand:\n"); + */ + if ((buffer = rfbProcessFileTransferReadBuffer(cl, length))==NULL) return FALSE; + switch (contentParam) { + case rfbCDirCreate: /* Client requests the creation of a directory */ + rfbFilenameTranslate2UNIX(cl, buffer, filename1); + retval = mkdir(filename1, 0755); + /* + rfbLog("rfbProcessFileTransfer() rfbCommand: rfbCDirCreate(\"%s\"->\"%s\") %s\n", buffer, filename1, (retval==-1?"Failed":"Success")); + */ + retval = rfbSendFileTransferMessage(cl, rfbCommandReturn, rfbADirCreate, retval, length, buffer); + if (buffer!=NULL) free(buffer); + return retval; + case rfbCFileDelete: /* Client requests the deletion of a file */ + rfbFilenameTranslate2UNIX(cl, buffer, filename1); + if (stat(filename1,&statbuf)==0) + { + if (S_ISDIR(statbuf.st_mode)) + retval = rmdir(filename1); + else + retval = unlink(filename1); + } + else retval=-1; + retval = rfbSendFileTransferMessage(cl, rfbCommandReturn, rfbAFileDelete, retval, length, buffer); + if (buffer!=NULL) free(buffer); + return retval; + case rfbCFileRename: /* Client requests the Renaming of a file/directory */ + p = strrchr(buffer, '*'); + if (p != NULL) + { + /* Split into 2 filenames ('*' is a seperator) */ + *p = '\0'; + rfbFilenameTranslate2UNIX(cl, buffer, filename1); + rfbFilenameTranslate2UNIX(cl, p+1, filename2); + retval = rename(filename1,filename2); + /* + rfbLog("rfbProcessFileTransfer() rfbCommand: rfbCFileRename(\"%s\"->\"%s\" -->> \"%s\"->\"%s\") %s\n", buffer, filename1, p+1, filename2, (retval==-1?"Failed":"Success")); + */ + /* Restore the buffer so the reply is good */ + *p = '*'; + retval = rfbSendFileTransferMessage(cl, rfbCommandReturn, rfbAFileRename, retval, length, buffer); + if (buffer!=NULL) free(buffer); + return retval; + } + break; + } + + break; + } + + /* NOTE: don't forget to free(buffer) if you return early! */ + if (buffer!=NULL) free(buffer); + return TRUE; +} + /* * rfbProcessClientNormalMessage is called when the client has sent a normal * protocol message. @@ -994,6 +1663,11 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) int n=0; rfbClientToServerMsg msg; char *str; + int i; + uint32_t enc=0; + uint32_t lastPreferredEncoding = -1; + char encBuf[64]; + char encBuf2[64]; if ((n = rfbReadExact(cl, (char *)&msg, 1)) <= 0) { if (n != 0) @@ -1028,6 +1702,8 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) cl->readyForSetColourMapEntries = TRUE; cl->screen->setTranslateFunction(cl); + rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbSetPixelFormatMsg, sz_rfbSetPixelFormatMsg); + return; @@ -1039,16 +1715,23 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) rfbCloseClient(cl); return; } + rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbSetPixelFormatMsg, sz_rfbSetPixelFormatMsg); rfbLog("rfbProcessClientNormalMessage: %s", "FixColourMapEntries unsupported\n"); rfbCloseClient(cl); return; + /* NOTE: Some clients send us a set of encodings (ie: PointerPos) designed to enable/disable features... + * We may want to look into this... + * Example: + * case rfbEncodingXCursor: + * cl->enableCursorShapeUpdates = TRUE; + * + * Currently: cl->enableCursorShapeUpdates can *never* be turned off... + */ case rfbSetEncodings: { - int i; - uint32_t enc; if ((n = rfbReadExact(cl, ((char *)&msg) + 1, sz_rfbSetEncodingsMsg - 1)) <= 0) { @@ -1060,6 +1743,31 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) msg.se.nEncodings = Swap16IfLE(msg.se.nEncodings); + rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbSetEncodingsMsg+(msg.se.nEncodings*4),sz_rfbSetEncodingsMsg+(msg.se.nEncodings*4)); + + /* + * UltraVNC Client has the ability to adapt to changing network environments + * So, let's give it a change to tell us what it wants now! + */ + if (cl->preferredEncoding!=-1) + lastPreferredEncoding = cl->preferredEncoding; + + /* Reset all flags to defaults (allows us to switch between PointerPos and Server Drawn Cursors) */ + cl->preferredEncoding=-1; + cl->useCopyRect = FALSE; + cl->useNewFBSize = FALSE; + cl->cursorWasChanged = FALSE; + cl->useRichCursorEncoding = FALSE; + cl->enableCursorPosUpdates = FALSE; + cl->enableCursorShapeUpdates = FALSE; + cl->enableCursorShapeUpdates = FALSE; + cl->enableLastRectEncoding = FALSE; + cl->enableKeyboardLedState = FALSE; + cl->enableSupportedMessages = FALSE; + cl->enableSupportedEncodings = FALSE; + cl->enableServerIdentity = FALSE; + + for (i = 0; i < msg.se.nEncodings; i++) { if ((n = rfbReadExact(cl, (char *)&enc, 4)) <= 0) { if (n != 0) @@ -1075,58 +1783,23 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) cl->useCopyRect = TRUE; break; case rfbEncodingRaw: - if (cl->preferredEncoding == -1) { - cl->preferredEncoding = enc; - rfbLog("Using raw encoding for client %s\n", - cl->host); - } - break; case rfbEncodingRRE: - if (cl->preferredEncoding == -1) { - cl->preferredEncoding = enc; - rfbLog("Using rre encoding for client %s\n", - cl->host); - } - break; case rfbEncodingCoRRE: - if (cl->preferredEncoding == -1) { - cl->preferredEncoding = enc; - rfbLog("Using CoRRE encoding for client %s\n", - cl->host); - } - break; case rfbEncodingHextile: - if (cl->preferredEncoding == -1) { - cl->preferredEncoding = enc; - rfbLog("Using hextile encoding for client %s\n", - cl->host); - } - break; case rfbEncodingUltra: - if (cl->preferredEncoding == -1) { - cl->preferredEncoding = enc; - rfbLog("Using Ultra encoding for client %s\n", - cl->host); - } - break; #ifdef LIBVNCSERVER_HAVE_LIBZ case rfbEncodingZlib: - if (cl->preferredEncoding == -1) { - cl->preferredEncoding = enc; - rfbLog("Using zlib encoding for client %s\n", - cl->host); - } - break; + case rfbEncodingZRLE: #ifdef LIBVNCSERVER_HAVE_LIBJPEG case rfbEncodingTight: - if (cl->preferredEncoding == -1) { - cl->preferredEncoding = enc; - rfbLog("Using tight encoding for client %s\n", - cl->host); - } - break; #endif #endif + /* The first supported encoding is the 'preferred' encoding */ + if (cl->preferredEncoding == -1) + cl->preferredEncoding = enc; + + + break; case rfbEncodingXCursor: if(!cl->screen->dontConvertRichCursorToXCursor) { rfbLog("Enabling X-style cursor updates for client %s\n", @@ -1200,15 +1873,6 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) cl->enableServerIdentity = TRUE; } break; -#ifdef LIBVNCSERVER_HAVE_LIBZ - case rfbEncodingZRLE: - if (cl->preferredEncoding == -1) { - cl->preferredEncoding = enc; - rfbLog("Using ZRLE encoding for client %s\n", - cl->host); - } - break; -#endif default: #ifdef LIBVNCSERVER_HAVE_LIBZ if ( enc >= (uint32_t)rfbEncodingCompressLevel0 && @@ -1268,17 +1932,36 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) if(!handled) rfbLog("rfbProcessClientNormalMessage: " - "ignoring unknown encoding type %d\n", - (int)enc); + "ignoring unsupported encoding type %s\n", + encodingName(enc,encBuf,sizeof(encBuf))); } } } } + + if (cl->preferredEncoding == -1) { - cl->preferredEncoding = rfbEncodingRaw; + if (lastPreferredEncoding==-1) { + cl->preferredEncoding = rfbEncodingRaw; + rfbLog("Defaulting to %s encoding for client %s\n", encodingName(cl->preferredEncoding,encBuf,sizeof(encBuf)),cl->host); + } + else { + cl->preferredEncoding = lastPreferredEncoding; + rfbLog("Sticking with %s encoding for client %s\n", encodingName(cl->preferredEncoding,encBuf,sizeof(encBuf)),cl->host); + } } - + else + { + if (lastPreferredEncoding==-1) { + rfbLog("Using %s encoding for client %s\n", encodingName(cl->preferredEncoding,encBuf,sizeof(encBuf)),cl->host); + } else { + rfbLog("Switching from %s to %s Encoding for client %s\n", + encodingName(lastPreferredEncoding,encBuf2,sizeof(encBuf2)), + encodingName(cl->preferredEncoding,encBuf,sizeof(encBuf)), cl->host); + } + } + if (cl->enableCursorPosUpdates && !cl->enableCursorShapeUpdates) { rfbLog("Disabling cursor position updates for client %s\n", cl->host); @@ -1301,6 +1984,7 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) return; } + rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbFramebufferUpdateRequestMsg,sz_rfbFramebufferUpdateRequestMsg); /* The values come in based on the scaled screen, we need to convert them to * values based on the main screen's coordinate system @@ -1347,8 +2031,6 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) case rfbKeyEvent: - cl->keyEventsRcvd++; - if ((n = rfbReadExact(cl, ((char *)&msg) + 1, sz_rfbKeyEventMsg - 1)) <= 0) { if (n != 0) @@ -1357,6 +2039,8 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) return; } + rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbKeyEventMsg, sz_rfbKeyEventMsg); + if(!cl->viewOnly) { cl->screen->kbdAddEvent(msg.ke.down, (rfbKeySym)Swap32IfLE(msg.ke.key), cl); } @@ -1366,8 +2050,6 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) case rfbPointerEvent: - cl->pointerEventsRcvd++; - if ((n = rfbReadExact(cl, ((char *)&msg) + 1, sz_rfbPointerEventMsg - 1)) <= 0) { if (n != 0) @@ -1376,6 +2058,8 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) return; } + rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbPointerEventMsg, sz_rfbPointerEventMsg); + if (cl->screen->pointerClient && cl->screen->pointerClient != cl) return; @@ -1401,6 +2085,115 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) return; + case rfbFileTransfer: + if ((n = rfbReadExact(cl, ((char *)&msg) + 1, + sz_rfbFileTransferMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseClient(cl); + return; + } + msg.ft.size = Swap32IfLE(msg.ft.size); + msg.ft.length = Swap32IfLE(msg.ft.length); + /* record statistics in rfbProcessFileTransfer as length is filled with garbage when it is not valid */ + rfbProcessFileTransfer(cl, msg.ft.contentType, msg.ft.contentParam, msg.ft.size, msg.ft.length); + return; + + case rfbSetSW: + if ((n = rfbReadExact(cl, ((char *)&msg) + 1, + sz_rfbSetSWMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseClient(cl); + return; + } + msg.sw.x = Swap16IfLE(msg.sw.x); + msg.sw.y = Swap16IfLE(msg.sw.y); + rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbSetSWMsg, sz_rfbSetSWMsg); + /* msg.sw.status is not initialized in the ultraVNC viewer and contains random numbers (why???) */ + + rfbLog("Received a rfbSetSingleWindow(%d x, %d y)\n", msg.sw.x, msg.sw.y); + if (cl->screen->setSingleWindow!=NULL) + cl->screen->setSingleWindow(cl, msg.sw.x, msg.sw.y); + return; + + case rfbSetServerInput: + if ((n = rfbReadExact(cl, ((char *)&msg) + 1, + sz_rfbSetServerInputMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseClient(cl); + return; + } + rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbSetServerInputMsg, sz_rfbSetServerInputMsg); + + /* msg.sim.pad is not initialized in the ultraVNC viewer and contains random numbers (why???) */ + /* msg.sim.pad = Swap16IfLE(msg.sim.pad); */ + + rfbLog("Received a rfbSetServerInput(%d status)\n", msg.sim.status); + if (cl->screen->setServerInput!=NULL) + cl->screen->setServerInput(cl, msg.sim.status); + return; + + case rfbTextChat: + if ((n = rfbReadExact(cl, ((char *)&msg) + 1, + sz_rfbTextChatMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseClient(cl); + return; + } + + msg.tc.pad2 = Swap16IfLE(msg.tc.pad2); + msg.tc.length = Swap32IfLE(msg.tc.length); + + switch (msg.tc.length) { + case rfbTextChatOpen: + case rfbTextChatClose: + case rfbTextChatFinished: + /* commands do not have text following */ + /* Why couldn't they have used the pad byte??? */ + str=NULL; + rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbTextChatMsg, sz_rfbTextChatMsg); + break; + default: + if ((msg.tc.length>0) && (msg.tc.length%d\n", msg.tc.length, rfbTextMaxSize); + rfbCloseClient(cl); + return; + } + } + + /* Note: length can be commands: rfbTextChatOpen, rfbTextChatClose, and rfbTextChatFinished + * at which point, the str is NULL (as it is not sent) + */ + if (cl->screen->setTextChat!=NULL) + cl->screen->setTextChat(cl, msg.tc.length, str); + + free(str); + return; + + case rfbClientCutText: if ((n = rfbReadExact(cl, ((char *)&msg) + 1, @@ -1422,7 +2215,7 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) rfbCloseClient(cl); return; } - + rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbClientCutTextMsg+msg.cct.length, sz_rfbClientCutTextMsg+msg.cct.length); if(!cl->viewOnly) { cl->screen->setXCutText(str, msg.cct.length, cl); } @@ -1432,6 +2225,20 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) case rfbPalmVNCSetScaleFactor: cl->PalmVNC = TRUE; + if ((n = rfbReadExact(cl, ((char *)&msg) + 1, + sz_rfbSetScaleMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseClient(cl); + return; + } + rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbSetScaleMsg, sz_rfbSetScaleMsg); + rfbLog("rfbSetScale(%d)\n", msg.ssc.scale); + rfbScalingSetup(cl,cl->screen->width/msg.ssc.scale, cl->screen->height/msg.ssc.scale); + + rfbSendNewScaleSize(cl); + return; + case rfbSetScale: if ((n = rfbReadExact(cl, ((char *)&msg) + 1, @@ -1441,11 +2248,11 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) rfbCloseClient(cl); return; } + rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbSetScaleMsg, sz_rfbSetScaleMsg); rfbLog("rfbSetScale(%d)\n", msg.ssc.scale); rfbScalingSetup(cl,cl->screen->width/msg.ssc.scale, cl->screen->height/msg.ssc.scale); rfbSendNewScaleSize(cl); - return; default: @@ -1456,7 +2263,10 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) next = e->next; if(e->extension->handleMessage && e->extension->handleMessage(cl, e->data, &msg)) + { + rfbStatRecordMessageRcvd(cl, msg.type, 0, 0); /* Extension should handle this */ return; + } e = next; } @@ -1508,7 +2318,6 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, LOCK(cl->updateMutex); cl->newFBSizePending = FALSE; UNLOCK(cl->updateMutex); - cl->framebufferUpdateMessagesSent++; fu->type = rfbFramebufferUpdate; fu->nRects = Swap16IfLE(1); cl->ublen = sz_rfbFramebufferUpdateMsg; @@ -1690,11 +2499,11 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, rfbShowCursor(cl); } - /* + /* * Now send the update. */ - cl->framebufferUpdateMessagesSent++; - + + rfbStatRecordMessageSent(cl, rfbFramebufferUpdate, 0, 0); if (cl->preferredEncoding == rfbEncodingCoRRE) { nUpdateRegionRects = 0; @@ -1843,9 +2652,6 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, if (cl->screen!=cl->scaledScreen) rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h, "rfbSendFramebufferUpdate"); - cl->rawBytesEquivalent += (sz_rfbFramebufferUpdateRectHeader - + w * (cl->format.bitsPerPixel / 8) * h); - switch (cl->preferredEncoding) { case -1: case rfbEncodingRaw: @@ -1961,10 +2767,8 @@ rfbSendCopyRegion(rfbClientPtr cl, memcpy(&cl->updateBuf[cl->ublen], (char *)&cr, sz_rfbCopyRect); cl->ublen += sz_rfbCopyRect; - cl->rectanglesSent[rfbEncodingCopyRect]++; - cl->bytesSent[rfbEncodingCopyRect] - += sz_rfbFramebufferUpdateRectHeader + sz_rfbCopyRect; - + rfbStatRecordEncodingSent(cl, rfbEncodingCopyRect, sz_rfbFramebufferUpdateRectHeader + sz_rfbCopyRect, + w * h * (cl->scaledScreen->bitsPerPixel / 8)); } sraRgnReleaseIterator(i); @@ -2003,9 +2807,9 @@ rfbSendRectEncodingRaw(rfbClientPtr cl, memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,sz_rfbFramebufferUpdateRectHeader); cl->ublen += sz_rfbFramebufferUpdateRectHeader; - cl->rectanglesSent[rfbEncodingRaw]++; - cl->bytesSent[rfbEncodingRaw] - += sz_rfbFramebufferUpdateRectHeader + bytesPerLine * h; + + rfbStatRecordEncodingSent(cl, rfbEncodingRaw, sz_rfbFramebufferUpdateRectHeader + bytesPerLine * h, + sz_rfbFramebufferUpdateRectHeader + bytesPerLine * h); nlines = (UPDATE_BUF_SIZE - cl->ublen) / bytesPerLine; @@ -2069,8 +2873,8 @@ rfbSendLastRectMarker(rfbClientPtr cl) memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,sz_rfbFramebufferUpdateRectHeader); cl->ublen += sz_rfbFramebufferUpdateRectHeader; - cl->lastRectMarkersSent++; - cl->lastRectBytesSent += sz_rfbFramebufferUpdateRectHeader; + + rfbStatRecordEncodingSent(cl, rfbEncodingLastRect, sz_rfbFramebufferUpdateRectHeader, sz_rfbFramebufferUpdateRectHeader); return TRUE; } @@ -2108,8 +2912,7 @@ rfbSendNewFBSize(rfbClientPtr cl, sz_rfbFramebufferUpdateRectHeader); cl->ublen += sz_rfbFramebufferUpdateRectHeader; - cl->lastRectMarkersSent++; - cl->lastRectBytesSent += sz_rfbFramebufferUpdateRectHeader; + rfbStatRecordEncodingSent(cl, rfbEncodingNewFBSize, sz_rfbFramebufferUpdateRectHeader, sz_rfbFramebufferUpdateRectHeader); return TRUE; } @@ -2181,6 +2984,8 @@ rfbSendSetColourMapEntries(rfbClientPtr cl, rfbCloseClient(cl); return FALSE; } + + rfbStatRecordMessageSent(cl, rfbSetColourMapEntries, len, len); return TRUE; } @@ -2203,6 +3008,7 @@ rfbSendBell(rfbScreenInfoPtr rfbScreen) rfbCloseClient(cl); } } + rfbStatRecordMessageSent(cl, rfbBell, sz_rfbBellMsg, sz_rfbBellMsg); rfbReleaseClientIterator(i); } @@ -2232,6 +3038,7 @@ rfbSendServerCutText(rfbScreenInfoPtr rfbScreen,char *str, int len) rfbLogPerror("rfbSendServerCutText: write"); rfbCloseClient(cl); } + rfbStatRecordMessageSent(cl, rfbServerCutText, sz_rfbServerCutTextMsg+len, sz_rfbServerCutTextMsg+len); } rfbReleaseClientIterator(iterator); } diff --git a/libvncserver/rre.c b/libvncserver/rre.c index 8ef91fd..c0759aa 100755 --- a/libvncserver/rre.c +++ b/libvncserver/rre.c @@ -40,7 +40,7 @@ static char *rreBeforeBuf = NULL; static int rreAfterBufSize = 0; static char *rreAfterBuf = NULL; -static int rreAfterBufLen; +static int rreAfterBufLen=0; static int subrectEncode8(uint8_t *data, int w, int h); static int subrectEncode16(uint16_t *data, int w, int h); @@ -112,9 +112,9 @@ rfbSendRectEncodingRRE(rfbClientPtr cl, return rfbSendRectEncodingRaw(cl, x, y, w, h); } - cl->rectanglesSent[rfbEncodingRRE]++; - cl->bytesSent[rfbEncodingRRE] += (sz_rfbFramebufferUpdateRectHeader - + sz_rfbRREHeader + rreAfterBufLen); + rfbStatRecordEncodingSent(cl, rfbEncodingRRE, + sz_rfbFramebufferUpdateRectHeader + sz_rfbRREHeader + rreAfterBufLen, + sz_rfbFramebufferUpdateRectHeader + w * h * (cl->format.bitsPerPixel / 8)); if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbRREHeader > UPDATE_BUF_SIZE) diff --git a/libvncserver/scale.c b/libvncserver/scale.c index 63d232e..67de35f 100644 --- a/libvncserver/scale.c +++ b/libvncserver/scale.c @@ -128,8 +128,8 @@ void rfbScaledCorrection(rfbScreenInfoPtr from, rfbScreenInfoPtr to, int *x, int *h = (int)h2; /* Small changes for a thumbnail may be scaled to zero */ - if (*w==0) *w++; - if (*h==0) *h++; + if (*w==0) (*w)++; + if (*h==0) (*h)++; /* scaling from small to big may overstep the size a bit */ if (*x+*w > to->width) *w=to->width - *x; if (*y+*h > to->height) *h=to->height - *y; @@ -212,7 +212,9 @@ void rfbScaledScreenUpdateRect(rfbScreenInfoPtr screen, rfbScreenInfoPtr ptr, in pixel_value += (srcptr2[z] << (8 * z)); break; } - //srcptr2 += bytesPerPixel; + /* + srcptr2 += bytesPerPixel; + */ red += ((pixel_value >> redShift) & redMax); green += ((pixel_value >> greenShift) & greenMax); diff --git a/libvncserver/sockets.c b/libvncserver/sockets.c index 181862d..0d04f0a 100755 --- a/libvncserver/sockets.c +++ b/libvncserver/sockets.c @@ -239,8 +239,18 @@ rfbCheckFds(rfbScreenInfoPtr rfbScreen,long usec) tv.tv_usec = usec; nfds = select(rfbScreen->maxFd + 1, &fds, NULL, NULL /* &fds */, &tv); if (nfds == 0) { + /* timed out, check for async events */ + i = rfbGetClientIterator(rfbScreen); + while((cl = rfbClientIteratorNext(i))) { + if (cl->onHold) + continue; + if (FD_ISSET(cl->sock, &(rfbScreen->allFds))) + rfbSendFileTransferChunk(cl); + } + rfbReleaseClientIterator(i); return result; } + if (nfds < 0) { #ifdef WIN32 errno = WSAGetLastError(); @@ -332,11 +342,17 @@ rfbCheckFds(rfbScreenInfoPtr rfbScreen,long usec) i = rfbGetClientIterator(rfbScreen); while((cl = rfbClientIteratorNext(i))) { + if (cl->onHold) continue; - if (FD_ISSET(cl->sock, &fds) && - FD_ISSET(cl->sock, &(rfbScreen->allFds))) - rfbProcessClientMessage(cl); + + if (FD_ISSET(cl->sock, &(rfbScreen->allFds))) + { + if (FD_ISSET(cl->sock, &fds)) + rfbProcessClientMessage(cl); + else + rfbSendFileTransferChunk(cl); + } } rfbReleaseClientIterator(i); } while(rfbScreen->handleEventsEagerly); diff --git a/libvncserver/stats.c b/libvncserver/stats.c index ed9a3d0..0f8758f 100755 --- a/libvncserver/stats.c +++ b/libvncserver/stats.c @@ -26,90 +26,434 @@ #include -static const char* encNames[] = { - "raw", "copyRect", "RRE", "[encoding 3]", "CoRRE", "hextile", - "zlib", "tight", "[encoding 8]", "ultra", "[encoding 10]", - "[encoding 11]", "[encoding 12]", "[encoding 13]", "[encoding 14]", - "[encoding 15]", - "ZRLE", "[encoding 17]", "[encoding 18]", "[encoding 19]", "[encoding 20]" -}; +char *messageNameServer2Client(uint32_t type, char *buf, int len); +char *messageNameClient2Server(uint32_t type, char *buf, int len); +char *encodingName(uint32_t enc, char *buf, int len); +rfbStatList *rfbStatLookupEncoding(rfbClientPtr cl, uint32_t type); +rfbStatList *rfbStatLookupMessage(rfbClientPtr cl, uint32_t type); -void -rfbResetStats(rfbClientPtr cl) +void rfbStatRecordEncodingSent(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw); +void rfbStatRecordEncodingRcvd(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw); +void rfbStatRecordMessageSent(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw); +void rfbStatRecordMessageRcvd(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw); +void rfbResetStats(rfbClientPtr cl); +void rfbPrintStats(rfbClientPtr cl); + + + + +char *messageNameServer2Client(uint32_t type, char *buf, int len) { + if (buf==NULL) return "error"; + switch (type) { + case rfbFramebufferUpdate: snprintf(buf, len, "FramebufferUpdate"); break; + case rfbSetColourMapEntries: snprintf(buf, len, "SetColourMapEntries"); break; + case rfbBell: snprintf(buf, len, "Bell"); break; + case rfbServerCutText: snprintf(buf, len, "ServerCutText"); break; + case rfbResizeFrameBuffer: snprintf(buf, len, "ResizeFrameBuffer"); break; + case rfbKeyFrameUpdate: snprintf(buf, len, "KeyFrameUpdate"); break; + case rfbFileTransfer: snprintf(buf, len, "FileTransfer"); break; + case rfbTextChat: snprintf(buf, len, "TextChat"); break; + case rfbPalmVNCReSizeFrameBuffer: snprintf(buf, len, "PalmVNCReSize"); break; + default: + snprintf(buf, len, "server2client(0x%04X)", type); + } + return buf; +} + +char *messageNameClient2Server(uint32_t type, char *buf, int len) { + if (buf==NULL) return "error"; + switch (type) { + case rfbSetPixelFormat: snprintf(buf, len, "SetPixelFormat"); break; + case rfbFixColourMapEntries: snprintf(buf, len, "FixColourMapEntries"); break; + case rfbSetEncodings: snprintf(buf, len, "SetEncodings"); break; + case rfbFramebufferUpdateRequest: snprintf(buf, len, "FramebufferUpdate"); break; + case rfbKeyEvent: snprintf(buf, len, "KeyEvent"); break; + case rfbPointerEvent: snprintf(buf, len, "PointerEvent"); break; + case rfbClientCutText: snprintf(buf, len, "ClientCutText"); break; + case rfbFileTransfer: snprintf(buf, len, "FileTransfer"); break; + case rfbSetScale: snprintf(buf, len, "SetScale"); break; + case rfbSetServerInput: snprintf(buf, len, "SetServerInput"); break; + case rfbSetSW: snprintf(buf, len, "SetSingleWindow"); break; + case rfbTextChat: snprintf(buf, len, "TextChat"); break; + case rfbKeyFrameRequest: snprintf(buf, len, "KeyFrameRequest"); break; + case rfbPalmVNCSetScaleFactor: snprintf(buf, len, "PalmVNCSetScale"); break; + default: + snprintf(buf, len, "client2server(0x%04X)", type); + + + } + return buf; +} + +char *encodingName(uint32_t type, char *buf, int len) { + if (buf==NULL) return "error"; + + switch (type) { + case rfbEncodingRaw: snprintf(buf, len, "raw"); break; + case rfbEncodingCopyRect: snprintf(buf, len, "copyRect"); break; + case rfbEncodingRRE: snprintf(buf, len, "RRE"); break; + case rfbEncodingCoRRE: snprintf(buf, len, "CoRRE"); break; + case rfbEncodingHextile: snprintf(buf, len, "hextile"); break; + case rfbEncodingZlib: snprintf(buf, len, "zlib"); break; + case rfbEncodingTight: snprintf(buf, len, "tight"); break; + case rfbEncodingZlibHex: snprintf(buf, len, "zlibhex"); break; + case rfbEncodingUltra: snprintf(buf, len, "ultra"); break; + case rfbEncodingZRLE: snprintf(buf, len, "ZRLE"); break; + case rfbEncodingCache: snprintf(buf, len, "cache"); break; + case rfbEncodingCacheEnable: snprintf(buf, len, "cacheEnable"); break; + case rfbEncodingXOR_Zlib: snprintf(buf, len, "xorZlib"); break; + case rfbEncodingXORMonoColor_Zlib: snprintf(buf, len, "xorMonoZlib"); break; + case rfbEncodingXORMultiColor_Zlib: snprintf(buf, len, "xorColorZlib"); break; + case rfbEncodingSolidColor: snprintf(buf, len, "solidColor"); break; + case rfbEncodingXOREnable: snprintf(buf, len, "xorEnable"); break; + case rfbEncodingCacheZip: snprintf(buf, len, "cacheZip"); break; + case rfbEncodingSolMonoZip: snprintf(buf, len, "monoZip"); break; + case rfbEncodingUltraZip: snprintf(buf, len, "ultraZip"); break; + + case rfbEncodingXCursor: snprintf(buf, len, "Xcursor"); break; + case rfbEncodingRichCursor: snprintf(buf, len, "RichCursor"); break; + case rfbEncodingPointerPos: snprintf(buf, len, "PointerPos"); break; + + case rfbEncodingLastRect: snprintf(buf, len, "LastRect"); break; + case rfbEncodingNewFBSize: snprintf(buf, len, "NewFBSize"); break; + case rfbEncodingKeyboardLedState: snprintf(buf, len, "LedState"); break; + case rfbEncodingSupportedMessages: snprintf(buf, len, "SupportedMessages"); break; + case rfbEncodingSupportedEncodings: snprintf(buf, len, "SupportedEncodings"); break; + case rfbEncodingServerIdentity: snprintf(buf, len, "ServerIdentity"); break; + + case rfbEncodingCompressLevel0: snprintf(buf, len, "CompressLevel0"); break; + case rfbEncodingCompressLevel1: snprintf(buf, len, "CompressLevel1"); break; + case rfbEncodingCompressLevel2: snprintf(buf, len, "CompressLevel2"); break; + case rfbEncodingCompressLevel3: snprintf(buf, len, "CompressLevel3"); break; + case rfbEncodingCompressLevel4: snprintf(buf, len, "CompressLevel4"); break; + case rfbEncodingCompressLevel5: snprintf(buf, len, "CompressLevel5"); break; + case rfbEncodingCompressLevel6: snprintf(buf, len, "CompressLevel6"); break; + case rfbEncodingCompressLevel7: snprintf(buf, len, "CompressLevel7"); break; + case rfbEncodingCompressLevel8: snprintf(buf, len, "CompressLevel8"); break; + case rfbEncodingCompressLevel9: snprintf(buf, len, "CompressLevel9"); break; + + case rfbEncodingQualityLevel0: snprintf(buf, len, "QualityLevel0"); break; + case rfbEncodingQualityLevel1: snprintf(buf, len, "QualityLevel1"); break; + case rfbEncodingQualityLevel2: snprintf(buf, len, "QualityLevel2"); break; + case rfbEncodingQualityLevel3: snprintf(buf, len, "QualityLevel3"); break; + case rfbEncodingQualityLevel4: snprintf(buf, len, "QualityLevel4"); break; + case rfbEncodingQualityLevel5: snprintf(buf, len, "QualityLevel5"); break; + case rfbEncodingQualityLevel6: snprintf(buf, len, "QualityLevel6"); break; + case rfbEncodingQualityLevel7: snprintf(buf, len, "QualityLevel7"); break; + case rfbEncodingQualityLevel8: snprintf(buf, len, "QualityLevel8"); break; + case rfbEncodingQualityLevel9: snprintf(buf, len, "QualityLevel9"); break; + + + default: + snprintf(buf, len, "encoding(0x%04X)", type); + } + + return buf; +} + + + + + +rfbStatList *rfbStatLookupEncoding(rfbClientPtr cl, uint32_t type) { - int i; - for (i = 0; i < MAX_ENCODINGS; i++) { - cl->bytesSent[i] = 0; - cl->rectanglesSent[i] = 0; + rfbStatList *ptr; + if (cl==NULL) return NULL; + for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next) + { + if (ptr->type==type) return ptr; + } + /* Well, we are here... need to *CREATE* an entry */ + ptr = (rfbStatList *)malloc(sizeof(rfbStatList)); + if (ptr!=NULL) + { + memset((char *)ptr, 0, sizeof(rfbStatList)); + ptr->type = type; + /* add to the top of the list */ + ptr->Next = cl->statEncList; + cl->statEncList = ptr; } - cl->lastRectMarkersSent = 0; - cl->lastRectBytesSent = 0; - cl->cursorShapeBytesSent = 0; - cl->cursorShapeUpdatesSent = 0; - cl->cursorPosBytesSent = 0; - cl->cursorPosUpdatesSent = 0; - cl->framebufferUpdateMessagesSent = 0; - cl->rawBytesEquivalent = 0; - cl->keyEventsRcvd = 0; - cl->pointerEventsRcvd = 0; + return ptr; } -void -rfbPrintStats(rfbClientPtr cl) + +rfbStatList *rfbStatLookupMessage(rfbClientPtr cl, uint32_t type) { - int i; - int totalRectanglesSent = 0; - int totalBytesSent = 0; + rfbStatList *ptr; + if (cl==NULL) return NULL; + for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next) + { + if (ptr->type==type) return ptr; + } + /* Well, we are here... need to *CREATE* an entry */ + ptr = (rfbStatList *)malloc(sizeof(rfbStatList)); + if (ptr!=NULL) + { + memset((char *)ptr, 0, sizeof(rfbStatList)); + ptr->type = type; + /* add to the top of the list */ + ptr->Next = cl->statMsgList; + cl->statMsgList = ptr; + } + return ptr; +} - rfbLog("Statistics:\n"); +void rfbStatRecordEncodingSentAdd(rfbClientPtr cl, uint32_t type, int byteCount) /* Specifically for tight encoding */ +{ + rfbStatList *ptr; - if ((cl->keyEventsRcvd != 0) || (cl->pointerEventsRcvd != 0)) - rfbLog(" key events received %d, pointer events %d\n", - cl->keyEventsRcvd, cl->pointerEventsRcvd); + ptr = rfbStatLookupEncoding(cl, type); + if (ptr!=NULL) + ptr->bytesSent += byteCount; +} - for (i = 0; i < MAX_ENCODINGS; i++) { - totalRectanglesSent += cl->rectanglesSent[i]; - totalBytesSent += cl->bytesSent[i]; + +void rfbStatRecordEncodingSent(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw) +{ + rfbStatList *ptr; + + ptr = rfbStatLookupEncoding(cl, type); + if (ptr!=NULL) + { + ptr->sentCount++; + ptr->bytesSent += byteCount; + ptr->bytesSentIfRaw += byteIfRaw; } +} - totalRectanglesSent += (cl->cursorShapeUpdatesSent + - cl->cursorPosUpdatesSent + - cl->lastRectMarkersSent); - totalBytesSent += (cl->cursorShapeBytesSent + - cl->cursorPosBytesSent + - cl->lastRectBytesSent); +void rfbStatRecordEncodingRcvd(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw) +{ + rfbStatList *ptr; - rfbLog(" framebuffer updates %d, rectangles %d, bytes %d\n", - cl->framebufferUpdateMessagesSent, totalRectanglesSent, - totalBytesSent); + ptr = rfbStatLookupEncoding(cl, type); + if (ptr!=NULL) + { + ptr->rcvdCount++; + ptr->bytesRcvd += byteCount; + ptr->bytesRcvdIfRaw += byteIfRaw; + } +} - if (cl->lastRectMarkersSent != 0) - rfbLog(" LastRect and NewFBSize markers %d, bytes %d\n", - cl->lastRectMarkersSent, cl->lastRectBytesSent); +void rfbStatRecordMessageSent(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw) +{ + rfbStatList *ptr; - if (cl->cursorShapeUpdatesSent != 0) - rfbLog(" cursor shape updates %d, bytes %d\n", - cl->cursorShapeUpdatesSent, cl->cursorShapeBytesSent); + ptr = rfbStatLookupMessage(cl, type); + if (ptr!=NULL) + { + ptr->sentCount++; + ptr->bytesSent += byteCount; + ptr->bytesSentIfRaw += byteIfRaw; + } +} - if (cl->cursorPosUpdatesSent != 0) - rfbLog(" cursor position updates %d, bytes %d\n", - cl->cursorPosUpdatesSent, cl->cursorPosBytesSent); +void rfbStatRecordMessageRcvd(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw) +{ + rfbStatList *ptr; - for (i = 0; i < MAX_ENCODINGS; i++) { - if (cl->rectanglesSent[i] != 0) - rfbLog(" %s rectangles %d, bytes %d\n", - encNames[i], cl->rectanglesSent[i], cl->bytesSent[i]); + ptr = rfbStatLookupMessage(cl, type); + if (ptr!=NULL) + { + ptr->rcvdCount++; + ptr->bytesRcvd += byteCount; + ptr->bytesRcvdIfRaw += byteIfRaw; } +} + + +int rfbStatGetSentBytes(rfbClientPtr cl) +{ + rfbStatList *ptr=NULL; + int bytes=0; + if (cl==NULL) return 0; + for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next) + bytes += ptr->bytesSent; + for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next) + bytes += ptr->bytesSent; + return bytes; +} + +int rfbStatGetSentBytesIfRaw(rfbClientPtr cl) +{ + rfbStatList *ptr=NULL; + int bytes=0; + if (cl==NULL) return 0; + for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next) + bytes += ptr->bytesSentIfRaw; + for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next) + bytes += ptr->bytesSentIfRaw; + return bytes; +} + +int rfbStatGetRcvdBytes(rfbClientPtr cl) +{ + rfbStatList *ptr=NULL; + int bytes=0; + if (cl==NULL) return 0; + for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next) + bytes += ptr->bytesRcvd; + for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next) + bytes += ptr->bytesRcvd; + return bytes; +} + +int rfbStatGetRcvdBytesIfRaw(rfbClientPtr cl) +{ + rfbStatList *ptr=NULL; + int bytes=0; + if (cl==NULL) return 0; + for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next) + bytes += ptr->bytesRcvdIfRaw; + for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next) + bytes += ptr->bytesRcvdIfRaw; + return bytes; +} + +int rfbStatGetMessageCountSent(rfbClientPtr cl, uint32_t type) +{ + rfbStatList *ptr=NULL; + if (cl==NULL) return 0; + for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next) + if (ptr->type==type) return ptr->sentCount; + return 0; +} +int rfbStatGetMessageCountRcvd(rfbClientPtr cl, uint32_t type) +{ + rfbStatList *ptr=NULL; + if (cl==NULL) return 0; + for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next) + if (ptr->type==type) return ptr->rcvdCount; + return 0; +} + +int rfbStatGetEncodingCountSent(rfbClientPtr cl, uint32_t type) +{ + rfbStatList *ptr=NULL; + if (cl==NULL) return 0; + for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next) + if (ptr->type==type) return ptr->sentCount; + return 0; +} +int rfbStatGetEncodingCountRcvd(rfbClientPtr cl, uint32_t type) +{ + rfbStatList *ptr=NULL; + if (cl==NULL) return 0; + for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next) + if (ptr->type==type) return ptr->rcvdCount; + return 0; +} + + - if ((totalBytesSent - cl->bytesSent[rfbEncodingCopyRect]) != 0) { - rfbLog(" raw bytes equivalent %d, compression ratio %f\n", - cl->rawBytesEquivalent, - (double)cl->rawBytesEquivalent - / (double)(totalBytesSent - - cl->bytesSent[rfbEncodingCopyRect]- - cl->cursorShapeBytesSent - - cl->cursorPosBytesSent - - cl->lastRectBytesSent)); + +void rfbResetStats(rfbClientPtr cl) +{ + rfbStatList *ptr; + if (cl==NULL) return; + while (cl->statEncList!=NULL) + { + ptr = cl->statEncList; + cl->statEncList = ptr->Next; + free(ptr); + } + while (cl->statMsgList!=NULL) + { + ptr = cl->statMsgList; + cl->statMsgList = ptr->Next; + free(ptr); } } + + +void rfbPrintStats(rfbClientPtr cl) +{ + rfbStatList *ptr=NULL; + char encBuf[64]; + double savings=0.0; + int totalRectsSent=0; + double totalBytesSent=0.0; + double totalBytesIfRawSent=0.0; + int totalRectsRcvd=0; + double totalBytesRcvd=0.0; + double totalBytesIfRawRcvd=0.0; + + char *name=NULL; + int bytes=0; + int count=0; + + if (cl==NULL) return; + + rfbLog("Statistics: Transmit\n"); + for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next) + { + name = messageNameServer2Client(ptr->type, encBuf, sizeof(encBuf)); + count = ptr->sentCount; + bytes = ptr->bytesSent; + savings = 0.0; + if (ptr->bytesSentIfRaw>0.0) + savings = 100.0 - (((double)ptr->bytesSent / (double)ptr->bytesSentIfRaw) * 100.0); + if ((bytes>0) || (count>0)) + rfbLog(" %-24.24s: %6d events %9d/%9d bytes (%5.2f%% saved)\n", + name, count, bytes, ptr->bytesSentIfRaw, savings); + totalRectsSent += count; + totalBytesSent += bytes; + totalBytesIfRawSent += ptr->bytesSentIfRaw; + } + for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next) + { + name = encodingName(ptr->type, encBuf, sizeof(encBuf)); + count = ptr->sentCount; + bytes = ptr->bytesSent; + savings = 0.0; + if (ptr->bytesSentIfRaw>0.0) + savings = 100.0 - (((double)ptr->bytesSent / (double)ptr->bytesSentIfRaw) * 100.0); + if ((bytes>0) || (count>0)) + rfbLog(" %-24.24s: %6d events %9d/%9d bytes (%5.2f%% saved)\n", + name, count, bytes, ptr->bytesSentIfRaw, savings); + totalRectsSent += count; + totalBytesSent += bytes; + totalBytesIfRawSent += ptr->bytesSentIfRaw; + } + savings = 100.0 - ((totalBytesSent/totalBytesIfRawSent)*100.0); + rfbLog(" %-24.24s: %6d events %9.0f/%9.0f bytes (%5.2f%% savings)\n", + "TOTALS", totalRectsSent, totalBytesSent, totalBytesIfRawSent, savings); + + + rfbLog("Statistics: Receive\n"); + for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next) + { + name = messageNameClient2Server(ptr->type, encBuf, sizeof(encBuf)); + count = ptr->rcvdCount; + bytes = ptr->bytesRcvd; + savings = 0.0; + if (ptr->bytesSentIfRaw>0.0) + savings = 100.0 - (((double)ptr->bytesRcvd / (double)ptr->bytesRcvdIfRaw) * 100.0); + if ((bytes>0) || (count>0)) + rfbLog(" %-24.24s: %6d events %9d/%9d bytes (%5.2f%% saved)\n", + name, count, bytes, ptr->bytesRcvdIfRaw, savings); + totalRectsRcvd += count; + totalBytesRcvd += bytes; + totalBytesIfRawRcvd += ptr->bytesRcvdIfRaw; + } + for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next) + { + name = encodingName(ptr->type, encBuf, sizeof(encBuf)); + count = ptr->rcvdCount; + bytes = ptr->bytesRcvd; + savings = 0.0; + if (ptr->bytesSentIfRaw>0.0) + savings = 100.0 - (((double)ptr->bytesRcvd / (double)ptr->bytesRcvdIfRaw) * 100.0); + if ((bytes>0) || (count>0)) + rfbLog(" %-24.24s: %6d events %9d/%9d bytes (%5.2f%% saved)\n", + name, count, bytes, ptr->bytesRcvdIfRaw, savings); + totalRectsRcvd += count; + totalBytesRcvd += bytes; + totalBytesIfRawRcvd += ptr->bytesRcvdIfRaw; + } + savings = 100.0 - ((totalBytesRcvd/totalBytesIfRawRcvd)*100.0); + rfbLog(" %-24.24s: %6d events %9.0f/%9.0f bytes (%5.2f%% savings)\n", + "TOTALS", totalRectsRcvd, totalBytesRcvd,totalBytesIfRawRcvd, savings); + +} + diff --git a/libvncserver/tight.c b/libvncserver/tight.c index 71ce0d9..8a594c2 100644 --- a/libvncserver/tight.c +++ b/libvncserver/tight.c @@ -663,8 +663,8 @@ SendTightHeader(rfbClientPtr cl, sz_rfbFramebufferUpdateRectHeader); cl->ublen += sz_rfbFramebufferUpdateRectHeader; - cl->rectanglesSent[rfbEncodingTight]++; - cl->bytesSent[rfbEncodingTight] += sz_rfbFramebufferUpdateRectHeader; + rfbStatRecordEncodingSent(cl, rfbEncodingTight, sz_rfbFramebufferUpdateRectHeader, + sz_rfbFramebufferUpdateRectHeader + w * (cl->format.bitsPerPixel / 8) * h); return TRUE; } @@ -693,7 +693,7 @@ SendSolidRect(rfbClientPtr cl) memcpy (&cl->updateBuf[cl->ublen], tightBeforeBuf, len); cl->ublen += len; - cl->bytesSent[rfbEncodingTight] += len + 1; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, len+1); return TRUE; } @@ -736,7 +736,7 @@ SendMonoRect(rfbClientPtr cl, memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteLen); cl->ublen += paletteLen; - cl->bytesSent[rfbEncodingTight] += 3 + paletteLen; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 3 + paletteLen); break; case 16: @@ -747,7 +747,7 @@ SendMonoRect(rfbClientPtr cl, memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, 4); cl->ublen += 4; - cl->bytesSent[rfbEncodingTight] += 7; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 7); break; default: @@ -755,7 +755,7 @@ SendMonoRect(rfbClientPtr cl, cl->updateBuf[cl->ublen++] = (char)monoBackground; cl->updateBuf[cl->ublen++] = (char)monoForeground; - cl->bytesSent[rfbEncodingTight] += 5; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 5); } return CompressData(cl, streamId, dataLen, @@ -801,7 +801,7 @@ SendIndexedRect(rfbClientPtr cl, memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteNumColors * entryLen); cl->ublen += paletteNumColors * entryLen; - cl->bytesSent[rfbEncodingTight] += 3 + paletteNumColors * entryLen; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 3 + paletteNumColors * entryLen); break; case 16: @@ -814,7 +814,7 @@ SendIndexedRect(rfbClientPtr cl, memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteNumColors * 2); cl->ublen += paletteNumColors * 2; - cl->bytesSent[rfbEncodingTight] += 3 + paletteNumColors * 2; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 3 + paletteNumColors * 2); break; default: @@ -840,7 +840,7 @@ SendFullColorRect(rfbClientPtr cl, } cl->updateBuf[cl->ublen++] = 0x00; /* stream id = 0, no flushing, no filter */ - cl->bytesSent[rfbEncodingTight]++; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1); if (usePixelFormat24) { Pack24(cl, tightBeforeBuf, &cl->format, w * h); @@ -874,7 +874,7 @@ SendGradientRect(rfbClientPtr cl, cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4; cl->updateBuf[cl->ublen++] = rfbTightFilterGradient; - cl->bytesSent[rfbEncodingTight] += 2; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 2); if (usePixelFormat24) { FilterGradient24(cl, tightBeforeBuf, &cl->format, w, h); @@ -905,7 +905,7 @@ CompressData(rfbClientPtr cl, if (dataLen < TIGHT_MIN_TO_COMPRESS) { memcpy(&cl->updateBuf[cl->ublen], tightBeforeBuf, dataLen); cl->ublen += dataLen; - cl->bytesSent[rfbEncodingTight] += dataLen; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, dataLen); return TRUE; } @@ -955,15 +955,15 @@ static rfbBool SendCompressedData(rfbClientPtr cl, int i, portionLen; cl->updateBuf[cl->ublen++] = compressedLen & 0x7F; - cl->bytesSent[rfbEncodingTight]++; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1); if (compressedLen > 0x7F) { cl->updateBuf[cl->ublen-1] |= 0x80; cl->updateBuf[cl->ublen++] = compressedLen >> 7 & 0x7F; - cl->bytesSent[rfbEncodingTight]++; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1); if (compressedLen > 0x3FFF) { cl->updateBuf[cl->ublen-1] |= 0x80; cl->updateBuf[cl->ublen++] = compressedLen >> 14 & 0xFF; - cl->bytesSent[rfbEncodingTight]++; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1); } } @@ -979,7 +979,7 @@ static rfbBool SendCompressedData(rfbClientPtr cl, memcpy(&cl->updateBuf[cl->ublen], &tightAfterBuf[i], portionLen); cl->ublen += portionLen; } - cl->bytesSent[rfbEncodingTight] += compressedLen; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, compressedLen); return TRUE; } @@ -1686,7 +1686,7 @@ SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h, int quality) } cl->updateBuf[cl->ublen++] = (char)(rfbTightJpeg << 4); - cl->bytesSent[rfbEncodingTight]++; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1); return SendCompressedData(cl, jpegDstDataLen); } diff --git a/libvncserver/tightvnc-filetransfer/rfbtightproto.h b/libvncserver/tightvnc-filetransfer/rfbtightproto.h index 397daba..ef683ae 100644 --- a/libvncserver/tightvnc-filetransfer/rfbtightproto.h +++ b/libvncserver/tightvnc-filetransfer/rfbtightproto.h @@ -28,6 +28,12 @@ #include #include +/* PATH_MAX is not defined in limits.h on some platforms */ +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + + #define rfbSecTypeTight 16 void rfbTightUsage(void); diff --git a/libvncserver/ultra.c b/libvncserver/ultra.c index e1821bb..1e51446 100644 --- a/libvncserver/ultra.c +++ b/libvncserver/ultra.c @@ -101,9 +101,7 @@ rfbSendOneRectEncodingUltra(rfbClientPtr cl, } /* Update statics */ - cl->rectanglesSent[rfbEncodingUltra]++; - cl->bytesSent[rfbEncodingUltra] += (sz_rfbFramebufferUpdateRectHeader - + sz_rfbZlibHeader + lzoAfterBufLen); + rfbStatRecordEncodingSent(cl, rfbEncodingUltra, sz_rfbFramebufferUpdateRectHeader + sz_rfbZlibHeader + lzoAfterBufLen, maxRawSize); if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbZlibHeader > UPDATE_BUF_SIZE) diff --git a/libvncserver/zlib.c b/libvncserver/zlib.c index 321d86f..56bcc6e 100644 --- a/libvncserver/zlib.c +++ b/libvncserver/zlib.c @@ -121,6 +121,7 @@ rfbSendOneRectEncodingZlib(rfbClientPtr cl, zlibAfterBuf = (char *)realloc(zlibAfterBuf, zlibAfterBufSize); } + /* * Convert pixel data to client format. */ @@ -176,9 +177,8 @@ rfbSendOneRectEncodingZlib(rfbClientPtr cl, */ /* Update statics */ - cl->rectanglesSent[rfbEncodingZlib]++; - cl->bytesSent[rfbEncodingZlib] += (sz_rfbFramebufferUpdateRectHeader - + sz_rfbZlibHeader + zlibAfterBufLen); + rfbStatRecordEncodingSent(cl, rfbEncodingZlib, sz_rfbFramebufferUpdateRectHeader + sz_rfbZlibHeader + zlibAfterBufLen, + + w * (cl->format.bitsPerPixel / 8) * h); if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbZlibHeader > UPDATE_BUF_SIZE) diff --git a/libvncserver/zrle.c b/libvncserver/zrle.c index 76123a0..d72993e 100644 --- a/libvncserver/zrle.c +++ b/libvncserver/zrle.c @@ -124,9 +124,8 @@ rfbBool rfbSendRectEncodingZRLE(rfbClientPtr cl, int x, int y, int w, int h) break; } - cl->rectanglesSent[rfbEncodingZRLE]++; - cl->bytesSent[rfbEncodingZRLE] += (sz_rfbFramebufferUpdateRectHeader - + sz_rfbZRLEHeader + ZRLE_BUFFER_LENGTH(&zos->out)); + rfbStatRecordEncodingSent(cl, rfbEncodingZRLE, sz_rfbFramebufferUpdateRectHeader + sz_rfbZRLEHeader + ZRLE_BUFFER_LENGTH(&zos->out), + + w * (cl->format.bitsPerPixel / 8) * h); if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbZRLEHeader > UPDATE_BUF_SIZE) -- cgit v1.2.1