| /* |
| * rfbserver.c - deal with server-side of the RFB protocol. |
| */ |
| |
| /* |
| * Copyright (C) 2011-2012 D. R. Commander |
| * Copyright (C) 2005 Rohit Kumar, Johannes E. Schindelin |
| * Copyright (C) 2002 RealVNC Ltd. |
| * OSXvnc Copyright (C) 2001 Dan McGuirk <[email protected]>. |
| * Original Xvnc code 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. |
| */ |
| |
| #ifdef __STRICT_ANSI__ |
| #define _BSD_SOURCE |
| #define _POSIX_SOURCE |
| #define _XOPEN_SOURCE 600 |
| #endif |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <rfb/rfb.h> |
| #include <rfb/rfbregion.h> |
| #include "private.h" |
| |
| #ifdef LIBVNCSERVER_HAVE_FCNTL_H |
| #include <fcntl.h> |
| #endif |
| |
| #ifdef WIN32 |
| #include <winsock2.h> |
| #include <ws2tcpip.h> |
| #include <io.h> |
| #define write(sock,buf,len) send(sock,buf,len,0) |
| #else |
| #ifdef LIBVNCSERVER_HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #include <pwd.h> |
| #ifdef LIBVNCSERVER_HAVE_SYS_SOCKET_H |
| #include <sys/socket.h> |
| #endif |
| #ifdef LIBVNCSERVER_HAVE_NETINET_IN_H |
| #include <netinet/in.h> |
| #include <netinet/tcp.h> |
| #include <netdb.h> |
| #include <arpa/inet.h> |
| #endif |
| #endif |
| |
| #ifdef DEBUGPROTO |
| #undef DEBUGPROTO |
| #define DEBUGPROTO(x) x |
| #else |
| #define DEBUGPROTO(x) |
| #endif |
| #include <stdarg.h> |
| #include <scale.h> |
| /* stst() */ |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| #ifndef WIN32 |
| /* readdir() */ |
| #include <dirent.h> |
| #endif |
| |
| /* errno */ |
| #include <errno.h> |
| /* strftime() */ |
| #include <time.h> |
| |
| #ifdef LIBVNCSERVER_WITH_WEBSOCKETS |
| #include "rfbssl.h" |
| #endif |
| |
| #ifdef _MSC_VER |
| #define snprintf _snprintf /* Missing in MSVC */ |
| /* Prevent POSIX deprecation warnings */ |
| #define close _close |
| #define strdup _strdup |
| #endif |
| |
| #ifdef WIN32 |
| #ifdef __MINGW32__ |
| #define mkdir(path, perms) mkdir(path) /* Omit the perms argument to match POSIX signature */ |
| #else /* MSVC and other windows compilers */ |
| #define mkdir(path, perms) _mkdir(path) /* Omit the perms argument to match POSIX signature */ |
| #endif /* __MINGW32__ else... */ |
| #define S_ISDIR(m) (((m) & S_IFDIR) == S_IFDIR) |
| #include <direct.h> |
| #endif |
| |
| #ifdef LIBVNCSERVER_HAVE_LIBJPEG |
| /* |
| * Map of quality levels to provide compatibility with TightVNC/TigerVNC |
| * clients. This emulates the behavior of the TigerVNC Server. |
| */ |
| |
| static const int tight2turbo_qual[10] = { |
| 15, 29, 41, 42, 62, 77, 79, 86, 92, 100 |
| }; |
| |
| static const int tight2turbo_subsamp[10] = { |
| 1, 1, 1, 2, 2, 2, 0, 0, 0, 0 |
| }; |
| #endif |
| |
| static void rfbProcessClientProtocolVersion(rfbClientPtr cl); |
| static void rfbProcessClientNormalMessage(rfbClientPtr cl); |
| static void rfbProcessClientInitMessage(rfbClientPtr cl); |
| |
| #ifdef LIBVNCSERVER_HAVE_LIBPTHREAD |
| void rfbIncrClientRef(rfbClientPtr cl) |
| { |
| LOCK(cl->refCountMutex); |
| cl->refCount++; |
| UNLOCK(cl->refCountMutex); |
| } |
| |
| void rfbDecrClientRef(rfbClientPtr cl) |
| { |
| LOCK(cl->refCountMutex); |
| cl->refCount--; |
| if(cl->refCount<=0) /* just to be sure also < 0 */ |
| TSIGNAL(cl->deleteCond); |
| UNLOCK(cl->refCountMutex); |
| } |
| #else |
| void rfbIncrClientRef(rfbClientPtr cl) {} |
| void rfbDecrClientRef(rfbClientPtr cl) {} |
| #endif |
| |
| #ifdef LIBVNCSERVER_HAVE_LIBPTHREAD |
| static MUTEX(rfbClientListMutex); |
| #endif |
| |
| struct rfbClientIterator { |
| rfbClientPtr next; |
| rfbScreenInfoPtr screen; |
| rfbBool closedToo; |
| }; |
| |
| void |
| rfbClientListInit(rfbScreenInfoPtr rfbScreen) |
| { |
| if(sizeof(rfbBool)!=1) { |
| /* a sanity check */ |
| fprintf(stderr,"rfbBool's size is not 1 (%d)!\n",(int)sizeof(rfbBool)); |
| /* we cannot continue, because rfbBool is supposed to be char everywhere */ |
| exit(1); |
| } |
| rfbScreen->clientHead = NULL; |
| INIT_MUTEX(rfbClientListMutex); |
| } |
| |
| rfbClientIteratorPtr |
| rfbGetClientIterator(rfbScreenInfoPtr rfbScreen) |
| { |
| rfbClientIteratorPtr i = |
| (rfbClientIteratorPtr)malloc(sizeof(struct rfbClientIterator)); |
| i->next = NULL; |
| i->screen = rfbScreen; |
| i->closedToo = FALSE; |
| return i; |
| } |
| |
| rfbClientIteratorPtr |
| rfbGetClientIteratorWithClosed(rfbScreenInfoPtr rfbScreen) |
| { |
| rfbClientIteratorPtr i = |
| (rfbClientIteratorPtr)malloc(sizeof(struct rfbClientIterator)); |
| i->next = NULL; |
| i->screen = rfbScreen; |
| i->closedToo = TRUE; |
| return i; |
| } |
| |
| rfbClientPtr |
| rfbClientIteratorHead(rfbClientIteratorPtr i) |
| { |
| #ifdef LIBVNCSERVER_HAVE_LIBPTHREAD |
| if(i->next != 0) { |
| rfbDecrClientRef(i->next); |
| rfbIncrClientRef(i->screen->clientHead); |
| } |
| #endif |
| LOCK(rfbClientListMutex); |
| i->next = i->screen->clientHead; |
| UNLOCK(rfbClientListMutex); |
| return i->next; |
| } |
| |
| rfbClientPtr |
| rfbClientIteratorNext(rfbClientIteratorPtr i) |
| { |
| if(i->next == 0) { |
| LOCK(rfbClientListMutex); |
| i->next = i->screen->clientHead; |
| UNLOCK(rfbClientListMutex); |
| } else { |
| IF_PTHREADS(rfbClientPtr cl = i->next); |
| i->next = i->next->next; |
| IF_PTHREADS(rfbDecrClientRef(cl)); |
| } |
| |
| #ifdef LIBVNCSERVER_HAVE_LIBPTHREAD |
| if(!i->closedToo) |
| while(i->next && i->next->sock<0) |
| i->next = i->next->next; |
| if(i->next) |
| rfbIncrClientRef(i->next); |
| #endif |
| |
| return i->next; |
| } |
| |
| void |
| rfbReleaseClientIterator(rfbClientIteratorPtr iterator) |
| { |
| IF_PTHREADS(if(iterator->next) rfbDecrClientRef(iterator->next)); |
| free(iterator); |
| } |
| |
| |
| /* |
| * rfbNewClientConnection is called from sockets.c when a new connection |
| * comes in. |
| */ |
| |
| void |
| rfbNewClientConnection(rfbScreenInfoPtr rfbScreen, |
| int sock) |
| { |
| rfbNewClient(rfbScreen,sock); |
| } |
| |
| |
| /* |
| * rfbReverseConnection is called to make an outward |
| * connection to a "listening" RFB client. |
| */ |
| |
| rfbClientPtr |
| rfbReverseConnection(rfbScreenInfoPtr rfbScreen, |
| char *host, |
| int port) |
| { |
| int sock; |
| rfbClientPtr cl; |
| |
| if ((sock = rfbConnect(rfbScreen, host, port)) < 0) |
| return (rfbClientPtr)NULL; |
| |
| cl = rfbNewClient(rfbScreen, sock); |
| |
| if (cl) { |
| cl->reverseConnection = TRUE; |
| } |
| |
| return cl; |
| } |
| |
| |
| 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. |
| */ |
| |
| static rfbClientPtr |
| rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen, |
| int sock, |
| rfbBool isUDP) |
| { |
| rfbProtocolVersionMsg pv; |
| rfbClientIteratorPtr iterator; |
| rfbClientPtr cl,cl_; |
| #ifdef LIBVNCSERVER_IPv6 |
| struct sockaddr_storage addr; |
| #else |
| struct sockaddr_in addr; |
| #endif |
| socklen_t addrlen = sizeof(addr); |
| rfbProtocolExtension* extension; |
| |
| cl = (rfbClientPtr)calloc(sizeof(rfbClientRec),1); |
| |
| cl->screen = rfbScreen; |
| cl->sock = sock; |
| cl->viewOnly = FALSE; |
| /* setup pseudo scaling */ |
| cl->scaledScreen = rfbScreen; |
| cl->scaledScreen->scaledScreenRefCount++; |
| |
| rfbResetStats(cl); |
| |
| cl->clientData = NULL; |
| cl->clientGoneHook = rfbDoNothingWithClient; |
| |
| if(isUDP) { |
| rfbLog(" accepted UDP client\n"); |
| } else { |
| #ifdef LIBVNCSERVER_IPv6 |
| char host[1024]; |
| #endif |
| int one=1; |
| |
| getpeername(sock, (struct sockaddr *)&addr, &addrlen); |
| #ifdef LIBVNCSERVER_IPv6 |
| if(getnameinfo((struct sockaddr*)&addr, addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST) != 0) { |
| rfbLogPerror("rfbNewClient: error in getnameinfo"); |
| cl->host = strdup(""); |
| } |
| else |
| cl->host = strdup(host); |
| #else |
| cl->host = strdup(inet_ntoa(addr.sin_addr)); |
| #endif |
| |
| rfbLog(" other clients:\n"); |
| iterator = rfbGetClientIterator(rfbScreen); |
| while ((cl_ = rfbClientIteratorNext(iterator)) != NULL) { |
| rfbLog(" %s\n",cl_->host); |
| } |
| rfbReleaseClientIterator(iterator); |
| |
| if(!rfbSetNonBlocking(sock)) { |
| close(sock); |
| return NULL; |
| } |
| |
| if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, |
| (char *)&one, sizeof(one)) < 0) { |
| rfbLogPerror("setsockopt failed"); |
| close(sock); |
| return NULL; |
| } |
| |
| FD_SET(sock,&(rfbScreen->allFds)); |
| rfbScreen->maxFd = max(sock,rfbScreen->maxFd); |
| |
| INIT_MUTEX(cl->outputMutex); |
| INIT_MUTEX(cl->refCountMutex); |
| INIT_MUTEX(cl->sendMutex); |
| INIT_COND(cl->deleteCond); |
| |
| cl->state = RFB_PROTOCOL_VERSION; |
| |
| cl->reverseConnection = FALSE; |
| cl->readyForSetColourMapEntries = FALSE; |
| cl->useCopyRect = FALSE; |
| cl->preferredEncoding = -1; |
| cl->correMaxWidth = 48; |
| cl->correMaxHeight = 48; |
| #ifdef LIBVNCSERVER_HAVE_LIBZ |
| cl->zrleData = NULL; |
| #endif |
| |
| cl->copyRegion = sraRgnCreate(); |
| cl->copyDX = 0; |
| cl->copyDY = 0; |
| |
| cl->modifiedRegion = |
| sraRgnCreateRect(0,0,rfbScreen->width,rfbScreen->height); |
| |
| INIT_MUTEX(cl->updateMutex); |
| INIT_COND(cl->updateCond); |
| |
| cl->requestedRegion = sraRgnCreate(); |
| |
| cl->format = cl->screen->serverFormat; |
| cl->translateFn = rfbTranslateNone; |
| cl->translateLookupTable = NULL; |
| |
| LOCK(rfbClientListMutex); |
| |
| IF_PTHREADS(cl->refCount = 0); |
| cl->next = rfbScreen->clientHead; |
| cl->prev = NULL; |
| if (rfbScreen->clientHead) |
| rfbScreen->clientHead->prev = cl; |
| |
| rfbScreen->clientHead = cl; |
| UNLOCK(rfbClientListMutex); |
| |
| #if defined(LIBVNCSERVER_HAVE_LIBZ) || defined(LIBVNCSERVER_HAVE_LIBPNG) |
| cl->tightQualityLevel = -1; |
| #ifdef LIBVNCSERVER_HAVE_LIBJPEG |
| cl->tightCompressLevel = TIGHT_DEFAULT_COMPRESSION; |
| cl->turboSubsampLevel = TURBO_DEFAULT_SUBSAMP; |
| { |
| int i; |
| for (i = 0; i < 4; i++) |
| cl->zsActive[i] = FALSE; |
| } |
| #endif |
| #endif |
| |
| cl->fileTransfer.fd = -1; |
| |
| cl->enableCursorShapeUpdates = FALSE; |
| cl->enableCursorPosUpdates = FALSE; |
| cl->useRichCursorEncoding = FALSE; |
| cl->enableLastRectEncoding = FALSE; |
| cl->enableKeyboardLedState = FALSE; |
| cl->enableSupportedMessages = FALSE; |
| cl->enableSupportedEncodings = FALSE; |
| cl->enableServerIdentity = FALSE; |
| cl->lastKeyboardLedState = -1; |
| cl->cursorX = rfbScreen->cursorX; |
| cl->cursorY = rfbScreen->cursorY; |
| cl->useNewFBSize = FALSE; |
| |
| #ifdef LIBVNCSERVER_HAVE_LIBZ |
| cl->compStreamInited = FALSE; |
| cl->compStream.total_in = 0; |
| cl->compStream.total_out = 0; |
| cl->compStream.zalloc = Z_NULL; |
| cl->compStream.zfree = Z_NULL; |
| cl->compStream.opaque = Z_NULL; |
| |
| cl->zlibCompressLevel = 5; |
| #endif |
| |
| cl->progressiveSliceY = 0; |
| |
| cl->extensions = NULL; |
| |
| cl->lastPtrX = -1; |
| |
| #ifdef LIBVNCSERVER_WITH_WEBSOCKETS |
| /* |
| * Wait a few ms for the client to send one of: |
| * - Flash policy request |
| * - WebSockets connection (TLS/SSL or plain) |
| */ |
| if (!webSocketsCheck(cl)) { |
| /* Error reporting handled in webSocketsHandshake */ |
| rfbCloseClient(cl); |
| rfbClientConnectionGone(cl); |
| return NULL; |
| } |
| #endif |
| |
| sprintf(pv,rfbProtocolVersionFormat,rfbScreen->protocolMajorVersion, |
| rfbScreen->protocolMinorVersion); |
| |
| if (rfbWriteExact(cl, pv, sz_rfbProtocolVersionMsg) < 0) { |
| rfbLogPerror("rfbNewClient: write"); |
| rfbCloseClient(cl); |
| rfbClientConnectionGone(cl); |
| return NULL; |
| } |
| } |
| |
| for(extension = rfbGetExtensionIterator(); extension; |
| extension=extension->next) { |
| void* data = NULL; |
| /* if the extension does not have a newClient method, it wants |
| * to be initialized later. */ |
| if(extension->newClient && extension->newClient(cl, &data)) |
| rfbEnableExtension(cl, extension, data); |
| } |
| rfbReleaseExtensionIterator(); |
| |
| switch (cl->screen->newClientHook(cl)) { |
| case RFB_CLIENT_ON_HOLD: |
| cl->onHold = TRUE; |
| break; |
| case RFB_CLIENT_ACCEPT: |
| cl->onHold = FALSE; |
| break; |
| case RFB_CLIENT_REFUSE: |
| rfbCloseClient(cl); |
| rfbClientConnectionGone(cl); |
| cl = NULL; |
| break; |
| } |
| return cl; |
| } |
| |
| rfbClientPtr |
| rfbNewClient(rfbScreenInfoPtr rfbScreen, |
| int sock) |
| { |
| return(rfbNewTCPOrUDPClient(rfbScreen,sock,FALSE)); |
| } |
| |
| rfbClientPtr |
| rfbNewUDPClient(rfbScreenInfoPtr rfbScreen) |
| { |
| return((rfbScreen->udpClient= |
| rfbNewTCPOrUDPClient(rfbScreen,rfbScreen->udpSock,TRUE))); |
| } |
| |
| /* |
| * rfbClientConnectionGone is called from sockets.c just after a connection |
| * has gone away. |
| */ |
| |
| void |
| rfbClientConnectionGone(rfbClientPtr cl) |
| { |
| #if defined(LIBVNCSERVER_HAVE_LIBZ) && defined(LIBVNCSERVER_HAVE_LIBJPEG) |
| int i; |
| #endif |
| |
| LOCK(rfbClientListMutex); |
| |
| if (cl->prev) |
| cl->prev->next = cl->next; |
| else |
| cl->screen->clientHead = cl->next; |
| if (cl->next) |
| cl->next->prev = cl->prev; |
| |
| UNLOCK(rfbClientListMutex); |
| |
| #ifdef LIBVNCSERVER_HAVE_LIBPTHREAD |
| if(cl->screen->backgroundLoop != FALSE) { |
| int i; |
| do { |
| LOCK(cl->refCountMutex); |
| i=cl->refCount; |
| if(i>0) |
| WAIT(cl->deleteCond,cl->refCountMutex); |
| UNLOCK(cl->refCountMutex); |
| } while(i>0); |
| } |
| #endif |
| |
| if(cl->sock>=0) |
| close(cl->sock); |
| |
| if (cl->scaledScreen!=NULL) |
| cl->scaledScreen->scaledScreenRefCount--; |
| |
| #ifdef LIBVNCSERVER_HAVE_LIBZ |
| rfbFreeZrleData(cl); |
| #endif |
| |
| rfbFreeUltraData(cl); |
| |
| /* free buffers holding pixel data before and after encoding */ |
| free(cl->beforeEncBuf); |
| free(cl->afterEncBuf); |
| |
| if(cl->sock>=0) |
| FD_CLR(cl->sock,&(cl->screen->allFds)); |
| |
| cl->clientGoneHook(cl); |
| |
| rfbLog("Client %s gone\n",cl->host); |
| free(cl->host); |
| |
| #ifdef LIBVNCSERVER_HAVE_LIBZ |
| /* Release the compression state structures if any. */ |
| if ( cl->compStreamInited ) { |
| deflateEnd( &(cl->compStream) ); |
| } |
| |
| #ifdef LIBVNCSERVER_HAVE_LIBJPEG |
| for (i = 0; i < 4; i++) { |
| if (cl->zsActive[i]) |
| deflateEnd(&cl->zsStruct[i]); |
| } |
| #endif |
| #endif |
| |
| if (cl->screen->pointerClient == cl) |
| cl->screen->pointerClient = NULL; |
| |
| sraRgnDestroy(cl->modifiedRegion); |
| sraRgnDestroy(cl->requestedRegion); |
| sraRgnDestroy(cl->copyRegion); |
| |
| if (cl->translateLookupTable) free(cl->translateLookupTable); |
| |
| TINI_COND(cl->updateCond); |
| TINI_MUTEX(cl->updateMutex); |
| |
| /* make sure outputMutex is unlocked before destroying */ |
| LOCK(cl->outputMutex); |
| UNLOCK(cl->outputMutex); |
| TINI_MUTEX(cl->outputMutex); |
| |
| LOCK(cl->sendMutex); |
| UNLOCK(cl->sendMutex); |
| TINI_MUTEX(cl->sendMutex); |
| |
| rfbPrintStats(cl); |
| rfbResetStats(cl); |
| |
| free(cl); |
| } |
| |
| |
| /* |
| * rfbProcessClientMessage is called when there is data to read from a client. |
| */ |
| |
| void |
| rfbProcessClientMessage(rfbClientPtr cl) |
| { |
| switch (cl->state) { |
| case RFB_PROTOCOL_VERSION: |
| rfbProcessClientProtocolVersion(cl); |
| return; |
| case RFB_SECURITY_TYPE: |
| rfbProcessClientSecurityType(cl); |
| return; |
| case RFB_AUTHENTICATION: |
| rfbAuthProcessClientMessage(cl); |
| return; |
| case RFB_INITIALISATION: |
| case RFB_INITIALISATION_SHARED: |
| rfbProcessClientInitMessage(cl); |
| return; |
| default: |
| rfbProcessClientNormalMessage(cl); |
| return; |
| } |
| } |
| |
| |
| /* |
| * rfbProcessClientProtocolVersion is called when the client sends its |
| * protocol version. |
| */ |
| |
| static void |
| rfbProcessClientProtocolVersion(rfbClientPtr cl) |
| { |
| rfbProtocolVersionMsg pv; |
| int n, major_, minor_; |
| |
| if ((n = rfbReadExact(cl, pv, sz_rfbProtocolVersionMsg)) <= 0) { |
| if (n == 0) |
| rfbLog("rfbProcessClientProtocolVersion: client gone\n"); |
| else |
| rfbLogPerror("rfbProcessClientProtocolVersion: read"); |
| rfbCloseClient(cl); |
| return; |
| } |
| |
| pv[sz_rfbProtocolVersionMsg] = 0; |
| if (sscanf(pv,rfbProtocolVersionFormat,&major_,&minor_) != 2) { |
| rfbErr("rfbProcessClientProtocolVersion: not a valid RFB client: %s\n", pv); |
| rfbCloseClient(cl); |
| return; |
| } |
| rfbLog("Client Protocol Version %d.%d\n", major_, minor_); |
| |
| if (major_ != rfbProtocolMajorVersion) { |
| rfbErr("RFB protocol version mismatch - server %d.%d, client %d.%d", |
| cl->screen->protocolMajorVersion, cl->screen->protocolMinorVersion, |
| major_,minor_); |
| rfbCloseClient(cl); |
| return; |
| } |
| |
| /* 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->protocolMajorVersion = major_; |
| cl->protocolMinorVersion = minor_; |
| |
| rfbLog("Protocol version sent %d.%d, using %d.%d\n", |
| major_, minor_, rfbProtocolMajorVersion, cl->protocolMinorVersion); |
| |
| rfbAuthNewClient(cl); |
| } |
| |
| |
| void |
| rfbClientSendString(rfbClientPtr cl, const char *reason) |
| { |
| char *buf; |
| int len = strlen(reason); |
| |
| rfbLog("rfbClientSendString(\"%s\")\n", reason); |
| |
| buf = (char *)malloc(4 + len); |
| ((uint32_t *)buf)[0] = Swap32IfLE(len); |
| memcpy(buf + 4, reason, len); |
| |
| if (rfbWriteExact(cl, buf, 4 + len) < 0) |
| rfbLogPerror("rfbClientSendString: write"); |
| free(buf); |
| |
| rfbCloseClient(cl); |
| } |
| |
| /* |
| * rfbClientConnFailed is called when a client connection has failed either |
| * because it talks the wrong protocol or it has failed authentication. |
| */ |
| |
| void |
| rfbClientConnFailed(rfbClientPtr cl, |
| const char *reason) |
| { |
| char *buf; |
| int len = strlen(reason); |
| |
| rfbLog("rfbClientConnFailed(\"%s\")\n", reason); |
| |
| buf = (char *)malloc(8 + len); |
| ((uint32_t *)buf)[0] = Swap32IfLE(rfbConnFailed); |
| ((uint32_t *)buf)[1] = Swap32IfLE(len); |
| memcpy(buf + 8, reason, len); |
| |
| if (rfbWriteExact(cl, buf, 8 + len) < 0) |
| rfbLogPerror("rfbClientConnFailed: write"); |
| free(buf); |
| |
| rfbCloseClient(cl); |
| } |
| |
| |
| /* |
| * rfbProcessClientInitMessage is called when the client sends its |
| * initialisation message. |
| */ |
| |
| static void |
| rfbProcessClientInitMessage(rfbClientPtr cl) |
| { |
| rfbClientInitMsg ci; |
| union { |
| char buf[256]; |
| rfbServerInitMsg si; |
| } u; |
| int len, n; |
| rfbClientIteratorPtr iterator; |
| rfbClientPtr otherCl; |
| rfbExtensionData* extension; |
| |
| if (cl->state == RFB_INITIALISATION_SHARED) { |
| /* In this case behave as though an implicit ClientInit message has |
| * already been received with a shared-flag of true. */ |
| ci.shared = 1; |
| /* Avoid the possibility of exposing the RFB_INITIALISATION_SHARED |
| * state to calling software. */ |
| cl->state = RFB_INITIALISATION; |
| } else { |
| if ((n = rfbReadExact(cl, (char *)&ci,sz_rfbClientInitMsg)) <= 0) { |
| if (n == 0) |
| rfbLog("rfbProcessClientInitMessage: client gone\n"); |
| else |
| rfbLogPerror("rfbProcessClientInitMessage: read"); |
| rfbCloseClient(cl); |
| return; |
| } |
| } |
| |
| memset(u.buf,0,sizeof(u.buf)); |
| |
| u.si.framebufferWidth = Swap16IfLE(cl->screen->width); |
| u.si.framebufferHeight = Swap16IfLE(cl->screen->height); |
| u.si.format = cl->screen->serverFormat; |
| u.si.format.redMax = Swap16IfLE(u.si.format.redMax); |
| u.si.format.greenMax = Swap16IfLE(u.si.format.greenMax); |
| u.si.format.blueMax = Swap16IfLE(u.si.format.blueMax); |
| |
| strncpy(u.buf + sz_rfbServerInitMsg, cl->screen->desktopName, 127); |
| len = strlen(u.buf + sz_rfbServerInitMsg); |
| u.si.nameLength = Swap32IfLE(len); |
| |
| if (rfbWriteExact(cl, u.buf, sz_rfbServerInitMsg + len) < 0) { |
| rfbLogPerror("rfbProcessClientInitMessage: write"); |
| rfbCloseClient(cl); |
| return; |
| } |
| |
| for(extension = cl->extensions; extension;) { |
| rfbExtensionData* next = extension->next; |
| if(extension->extension->init && |
| !extension->extension->init(cl, extension->data)) |
| /* extension requested that it be removed */ |
| rfbDisableExtension(cl, extension->extension); |
| extension = next; |
| } |
| |
| cl->state = RFB_NORMAL; |
| |
| if (!cl->reverseConnection && |
| (cl->screen->neverShared || (!cl->screen->alwaysShared && !ci.shared))) { |
| |
| if (cl->screen->dontDisconnect) { |
| iterator = rfbGetClientIterator(cl->screen); |
| while ((otherCl = rfbClientIteratorNext(iterator)) != NULL) { |
| if ((otherCl != cl) && (otherCl->state == RFB_NORMAL)) { |
| rfbLog("-dontdisconnect: Not shared & existing client\n"); |
| rfbLog(" refusing new client %s\n", cl->host); |
| rfbCloseClient(cl); |
| rfbReleaseClientIterator(iterator); |
| return; |
| } |
| } |
| rfbReleaseClientIterator(iterator); |
| } else { |
| iterator = rfbGetClientIterator(cl->screen); |
| while ((otherCl = rfbClientIteratorNext(iterator)) != NULL) { |
| if ((otherCl != cl) && (otherCl->state == RFB_NORMAL)) { |
| rfbLog("Not shared - closing connection to client %s\n", |
| otherCl->host); |
| rfbCloseClient(otherCl); |
| } |
| } |
| rfbReleaseClientIterator(iterator); |
| } |
| } |
| } |
| |
| /* The values come in based on the scaled screen, we need to convert them to |
| * values based on the man screen's coordinate system |
| */ |
| static rfbBool rectSwapIfLEAndClip(uint16_t* x,uint16_t* y,uint16_t* w,uint16_t* h, |
| rfbClientPtr cl) |
| { |
| int x1=Swap16IfLE(*x); |
| int y1=Swap16IfLE(*y); |
| int w1=Swap16IfLE(*w); |
| int h1=Swap16IfLE(*h); |
| |
| rfbScaledCorrection(cl->scaledScreen, cl->screen, &x1, &y1, &w1, &h1, "rectSwapIfLEAndClip"); |
| *x = x1; |
| *y = y1; |
| *w = w1; |
| *h = h1; |
| |
| if(*w>cl->screen->width-*x) |
| *w=cl->screen->width-*x; |
| /* possible underflow */ |
| if(*w>cl->screen->width-*x) |
| return FALSE; |
| if(*h>cl->screen->height-*y) |
| *h=cl->screen->height-*y; |
| if(*h>cl->screen->height-*y) |
| return FALSE; |
| |
| return TRUE; |
| } |
| |
| /* |
| * Send keyboard state (PointerPos pseudo-encoding). |
| */ |
| |
| rfbBool |
| rfbSendKeyboardLedState(rfbClientPtr cl) |
| { |
| rfbFramebufferUpdateRectHeader rect; |
| |
| if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) { |
| if (!rfbSendUpdateBuf(cl)) |
| return FALSE; |
| } |
| |
| rect.encoding = Swap32IfLE(rfbEncodingKeyboardLedState); |
| rect.r.x = Swap16IfLE(cl->lastKeyboardLedState); |
| rect.r.y = 0; |
| rect.r.w = 0; |
| rect.r.h = 0; |
| |
| memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, |
| sz_rfbFramebufferUpdateRectHeader); |
| cl->ublen += sz_rfbFramebufferUpdateRectHeader; |
| |
| rfbStatRecordEncodingSent(cl, rfbEncodingKeyboardLedState, sz_rfbFramebufferUpdateRectHeader, sz_rfbFramebufferUpdateRectHeader); |
| |
| if (!rfbSendUpdateBuf(cl)) |
| return FALSE; |
| |
| return TRUE; |
| } |
| |
| |
| #define rfbSetBit(buffer, position) (buffer[(position & 255) / 8] |= (1 << (position % 8))) |
| |
| /* |
| * Send rfbEncodingSupportedMessages. |
| */ |
| |
| rfbBool |
| rfbSendSupportedMessages(rfbClientPtr cl) |
| { |
| rfbFramebufferUpdateRectHeader rect; |
| rfbSupportedMessages msgs; |
| |
| if (cl->ublen + sz_rfbFramebufferUpdateRectHeader |
| + sz_rfbSupportedMessages > UPDATE_BUF_SIZE) { |
| if (!rfbSendUpdateBuf(cl)) |
| return FALSE; |
| } |
| |
| rect.encoding = Swap32IfLE(rfbEncodingSupportedMessages); |
| rect.r.x = 0; |
| rect.r.y = 0; |
| rect.r.w = Swap16IfLE(sz_rfbSupportedMessages); |
| rect.r.h = 0; |
| |
| memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, |
| sz_rfbFramebufferUpdateRectHeader); |
| cl->ublen += sz_rfbFramebufferUpdateRectHeader; |
| |
| memset((char *)&msgs, 0, sz_rfbSupportedMessages); |
| rfbSetBit(msgs.client2server, rfbSetPixelFormat); |
| rfbSetBit(msgs.client2server, rfbFixColourMapEntries); |
| rfbSetBit(msgs.client2server, rfbSetEncodings); |
| rfbSetBit(msgs.client2server, rfbFramebufferUpdateRequest); |
| rfbSetBit(msgs.client2server, rfbKeyEvent); |
| rfbSetBit(msgs.client2server, rfbPointerEvent); |
| 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, rfbPalmVNCSetScaleFactor); |
| rfbSetBit(msgs.client2server, rfbXvp); |
| |
| rfbSetBit(msgs.server2client, rfbFramebufferUpdate); |
| rfbSetBit(msgs.server2client, rfbSetColourMapEntries); |
| rfbSetBit(msgs.server2client, rfbBell); |
| rfbSetBit(msgs.server2client, rfbServerCutText); |
| rfbSetBit(msgs.server2client, rfbResizeFrameBuffer); |
| rfbSetBit(msgs.server2client, rfbPalmVNCReSizeFrameBuffer); |
| rfbSetBit(msgs.server2client, rfbXvp); |
| |
| 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; |
| |
| return TRUE; |
| } |
| |
| |
| |
| /* |
| * Send rfbEncodingSupportedEncodings. |
| */ |
| |
| rfbBool |
| rfbSendSupportedEncodings(rfbClientPtr cl) |
| { |
| rfbFramebufferUpdateRectHeader rect; |
| static uint32_t supported[] = { |
| rfbEncodingRaw, |
| rfbEncodingCopyRect, |
| rfbEncodingRRE, |
| rfbEncodingCoRRE, |
| rfbEncodingHextile, |
| #ifdef LIBVNCSERVER_HAVE_LIBZ |
| rfbEncodingZlib, |
| rfbEncodingZRLE, |
| rfbEncodingZYWRLE, |
| #endif |
| #ifdef LIBVNCSERVER_HAVE_LIBJPEG |
| rfbEncodingTight, |
| #endif |
| #ifdef LIBVNCSERVER_HAVE_LIBPNG |
| rfbEncodingTightPng, |
| #endif |
| rfbEncodingUltra, |
| rfbEncodingUltraZip, |
| rfbEncodingXCursor, |
| rfbEncodingRichCursor, |
| rfbEncodingPointerPos, |
| rfbEncodingLastRect, |
| rfbEncodingNewFBSize, |
| rfbEncodingKeyboardLedState, |
| rfbEncodingSupportedMessages, |
| rfbEncodingSupportedEncodings, |
| rfbEncodingServerIdentity, |
| }; |
| uint32_t nEncodings = sizeof(supported) / sizeof(supported[0]), i; |
| |
| /* think rfbSetEncodingsMsg */ |
| |
| if (cl->ublen + sz_rfbFramebufferUpdateRectHeader |
| + (nEncodings * sizeof(uint32_t)) > UPDATE_BUF_SIZE) { |
| if (!rfbSendUpdateBuf(cl)) |
| return FALSE; |
| } |
| |
| rect.encoding = Swap32IfLE(rfbEncodingSupportedEncodings); |
| rect.r.x = 0; |
| rect.r.y = 0; |
| rect.r.w = Swap16IfLE(nEncodings * sizeof(uint32_t)); |
| rect.r.h = Swap16IfLE(nEncodings); |
| |
| memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, |
| sz_rfbFramebufferUpdateRectHeader); |
| cl->ublen += sz_rfbFramebufferUpdateRectHeader; |
| |
| for (i = 0; i < nEncodings; i++) { |
| uint32_t encoding = Swap32IfLE(supported[i]); |
| memcpy(&cl->updateBuf[cl->ublen], (char *)&encoding, sizeof(encoding)); |
| cl->ublen += sizeof(encoding); |
| } |
| |
| rfbStatRecordEncodingSent(cl, rfbEncodingSupportedEncodings, |
| sz_rfbFramebufferUpdateRectHeader+(nEncodings * sizeof(uint32_t)), |
| sz_rfbFramebufferUpdateRectHeader+(nEncodings * sizeof(uint32_t))); |
| |
| if (!rfbSendUpdateBuf(cl)) |
| return FALSE; |
| |
| return TRUE; |
| } |
| |
| |
| void |
| rfbSetServerVersionIdentity(rfbScreenInfoPtr screen, char *fmt, ...) |
| { |
| char buffer[256]; |
| va_list ap; |
| |
| va_start(ap, fmt); |
| vsnprintf(buffer, sizeof(buffer)-1, fmt, ap); |
| va_end(ap); |
| |
| if (screen->versionString!=NULL) free(screen->versionString); |
| screen->versionString = strdup(buffer); |
| } |
| |
| /* |
| * Send rfbEncodingServerIdentity. |
| */ |
| |
| rfbBool |
| rfbSendServerIdentity(rfbClientPtr cl) |
| { |
| rfbFramebufferUpdateRectHeader rect; |
| char buffer[512]; |
| |
| /* tack on our library version */ |
| snprintf(buffer,sizeof(buffer)-1, "%s (%s)", |
| (cl->screen->versionString==NULL ? "unknown" : cl->screen->versionString), |
| LIBVNCSERVER_PACKAGE_STRING); |
| |
| if (cl->ublen + sz_rfbFramebufferUpdateRectHeader |
| + (strlen(buffer)+1) > UPDATE_BUF_SIZE) { |
| if (!rfbSendUpdateBuf(cl)) |
| return FALSE; |
| } |
| |
| rect.encoding = Swap32IfLE(rfbEncodingServerIdentity); |
| rect.r.x = 0; |
| rect.r.y = 0; |
| rect.r.w = Swap16IfLE(strlen(buffer)+1); |
| rect.r.h = 0; |
| |
| memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, |
| sz_rfbFramebufferUpdateRectHeader); |
| cl->ublen += sz_rfbFramebufferUpdateRectHeader; |
| |
| 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; |
| } |
| |
| /* |
| * Send an xvp server message |
| */ |
| |
| rfbBool |
| rfbSendXvp(rfbClientPtr cl, uint8_t version, uint8_t code) |
| { |
| rfbXvpMsg xvp; |
| |
| xvp.type = rfbXvp; |
| xvp.pad = 0; |
| xvp.version = version; |
| xvp.code = code; |
| |
| LOCK(cl->sendMutex); |
| if (rfbWriteExact(cl, (char *)&xvp, sz_rfbXvpMsg) < 0) { |
| rfbLogPerror("rfbSendXvp: write"); |
| rfbCloseClient(cl); |
| } |
| UNLOCK(cl->sendMutex); |
| |
| rfbStatRecordMessageSent(cl, rfbXvp, sz_rfbXvpMsg, sz_rfbXvpMsg); |
| |
| return TRUE; |
| } |
| |
| |
| rfbBool rfbSendTextChatMessage(rfbClientPtr cl, uint32_t length, char *buffer) |
| { |
| rfbTextChatMsg tc; |
| int bytesToSend=0; |
| |
| memset((char *)&tc, 0, sizeof(tc)); |
| tc.type = rfbTextChat; |
| tc.length = Swap32IfLE(length); |
| |
| switch(length) { |
| case rfbTextChatOpen: |
| case rfbTextChatClose: |
| case rfbTextChatFinished: |
| bytesToSend=0; |
| break; |
| default: |
| bytesToSend=length; |
| if (bytesToSend>rfbTextMaxSize) |
| bytesToSend=rfbTextMaxSize; |
| } |
| |
| if (cl->ublen + sz_rfbTextChatMsg + bytesToSend > UPDATE_BUF_SIZE) { |
| if (!rfbSendUpdateBuf(cl)) |
| return FALSE; |
| } |
| |
| memcpy(&cl->updateBuf[cl->ublen], (char *)&tc, sz_rfbTextChatMsg); |
| cl->ublen += sz_rfbTextChatMsg; |
| if (bytesToSend>0) { |
| memcpy(&cl->updateBuf[cl->ublen], buffer, bytesToSend); |
| cl->ublen += bytesToSend; |
| } |
| rfbStatRecordMessageSent(cl, rfbTextChat, sz_rfbTextChatMsg+bytesToSend, sz_rfbTextChatMsg+bytesToSend); |
| |
| if (!rfbSendUpdateBuf(cl)) |
| return FALSE; |
| |
| return TRUE; |
| } |
| |
| #define FILEXFER_ALLOWED_OR_CLOSE_AND_RETURN(msg, cl, ret) \ |
| if ((cl->screen->getFileTransferPermission != NULL \ |
| && cl->screen->getFileTransferPermission(cl) != TRUE) \ |
| || cl->screen->permitFileTransfer != TRUE) { \ |
| rfbLog("%sUltra File Transfer is disabled, dropping client: %s\n", msg, cl->host); \ |
| rfbCloseClient(cl); \ |
| return ret; \ |
| } |
| |
| int DB = 1; |
| |
| rfbBool rfbSendFileTransferMessage(rfbClientPtr cl, uint8_t contentType, uint8_t contentParam, uint32_t size, uint32_t length, const 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); |
| |
| FILEXFER_ALLOWED_OR_CLOSE_AND_RETURN("", cl, FALSE); |
| /* |
| rfbLog("rfbSendFileTransferMessage( %dtype, %dparam, %dsize, %dlen, %p)\n", contentType, contentParam, size, length, buffer); |
| */ |
| LOCK(cl->sendMutex); |
| if (rfbWriteExact(cl, (char *)&ft, sz_rfbFileTransferMsg) < 0) { |
| rfbLogPerror("rfbSendFileTransferMessage: write"); |
| rfbCloseClient(cl); |
| UNLOCK(cl->sendMutex); |
| return FALSE; |
| } |
| |
| if (length>0) |
| { |
| if (rfbWriteExact(cl, buffer, length) < 0) { |
| rfbLogPerror("rfbSendFileTransferMessage: write"); |
| rfbCloseClient(cl); |
| UNLOCK(cl->sendMutex); |
| return FALSE; |
| } |
| } |
| UNLOCK(cl->sendMutex); |
| |
| rfbStatRecordMessageSent(cl, rfbFileTransfer, sz_rfbFileTransferMsg+length, sz_rfbFileTransferMsg+length); |
| |
| return TRUE; |
| } |
| |
| |
| /* |
| * UltraVNC uses Windows Structures |
| */ |
| #define MAX_PATH 260 |
| |
| typedef struct { |
| uint32_t dwLowDateTime; |
| uint32_t dwHighDateTime; |
| } RFB_FILETIME; |
| |
| typedef struct { |
| uint32_t dwFileAttributes; |
| RFB_FILETIME ftCreationTime; |
| RFB_FILETIME ftLastAccessTime; |
| RFB_FILETIME ftLastWriteTime; |
| uint32_t nFileSizeHigh; |
| uint32_t nFileSizeLow; |
| uint32_t dwReserved0; |
| uint32_t dwReserved1; |
| uint8_t cFileName[ MAX_PATH ]; |
| uint8_t cAlternateFileName[ 14 ]; |
| } RFB_FIND_DATA; |
| |
| #define RFB_FILE_ATTRIBUTE_READONLY 0x1 |
| #define RFB_FILE_ATTRIBUTE_HIDDEN 0x2 |
| #define RFB_FILE_ATTRIBUTE_SYSTEM 0x4 |
| #define RFB_FILE_ATTRIBUTE_DIRECTORY 0x10 |
| #define RFB_FILE_ATTRIBUTE_ARCHIVE 0x20 |
| #define RFB_FILE_ATTRIBUTE_NORMAL 0x80 |
| #define RFB_FILE_ATTRIBUTE_TEMPORARY 0x100 |
| #define RFB_FILE_ATTRIBUTE_COMPRESSED 0x800 |
| |
| rfbBool rfbFilenameTranslate2UNIX(rfbClientPtr cl, /* in */ char *path, /* out */ char *unixPath, size_t unixPathMaxLen) |
| { |
| int x; |
| char *home=NULL; |
| |
| FILEXFER_ALLOWED_OR_CLOSE_AND_RETURN("", cl, FALSE); |
| |
| /* |
| * Do not use strncpy() - truncating the file name would probably have undesirable side effects |
| * Instead check if destination buffer is big enough |
| */ |
| if (strlen(path) >= unixPathMaxLen) |
| return FALSE; |
| |
| /* C: */ |
| if (path[0]=='C' && path[1]==':') |
| strcpy(unixPath, &path[2]); |
| else |
| { |
| home = getenv("HOME"); |
| if (home!=NULL) |
| { |
| /* Re-check buffer size */ |
| if ((strlen(path) + strlen(home) + 1) >= unixPathMaxLen) |
| return FALSE; |
| |
| strcpy(unixPath, home); |
| strcat(unixPath,"/"); |
| strcat(unixPath, path); |
| } |
| else |
| strcpy(unixPath, path); |
| } |
| for (x=0;x<strlen(unixPath);x++) |
| if (unixPath[x]=='\\') unixPath[x]='/'; |
| return TRUE; |
| } |
| |
| rfbBool rfbFilenameTranslate2DOS(rfbClientPtr cl, char *unixPath, char *path) |
| { |
| int x; |
| |
| FILEXFER_ALLOWED_OR_CLOSE_AND_RETURN("", cl, FALSE); |
| |
| sprintf(path,"C:%s", unixPath); |
| for (x=2;x<strlen(path);x++) |
| if (path[x]=='/') path[x]='\\'; |
| return TRUE; |
| } |
| |
| rfbBool rfbSendDirContent(rfbClientPtr cl, int length, char *buffer) |
| { |
| char retfilename[MAX_PATH]; |
| char path[MAX_PATH]; |
| struct stat statbuf; |
| RFB_FIND_DATA win32filename; |
| int nOptLen = 0, retval=0; |
| #ifdef WIN32 |
| WIN32_FIND_DATAA winFindData; |
| HANDLE findHandle; |
| int pathLen, basePathLength; |
| char *basePath; |
| #else |
| DIR *dirp=NULL; |
| struct dirent *direntp=NULL; |
| #endif |
| |
| FILEXFER_ALLOWED_OR_CLOSE_AND_RETURN("", cl, FALSE); |
| |
| /* Client thinks we are Winblows */ |
| if (!rfbFilenameTranslate2UNIX(cl, buffer, path, sizeof(path))) |
| return FALSE; |
| |
| if (DB) rfbLog("rfbProcessFileTransfer() rfbDirContentRequest: rfbRDirContent: \"%s\"->\"%s\"\n",buffer, path); |
| |
| #ifdef WIN32 |
| // Create a search string, like C:\folder\* |
| |
| pathLen = strlen(path); |
| basePath = malloc(pathLen + 3); |
| memcpy(basePath, path, pathLen); |
| basePathLength = pathLen; |
| basePath[basePathLength] = '\\'; |
| basePath[basePathLength + 1] = '*'; |
| basePath[basePathLength + 2] = '\0'; |
| |
| // Start a search |
| memset(&winFindData, 0, sizeof(winFindData)); |
| findHandle = FindFirstFileA(path, &winFindData); |
| free(basePath); |
| |
| if (findHandle == INVALID_HANDLE_VALUE) |
| #else |
| dirp=opendir(path); |
| if (dirp==NULL) |
| #endif |
| 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; |
| |
| #ifdef WIN32 |
| while (findHandle != INVALID_HANDLE_VALUE) |
| #else |
| for (direntp=readdir(dirp); direntp!=NULL; direntp=readdir(dirp)) |
| #endif |
| { |
| /* get stats */ |
| #ifdef WIN32 |
| snprintf(retfilename,sizeof(retfilename),"%s/%s", path, winFindData.cFileName); |
| #else |
| snprintf(retfilename,sizeof(retfilename),"%s/%s", path, direntp->d_name); |
| #endif |
| retval = stat(retfilename, &statbuf); |
| |
| if (retval==0) |
| { |
| memset((char *)&win32filename, 0, sizeof(win32filename)); |
| #ifdef WIN32 |
| win32filename.dwFileAttributes = winFindData.dwFileAttributes; |
| win32filename.ftCreationTime.dwLowDateTime = winFindData.ftCreationTime.dwLowDateTime; |
| win32filename.ftCreationTime.dwHighDateTime = winFindData.ftCreationTime.dwHighDateTime; |
| win32filename.ftLastAccessTime.dwLowDateTime = winFindData.ftLastAccessTime.dwLowDateTime; |
| win32filename.ftLastAccessTime.dwHighDateTime = winFindData.ftLastAccessTime.dwHighDateTime; |
| win32filename.ftLastWriteTime.dwLowDateTime = winFindData.ftLastWriteTime.dwLowDateTime; |
| win32filename.ftLastWriteTime.dwHighDateTime = winFindData.ftLastWriteTime.dwHighDateTime; |
| win32filename.nFileSizeLow = winFindData.nFileSizeLow; |
| win32filename.nFileSizeHigh = winFindData.nFileSizeHigh; |
| win32filename.dwReserved0 = winFindData.dwReserved0; |
| win32filename.dwReserved1 = winFindData.dwReserved1; |
| strcpy((char *)win32filename.cFileName, winFindData.cFileName); |
| strcpy((char *)win32filename.cAlternateFileName, winFindData.cAlternateFileName); |
| #else |
| win32filename.dwFileAttributes = Swap32IfBE(RFB_FILE_ATTRIBUTE_NORMAL); |
| if (S_ISDIR(statbuf.st_mode)) |
| win32filename.dwFileAttributes = Swap32IfBE(RFB_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); |
| #endif |
| |
| /* Do not show hidden files (but show how to move up the tree) */ |
| if ((strcmp((char *)win32filename.cFileName, "..")==0) || (win32filename.cFileName[0]!='.')) |
| { |
| nOptLen = sizeof(RFB_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) |
| { |
| #ifdef WIN32 |
| FindClose(findHandle); |
| #else |
| closedir(dirp); |
| #endif |
| return FALSE; |
| } |
| } |
| } |
| |
| #ifdef WIN32 |
| if (FindNextFileA(findHandle, &winFindData) == 0) |
| { |
| FindClose(findHandle); |
| findHandle = INVALID_HANDLE_VALUE; |
| } |
| #endif |
| } |
| #ifdef WIN32 |
| if (findHandle != INVALID_HANDLE_VALUE) |
| { |
| FindClose(findHandle); |
| } |
| #else |
| closedir(dirp); |
| #endif |
| /* 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; |
| |
| FILEXFER_ALLOWED_OR_CLOSE_AND_RETURN("", cl, NULL); |
| /* |
| 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 |
| |
| /* |
| * Don't close the client if we get into this one because |
| * it is called from many places to service file transfers. |
| * Note that permitFileTransfer is checked first. |
| */ |
| if (cl->screen->permitFileTransfer != TRUE || |
| (cl->screen->getFileTransferPermission != NULL |
| && cl->screen->getFileTransferPermission(cl) != TRUE)) { |
| return TRUE; |
| } |
| |
| /* 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<0) { |
| #ifdef WIN32 |
| errno=WSAGetLastError(); |
| #endif |
| 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... */ |
| #ifdef WIN32 |
| errno=WSAGetLastError(); |
| #endif |
| 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) && (nMaxCompSize<bytesRead)) |
| return rfbSendFileTransferMessage(cl, rfbFilePacket, 0, 1, nMaxCompSize, (char *)compBuf); |
| else |
| return rfbSendFileTransferMessage(cl, rfbFilePacket, 0, 0, bytesRead, (char *)readBuf); |
| #else |
| /* We do not support compression of the data stream */ |
| return rfbSendFileTransferMessage(cl, rfbFilePacket, 0, 0, bytesRead, (char *)readBuf); |
| #endif |
| } |
| } |
| } |
| } |
| return TRUE; |
| } |
| |
| rfbBool rfbProcessFileTransfer(rfbClientPtr cl, uint8_t contentType, uint8_t contentParam, uint32_t size, uint32_t length) |
| { |
| char *buffer=NULL, *p=NULL; |
| int retval=0; |
| char filename1[MAX_PATH]; |
| char filename2[MAX_PATH]; |
| char szFileTime[MAX_PATH]; |
| struct stat statbuf; |
| uint32_t sizeHtmp=0; |
| int n=0; |
| char timespec[64]; |
| #ifdef LIBVNCSERVER_HAVE_LIBZ |
| unsigned char compBuff[sz_rfbBlockSize]; |
| unsigned long nRawBytes = sz_rfbBlockSize; |
| int nRet = 0; |
| #endif |
| |
| FILEXFER_ALLOWED_OR_CLOSE_AND_RETURN("", cl, FALSE); |
| |
| /* |
| rfbLog("rfbProcessFileTransfer(%dtype, %dparam, %dsize, %dlen)\n", contentType, contentParam, size, length); |
| */ |
| |
| switch (contentType) { |
| case rfbDirContentRequest: |
| switch (contentParam) { |
| case rfbRDrivesList: /* Client requests the List of Local Drives */ |
| /* |
| rfbLog("rfbProcessFileTransfer() rfbDirContentRequest: rfbRDrivesList:\n"); |
| */ |
| /* Format when filled : "C:\<NULL>D:\<NULL>....Z:\<NULL><NULL> |
| * |
| * We replace the "\" char following the drive letter and ":" |
| * with a char corresponding to the type of drive |
| * We obtain something like "C:l<NULL>D:c<NULL>....Z:n\<NULL><NULL>" |
| * 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 */ |
| if (!rfbFilenameTranslate2UNIX(cl, buffer, filename1, sizeof(filename1))) |
| goto fail; |
| cl->fileTransfer.fd=open(filename1, O_RDONLY, 0744); |
| |
| /* |
| */ |
| if (DB) rfbLog("rfbProcessFileTransfer() rfbFileTransferRequest(\"%s\"->\"%s\") Open: %s fd=%d\n", buffer, filename1, (cl->fileTransfer.fd==-1?"Failed":"Success"), cl->fileTransfer.fd); |
| |
| 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); |
| if (DB) rfbLog("rfbProcessFileTransfer() buffer is now: \"%s\"\n", buffer); |
| } |
| } |
| |
| /* The viewer supports compression if size==1 */ |
| cl->fileTransfer.compressionEnabled = (size==1); |
| |
| /* |
| rfbLog("rfbProcessFileTransfer() rfbFileTransferRequest(\"%s\"->\"%s\")%s\n", buffer, filename1, (size==1?" <Compression Enabled>":"")); |
| */ |
| |
| /* 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; |
| LOCK(cl->sendMutex); |
| if (rfbWriteExact(cl, (char *)&sizeHtmp, 4) < 0) { |
| rfbLogPerror("rfbProcessFileTransfer: write"); |
| rfbCloseClient(cl); |
| UNLOCK(cl->sendMutex); |
| if (buffer!=NULL) free(buffer); |
| return FALSE; |
| } |
| UNLOCK(cl->sendMutex); |
| 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'; |
| strncpy(szFileTime, p+1, sizeof(szFileTime)); |
| szFileTime[sizeof(szFileTime)-1] = '\x00'; /* ensure NULL terminating byte is present, even if copy overflowed */ |
| } 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); |
| |
| if (!rfbFilenameTranslate2UNIX(cl, buffer, filename1, sizeof(filename1))) |
| goto fail; |
| |
| /* 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); |
| if (DB) rfbLog("rfbProcessFileTransfer() rfbFileTransferOffer(\"%s\"->\"%s\") %s %s fd=%d\n", buffer, filename1, (cl->fileTransfer.fd==-1?"Failed":"Success"), (cl->fileTransfer.fd==-1?strerror(errno):""), cl->fileTransfer.fd); |
| /* |
| */ |
| |
| /* 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); |
| if(nRet == Z_OK) |
| retval=write(cl->fileTransfer.fd, (char*)compBuff, nRawBytes); |
| else |
| retval = -1; |
| #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: |
| if (DB) 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: |
| if (DB) 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 */ |
| } |
| } |
| else |
| { |
| if (cl->screen->permitFileTransfer) |
| { |
| rfbLog("rfbProcessFileTransfer() File Transfer Permission Granted!\n"); |
| return rfbSendFileTransferMessage(cl, rfbFileTransferAccess, 0, 1 , 0, ""); /* Permit */ |
| } |
| else |
| { |
| 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 */ |
| if (!rfbFilenameTranslate2UNIX(cl, buffer, filename1, sizeof(filename1))) |
| goto fail; |
| retval = mkdir(filename1, 0755); |
| if (DB) 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 */ |
| if (!rfbFilenameTranslate2UNIX(cl, buffer, filename1, sizeof(filename1))) |
| goto fail; |
| 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'; |
| if (!rfbFilenameTranslate2UNIX(cl, buffer, filename1, sizeof(filename1))) |
| goto fail; |
| if (!rfbFilenameTranslate2UNIX(cl, p+1, filename2, sizeof(filename2))) |
| goto fail; |
| retval = rename(filename1,filename2); |
| if (DB) 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; |
| |
| fail: |
| if (buffer!=NULL) free(buffer); |
| return FALSE; |
| } |
| |
| /* |
| * rfbProcessClientNormalMessage is called when the client has sent a normal |
| * protocol message. |
| */ |
| |
| static void |
| 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]; |
| |
| #ifdef LIBVNCSERVER_WITH_WEBSOCKETS |
| if (cl->wsctx && webSocketCheckDisconnect(cl)) |
| return; |
| #endif |
| |
| if ((n = rfbReadExact(cl, (char *)&msg, 1)) <= 0) { |
| if (n != 0) |
| rfbLogPerror("rfbProcessClientNormalMessage: read"); |
| rfbCloseClient(cl); |
| return; |
| } |
| |
| switch (msg.type) { |
| |
| case rfbSetPixelFormat: |
| |
| if ((n = rfbReadExact(cl, ((char *)&msg) + 1, |
| sz_rfbSetPixelFormatMsg - 1)) <= 0) { |
| if (n != 0) |
| rfbLogPerror("rfbProcessClientNormalMessage: read"); |
| rfbCloseClient(cl); |
| return; |
| } |
| |
| cl->format.bitsPerPixel = msg.spf.format.bitsPerPixel; |
| cl->format.depth = msg.spf.format.depth; |
| cl->format.bigEndian = (msg.spf.format.bigEndian ? TRUE : FALSE); |
| cl->format.trueColour = (msg.spf.format.trueColour ? TRUE : FALSE); |
| cl->format.redMax = Swap16IfLE(msg.spf.format.redMax); |
| cl->format.greenMax = Swap16IfLE(msg.spf.format.greenMax); |
| cl->format.blueMax = Swap16IfLE(msg.spf.format.blueMax); |
| cl->format.redShift = msg.spf.format.redShift; |
| cl->format.greenShift = msg.spf.format.greenShift; |
| cl->format.blueShift = msg.spf.format.blueShift; |
| |
| cl->readyForSetColourMapEntries = TRUE; |
| cl->screen->setTranslateFunction(cl); |
| |
| rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbSetPixelFormatMsg, sz_rfbSetPixelFormatMsg); |
| |
| return; |
| |
| |
| case rfbFixColourMapEntries: |
| if ((n = rfbReadExact(cl, ((char *)&msg) + 1, |
| sz_rfbFixColourMapEntriesMsg - 1)) <= 0) { |
| if (n != 0) |
| rfbLogPerror("rfbProcessClientNormalMessage: read"); |
| 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: |
| { |
| |
| if ((n = rfbReadExact(cl, ((char *)&msg) + 1, |
| sz_rfbSetEncodingsMsg - 1)) <= 0) { |
| if (n != 0) |
| rfbLogPerror("rfbProcessClientNormalMessage: read"); |
| rfbCloseClient(cl); |
| return; |
| } |
| |
| 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; |
| #if defined(LIBVNCSERVER_HAVE_LIBZ) || defined(LIBVNCSERVER_HAVE_LIBPNG) |
| cl->tightQualityLevel = -1; |
| #ifdef LIBVNCSERVER_HAVE_LIBJPEG |
| cl->tightCompressLevel = TIGHT_DEFAULT_COMPRESSION; |
| cl->turboSubsampLevel = TURBO_DEFAULT_SUBSAMP; |
| cl->turboQualityLevel = -1; |
| #endif |
| #endif |
| |
| |
| for (i = 0; i < msg.se.nEncodings; i++) { |
| if ((n = rfbReadExact(cl, (char *)&enc, 4)) <= 0) { |
| if (n != 0) |
| rfbLogPerror("rfbProcessClientNormalMessage: read"); |
| rfbCloseClient(cl); |
| return; |
| } |
| enc = Swap32IfLE(enc); |
| |
| switch (enc) { |
| |
| case rfbEncodingCopyRect: |
| cl->useCopyRect = TRUE; |
| break; |
| case rfbEncodingRaw: |
| case rfbEncodingRRE: |
| case rfbEncodingCoRRE: |
| case rfbEncodingHextile: |
| case rfbEncodingUltra: |
| #ifdef LIBVNCSERVER_HAVE_LIBZ |
| case rfbEncodingZlib: |
| case rfbEncodingZRLE: |
| case rfbEncodingZYWRLE: |
| #ifdef LIBVNCSERVER_HAVE_LIBJPEG |
| case rfbEncodingTight: |
| #endif |
| #endif |
| #ifdef LIBVNCSERVER_HAVE_LIBPNG |
| case rfbEncodingTightPng: |
| #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", |
| cl->host); |
| /* if cursor was drawn, hide the cursor */ |
| if(!cl->enableCursorShapeUpdates) |
| rfbRedrawAfterHideCursor(cl,NULL); |
| |
| cl->enableCursorShapeUpdates = TRUE; |
| cl->cursorWasChanged = TRUE; |
| } |
| break; |
| case rfbEncodingRichCursor: |
| rfbLog("Enabling full-color cursor updates for client %s\n", |
| cl->host); |
| /* if cursor was drawn, hide the cursor */ |
| if(!cl->enableCursorShapeUpdates) |
| rfbRedrawAfterHideCursor(cl,NULL); |
| |
| cl->enableCursorShapeUpdates = TRUE; |
| cl->useRichCursorEncoding = TRUE; |
| cl->cursorWasChanged = TRUE; |
| break; |
| case rfbEncodingPointerPos: |
| if (!cl->enableCursorPosUpdates) { |
| rfbLog("Enabling cursor position updates for client %s\n", |
| cl->host); |
| cl->enableCursorPosUpdates = TRUE; |
| cl->cursorWasMoved = TRUE; |
| } |
| break; |
| case rfbEncodingLastRect: |
| if (!cl->enableLastRectEncoding) { |
| rfbLog("Enabling LastRect protocol extension for client " |
| "%s\n", cl->host); |
| cl->enableLastRectEncoding = TRUE; |
| } |
| break; |
| case rfbEncodingNewFBSize: |
| if (!cl->useNewFBSize) { |
| rfbLog("Enabling NewFBSize protocol extension for client " |
| "%s\n", cl->host); |
| cl->useNewFBSize = TRUE; |
| } |
| break; |
| case rfbEncodingKeyboardLedState: |
| if (!cl->enableKeyboardLedState) { |
| rfbLog("Enabling KeyboardLedState protocol extension for client " |
| "%s\n", cl->host); |
| cl->enableKeyboardLedState = TRUE; |
| } |
| break; |
| case rfbEncodingSupportedMessages: |
| if (!cl->enableSupportedMessages) { |
| rfbLog("Enabling SupportedMessages protocol extension for client " |
| "%s\n", cl->host); |
| cl->enableSupportedMessages = TRUE; |
| } |
| break; |
| case rfbEncodingSupportedEncodings: |
| if (!cl->enableSupportedEncodings) { |
| rfbLog("Enabling SupportedEncodings protocol extension for client " |
| "%s\n", cl->host); |
| cl->enableSupportedEncodings = TRUE; |
| } |
| break; |
| case rfbEncodingServerIdentity: |
| if (!cl->enableServerIdentity) { |
| rfbLog("Enabling ServerIdentity protocol extension for client " |
| "%s\n", cl->host); |
| cl->enableServerIdentity = TRUE; |
| } |
| break; |
| case rfbEncodingXvp: |
| rfbLog("Enabling Xvp protocol extension for client " |
| "%s\n", cl->host); |
| if (!rfbSendXvp(cl, 1, rfbXvp_Init)) { |
| rfbCloseClient(cl); |
| return; |
| } |
| break; |
| default: |
| #if defined(LIBVNCSERVER_HAVE_LIBZ) || defined(LIBVNCSERVER_HAVE_LIBPNG) |
| if ( enc >= (uint32_t)rfbEncodingCompressLevel0 && |
| enc <= (uint32_t)rfbEncodingCompressLevel9 ) { |
| cl->zlibCompressLevel = enc & 0x0F; |
| #ifdef LIBVNCSERVER_HAVE_LIBJPEG |
| cl->tightCompressLevel = enc & 0x0F; |
| rfbLog("Using compression level %d for client %s\n", |
| cl->tightCompressLevel, cl->host); |
| #endif |
| } else if ( enc >= (uint32_t)rfbEncodingQualityLevel0 && |
| enc <= (uint32_t)rfbEncodingQualityLevel9 ) { |
| cl->tightQualityLevel = enc & 0x0F; |
| rfbLog("Using image quality level %d for client %s\n", |
| cl->tightQualityLevel, cl->host); |
| #ifdef LIBVNCSERVER_HAVE_LIBJPEG |
| cl->turboQualityLevel = tight2turbo_qual[enc & 0x0F]; |
| cl->turboSubsampLevel = tight2turbo_subsamp[enc & 0x0F]; |
| rfbLog("Using JPEG subsampling %d, Q%d for client %s\n", |
| cl->turboSubsampLevel, cl->turboQualityLevel, cl->host); |
| } else if ( enc >= (uint32_t)rfbEncodingFineQualityLevel0 + 1 && |
| enc <= (uint32_t)rfbEncodingFineQualityLevel100 ) { |
| cl->turboQualityLevel = enc & 0xFF; |
| rfbLog("Using fine quality level %d for client %s\n", |
| cl->turboQualityLevel, cl->host); |
| } else if ( enc >= (uint32_t)rfbEncodingSubsamp1X && |
| enc <= (uint32_t)rfbEncodingSubsampGray ) { |
| cl->turboSubsampLevel = enc & 0xFF; |
| rfbLog("Using subsampling level %d for client %s\n", |
| cl->turboSubsampLevel, cl->host); |
| #endif |
| } else |
| #endif |
| { |
| rfbExtensionData* e; |
| for(e = cl->extensions; e;) { |
| rfbExtensionData* next = e->next; |
| if(e->extension->enablePseudoEncoding && |
| e->extension->enablePseudoEncoding(cl, |
| &e->data, (int)enc)) |
| /* ext handles this encoding */ |
| break; |
| e = next; |
| } |
| if(e == NULL) { |
| rfbBool handled = FALSE; |
| /* if the pseudo encoding is not handled by the |
| enabled extensions, search through all |
| extensions. */ |
| rfbProtocolExtension* e; |
| |
| for(e = rfbGetExtensionIterator(); e;) { |
| int* encs = e->pseudoEncodings; |
| while(encs && *encs!=0) { |
| if(*encs==(int)enc) { |
| void* data = NULL; |
| if(!e->enablePseudoEncoding(cl, &data, (int)enc)) { |
| rfbLog("Installed extension pretends to handle pseudo encoding 0x%x, but does not!\n",(int)enc); |
| } else { |
| rfbEnableExtension(cl, e, data); |
| handled = TRUE; |
| e = NULL; |
| break; |
| } |
| } |
| encs++; |
| } |
| |
| if(e) |
| e = e->next; |
| } |
| rfbReleaseExtensionIterator(); |
| |
| if(!handled) |
| rfbLog("rfbProcessClientNormalMessage: " |
| "ignoring unsupported encoding type %s\n", |
| encodingName(enc,encBuf,sizeof(encBuf))); |
| } |
| } |
| } |
| } |
| |
| |
| |
| if (cl->preferredEncoding == -1) { |
| 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); |
| cl->enableCursorPosUpdates = FALSE; |
| } |
| |
| return; |
| } |
| |
| |
| case rfbFramebufferUpdateRequest: |
| { |
| sraRegionPtr tmpRegion; |
| |
| if ((n = rfbReadExact(cl, ((char *)&msg) + 1, |
| sz_rfbFramebufferUpdateRequestMsg-1)) <= 0) { |
| if (n != 0) |
| rfbLogPerror("rfbProcessClientNormalMessage: read"); |
| rfbCloseClient(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 |
| */ |
| if(!rectSwapIfLEAndClip(&msg.fur.x,&msg.fur.y,&msg.fur.w,&msg.fur.h,cl)) |
| { |
| rfbLog("Warning, ignoring rfbFramebufferUpdateRequest: %dXx%dY-%dWx%dH\n",msg.fur.x, msg.fur.y, msg.fur.w, msg.fur.h); |
| return; |
| } |
| |
| |
| tmpRegion = |
| sraRgnCreateRect(msg.fur.x, |
| msg.fur.y, |
| msg.fur.x+msg.fur.w, |
| msg.fur.y+msg.fur.h); |
| |
| LOCK(cl->updateMutex); |
| sraRgnOr(cl->requestedRegion,tmpRegion); |
| |
| if (!cl->readyForSetColourMapEntries) { |
| /* client hasn't sent a SetPixelFormat so is using server's */ |
| cl->readyForSetColourMapEntries = TRUE; |
| if (!cl->format.trueColour) { |
| if (!rfbSetClientColourMap(cl, 0, 0)) { |
| sraRgnDestroy(tmpRegion); |
| TSIGNAL(cl->updateCond); |
| UNLOCK(cl->updateMutex); |
| return; |
| } |
| } |
| } |
| |
| if (!msg.fur.incremental) { |
| sraRgnOr(cl->modifiedRegion,tmpRegion); |
| sraRgnSubtract(cl->copyRegion,tmpRegion); |
| } |
| TSIGNAL(cl->updateCond); |
| UNLOCK(cl->updateMutex); |
| |
| sraRgnDestroy(tmpRegion); |
| |
| return; |
| } |
| |
| case rfbKeyEvent: |
| |
| if ((n = rfbReadExact(cl, ((char *)&msg) + 1, |
| sz_rfbKeyEventMsg - 1)) <= 0) { |
| if (n != 0) |
| rfbLogPerror("rfbProcessClientNormalMessage: read"); |
| rfbCloseClient(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); |
| } |
| |
| return; |
| |
| |
| case rfbPointerEvent: |
| |
| if ((n = rfbReadExact(cl, ((char *)&msg) + 1, |
| sz_rfbPointerEventMsg - 1)) <= 0) { |
| if (n != 0) |
| rfbLogPerror("rfbProcessClientNormalMessage: read"); |
| rfbCloseClient(cl); |
| return; |
| } |
| |
| rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbPointerEventMsg, sz_rfbPointerEventMsg); |
| |
| if (cl->screen->pointerClient && cl->screen->pointerClient != cl) |
| return; |
| |
| if (msg.pe.buttonMask == 0) |
| cl->screen->pointerClient = NULL; |
| else |
| cl->screen->pointerClient = cl; |
| |
| if(!cl->viewOnly) { |
| if (msg.pe.buttonMask != cl->lastPtrButtons || |
| cl->screen->deferPtrUpdateTime == 0) { |
| cl->screen->ptrAddEvent(msg.pe.buttonMask, |
| ScaleX(cl->scaledScreen, cl->screen, Swap16IfLE(msg.pe.x)), |
| ScaleY(cl->scaledScreen, cl->screen, Swap16IfLE(msg.pe.y)), |
| cl); |
| cl->lastPtrButtons = msg.pe.buttonMask; |
| } else { |
| cl->lastPtrX = ScaleX(cl->scaledScreen, cl->screen, Swap16IfLE(msg.pe.x)); |
| cl->lastPtrY = ScaleY(cl->scaledScreen, cl->screen, Swap16IfLE(msg.pe.y)); |
| cl->lastPtrButtons = msg.pe.buttonMask; |
| } |
| } |
| 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<rfbTextMaxSize)) |
| { |
| str = (char *)malloc(msg.tc.length); |
| if (str==NULL) |
| { |
| rfbLog("Unable to malloc %d bytes for a TextChat Message\n", msg.tc.length); |
| rfbCloseClient(cl); |
| return; |
| } |
| if ((n = rfbReadExact(cl, str, msg.tc.length)) <= 0) { |
| if (n != 0) |
| rfbLogPerror("rfbProcessClientNormalMessage: read"); |
| free(str); |
| rfbCloseClient(cl); |
| return; |
| } |
| rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbTextChatMsg+msg.tc.length, sz_rfbTextChatMsg+msg.tc.length); |
| } |
| else |
| { |
| /* This should never happen */ |
| rfbLog("client sent us a Text Message that is too big %d>%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, |
| sz_rfbClientCutTextMsg - 1)) <= 0) { |
| if (n != 0) |
| rfbLogPerror("rfbProcessClientNormalMessage: read"); |
| rfbCloseClient(cl); |
| return; |
| } |
| |
| msg.cct.length = Swap32IfLE(msg.cct.length); |
| |
| str = (char *)malloc(msg.cct.length); |
| if (str == NULL) { |
| rfbLogPerror("rfbProcessClientNormalMessage: not enough memory"); |
| rfbCloseClient(cl); |
| return; |
| } |
| |
| if ((n = rfbReadExact(cl, str, msg.cct.length)) <= 0) { |
| if (n != 0) |
| rfbLogPerror("rfbProcessClientNormalMessage: read"); |
| free(str); |
| 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); |
| } |
| free(str); |
| |
| return; |
| |
| 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; |
| } |
| |
| if (msg.ssc.scale == 0) { |
| rfbLogPerror("rfbProcessClientNormalMessage: will not accept a scale factor of zero"); |
| 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, |
| sz_rfbSetScaleMsg - 1)) <= 0) { |
| if (n != 0) |
| rfbLogPerror("rfbProcessClientNormalMessage: read"); |
| rfbCloseClient(cl); |
| return; |
| } |
| |
| if (msg.ssc.scale == 0) { |
| rfbLogPerror("rfbProcessClientNormalMessage: will not accept a scale factor of zero"); |
| 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 rfbXvp: |
| |
| if ((n = rfbReadExact(cl, ((char *)&msg) + 1, |
| sz_rfbXvpMsg - 1)) <= 0) { |
| if (n != 0) |
| rfbLogPerror("rfbProcessClientNormalMessage: read"); |
| rfbCloseClient(cl); |
| return; |
| } |
| rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbXvpMsg, sz_rfbXvpMsg); |
| |
| /* only version when is defined, so echo back a fail */ |
| if(msg.xvp.version != 1) { |
| rfbSendXvp(cl, msg.xvp.version, rfbXvp_Fail); |
| } |
| else { |
| /* if the hook exists and fails, send a fail msg */ |
| if(cl->screen->xvpHook && !cl->screen->xvpHook(cl, msg.xvp.version, msg.xvp.code)) |
| rfbSendXvp(cl, 1, rfbXvp_Fail); |
| } |
| return; |
| |
| default: |
| { |
| rfbExtensionData *e,*next; |
| |
| for(e=cl->extensions; e;) { |
| 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; |
| } |
| |
| rfbLog("rfbProcessClientNormalMessage: unknown message type %d\n", |
| msg.type); |
| rfbLog(" ... closing connection\n"); |
| rfbCloseClient(cl); |
| return; |
| } |
| } |
| } |
| |
| |
| |
| /* |
| * rfbSendFramebufferUpdate - send the currently pending framebuffer update to |
| * the RFB client. |
| * givenUpdateRegion is not changed. |
| */ |
| |
| rfbBool |
| rfbSendFramebufferUpdate(rfbClientPtr cl, |
| sraRegionPtr givenUpdateRegion) |
| { |
| sraRectangleIterator* i=NULL; |
| sraRect rect; |
| int nUpdateRegionRects; |
| rfbFramebufferUpdateMsg *fu = (rfbFramebufferUpdateMsg *)cl->updateBuf; |
| sraRegionPtr updateRegion,updateCopyRegion,tmpRegion; |
| int dx, dy; |
| rfbBool sendCursorShape = FALSE; |
| rfbBool sendCursorPos = FALSE; |
| rfbBool sendKeyboardLedState = FALSE; |
| rfbBool sendSupportedMessages = FALSE; |
| rfbBool sendSupportedEncodings = FALSE; |
| rfbBool sendServerIdentity = FALSE; |
| rfbBool result = TRUE; |
| |
| |
| if(cl->screen->displayHook) |
| cl->screen->displayHook(cl); |
| |
| /* |
| * If framebuffer size was changed and the client supports NewFBSize |
| * encoding, just send NewFBSize marker and return. |
| */ |
| |
| if (cl->useNewFBSize && cl->newFBSizePending) { |
| LOCK(cl->updateMutex); |
| cl->newFBSizePending = FALSE; |
| UNLOCK(cl->updateMutex); |
| fu->type = rfbFramebufferUpdate; |
| fu->nRects = Swap16IfLE(1); |
| cl->ublen = sz_rfbFramebufferUpdateMsg; |
| if (!rfbSendNewFBSize(cl, cl->scaledScreen->width, cl->scaledScreen->height)) { |
| if(cl->screen->displayFinishedHook) |
| cl->screen->displayFinishedHook(cl, FALSE); |
| return FALSE; |
| } |
| result = rfbSendUpdateBuf(cl); |
| if(cl->screen->displayFinishedHook) |
| cl->screen->displayFinishedHook(cl, result); |
| return result; |
| } |
| |
| /* |
| * If this client understands cursor shape updates, cursor should be |
| * removed from the framebuffer. Otherwise, make sure it's put up. |
| */ |
| |
| if (cl->enableCursorShapeUpdates) { |
| if (cl->cursorWasChanged && cl->readyForSetColourMapEntries) |
| sendCursorShape = TRUE; |
| } |
| |
| /* |
| * Do we plan to send cursor position update? |
| */ |
| |
| if (cl->enableCursorPosUpdates && cl->cursorWasMoved) |
| sendCursorPos = TRUE; |
| |
| /* |
| * Do we plan to send a keyboard state update? |
| */ |
| if ((cl->enableKeyboardLedState) && |
| (cl->screen->getKeyboardLedStateHook!=NULL)) |
| { |
| int x; |
| x=cl->screen->getKeyboardLedStateHook(cl->screen); |
| if (x!=cl->lastKeyboardLedState) |
| { |
| sendKeyboardLedState = TRUE; |
| cl->lastKeyboardLedState=x; |
| } |
| } |
| |
| /* |
| * Do we plan to send a rfbEncodingSupportedMessages? |
| */ |
| if (cl->enableSupportedMessages) |
| { |
| sendSupportedMessages = TRUE; |
| /* We only send this message ONCE <per setEncodings message received> |
| * (We disable it here) |
| */ |
| cl->enableSupportedMessages = FALSE; |
| } |
| /* |
| * Do we plan to send a rfbEncodingSupportedEncodings? |
| */ |
| if (cl->enableSupportedEncodings) |
| { |
| sendSupportedEncodings = TRUE; |
| /* We only send this message ONCE <per setEncodings message received> |
| * (We disable it here) |
| */ |
| cl->enableSupportedEncodings = FALSE; |
| } |
| /* |
| * Do we plan to send a rfbEncodingServerIdentity? |
| */ |
| if (cl->enableServerIdentity) |
| { |
| sendServerIdentity = TRUE; |
| /* We only send this message ONCE <per setEncodings message received> |
| * (We disable it here) |
| */ |
| cl->enableServerIdentity = FALSE; |
| } |
| |
| LOCK(cl->updateMutex); |
| |
| /* |
| * The modifiedRegion may overlap the destination copyRegion. We remove |
| * any overlapping bits from the copyRegion (since they'd only be |
| * overwritten anyway). |
| */ |
| |
| sraRgnSubtract(cl->copyRegion,cl->modifiedRegion); |
| |
| /* |
| * The client is interested in the region requestedRegion. The region |
| * which should be updated now is the intersection of requestedRegion |
| * and the union of modifiedRegion and copyRegion. If it's empty then |
| * no update is needed. |
| */ |
| |
| updateRegion = sraRgnCreateRgn(givenUpdateRegion); |
| if(cl->screen->progressiveSliceHeight>0) { |
| int height=cl->screen->progressiveSliceHeight, |
| y=cl->progressiveSliceY; |
| sraRegionPtr bbox=sraRgnBBox(updateRegion); |
| sraRect rect; |
| if(sraRgnPopRect(bbox,&rect,0)) { |
| sraRegionPtr slice; |
| if(y<rect.y1 || y>=rect.y2) |
| y=rect.y1; |
| slice=sraRgnCreateRect(0,y,cl->screen->width,y+height); |
| sraRgnAnd(updateRegion,slice); |
| sraRgnDestroy(slice); |
| } |
| sraRgnDestroy(bbox); |
| y+=height; |
| if(y>=cl->screen->height) |
| y=0; |
| cl->progressiveSliceY=y; |
| } |
| |
| sraRgnOr(updateRegion,cl->copyRegion); |
| if(!sraRgnAnd(updateRegion,cl->requestedRegion) && |
| sraRgnEmpty(updateRegion) && |
| (cl->enableCursorShapeUpdates || |
| (cl->cursorX == cl->screen->cursorX && cl->cursorY == cl->screen->cursorY)) && |
| !sendCursorShape && !sendCursorPos && !sendKeyboardLedState && |
| !sendSupportedMessages && !sendSupportedEncodings && !sendServerIdentity) { |
| sraRgnDestroy(updateRegion); |
| UNLOCK(cl->updateMutex); |
| if(cl->screen->displayFinishedHook) |
| cl->screen->displayFinishedHook(cl, TRUE); |
| return TRUE; |
| } |
| |
| /* |
| * We assume that the client doesn't have any pixel data outside the |
| * requestedRegion. In other words, both the source and destination of a |
| * copy must lie within requestedRegion. So the region we can send as a |
| * copy is the intersection of the copyRegion with both the requestedRegion |
| * and the requestedRegion translated by the amount of the copy. We set |
| * updateCopyRegion to this. |
| */ |
| |
| updateCopyRegion = sraRgnCreateRgn(cl->copyRegion); |
| sraRgnAnd(updateCopyRegion,cl->requestedRegion); |
| tmpRegion = sraRgnCreateRgn(cl->requestedRegion); |
| sraRgnOffset(tmpRegion,cl->copyDX,cl->copyDY); |
| sraRgnAnd(updateCopyRegion,tmpRegion); |
| sraRgnDestroy(tmpRegion); |
| dx = cl->copyDX; |
| dy = cl->copyDY; |
| |
| /* |
| * Next we remove updateCopyRegion from updateRegion so that updateRegion |
| * is the part of this update which is sent as ordinary pixel data (i.e not |
| * a copy). |
| */ |
| |
| sraRgnSubtract(updateRegion,updateCopyRegion); |
| |
| /* |
| * Finally we leave modifiedRegion to be the remainder (if any) of parts of |
| * the screen which are modified but outside the requestedRegion. We also |
| * empty both the requestedRegion and the copyRegion - note that we never |
| * carry over a copyRegion for a future update. |
| */ |
| |
| sraRgnOr(cl->modifiedRegion,cl->copyRegion); |
| sraRgnSubtract(cl->modifiedRegion,updateRegion); |
| sraRgnSubtract(cl->modifiedRegion,updateCopyRegion); |
| |
| sraRgnMakeEmpty(cl->requestedRegion); |
| sraRgnMakeEmpty(cl->copyRegion); |
| cl->copyDX = 0; |
| cl->copyDY = 0; |
| |
| UNLOCK(cl->updateMutex); |
| |
| if (!cl->enableCursorShapeUpdates) { |
| if(cl->cursorX != cl->screen->cursorX || cl->cursorY != cl->screen->cursorY) { |
| rfbRedrawAfterHideCursor(cl,updateRegion); |
| LOCK(cl->screen->cursorMutex); |
| cl->cursorX = cl->screen->cursorX; |
| cl->cursorY = cl->screen->cursorY; |
| UNLOCK(cl->screen->cursorMutex); |
| rfbRedrawAfterHideCursor(cl,updateRegion); |
| } |
| rfbShowCursor(cl); |
| } |
| |
| /* |
| * Now send the update. |
| */ |
| |
| rfbStatRecordMessageSent(cl, rfbFramebufferUpdate, 0, 0); |
| if (cl->preferredEncoding == rfbEncodingCoRRE) { |
| nUpdateRegionRects = 0; |
| |
| for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);){ |
| int x = rect.x1; |
| int y = rect.y1; |
| int w = rect.x2 - x; |
| int h = rect.y2 - y; |
| int rectsPerRow, rows; |
| /* We need to count the number of rects in the scaled screen */ |
| if (cl->screen!=cl->scaledScreen) |
| rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h, "rfbSendFramebufferUpdate"); |
| rectsPerRow = (w-1)/cl->correMaxWidth+1; |
| rows = (h-1)/cl->correMaxHeight+1; |
| nUpdateRegionRects += rectsPerRow*rows; |
| } |
| sraRgnReleaseIterator(i); i=NULL; |
| } else if (cl->preferredEncoding == rfbEncodingUltra) { |
| nUpdateRegionRects = 0; |
| |
| for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);){ |
| int x = rect.x1; |
| int y = rect.y1; |
| int w = rect.x2 - x; |
| int h = rect.y2 - y; |
| /* We need to count the number of rects in the scaled screen */ |
| if (cl->screen!=cl->scaledScreen) |
| rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h, "rfbSendFramebufferUpdate"); |
| nUpdateRegionRects += (((h-1) / (ULTRA_MAX_SIZE( w ) / w)) + 1); |
| } |
| sraRgnReleaseIterator(i); i=NULL; |
| #ifdef LIBVNCSERVER_HAVE_LIBZ |
| } else if (cl->preferredEncoding == rfbEncodingZlib) { |
| nUpdateRegionRects = 0; |
| |
| for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);){ |
| int x = rect.x1; |
| int y = rect.y1; |
| int w = rect.x2 - x; |
| int h = rect.y2 - y; |
| /* We need to count the number of rects in the scaled screen */ |
| if (cl->screen!=cl->scaledScreen) |
| rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h, "rfbSendFramebufferUpdate"); |
| nUpdateRegionRects += (((h-1) / (ZLIB_MAX_SIZE( w ) / w)) + 1); |
| } |
| sraRgnReleaseIterator(i); i=NULL; |
| #ifdef LIBVNCSERVER_HAVE_LIBJPEG |
| } else if (cl->preferredEncoding == rfbEncodingTight) { |
| nUpdateRegionRects = 0; |
| |
| for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);){ |
| int x = rect.x1; |
| int y = rect.y1; |
| int w = rect.x2 - x; |
| int h = rect.y2 - y; |
| int n; |
| /* We need to count the number of rects in the scaled screen */ |
| if (cl->screen!=cl->scaledScreen) |
| rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h, "rfbSendFramebufferUpdate"); |
| n = rfbNumCodedRectsTight(cl, x, y, w, h); |
| if (n == 0) { |
| nUpdateRegionRects = 0xFFFF; |
| break; |
| } |
| nUpdateRegionRects += n; |
| } |
| sraRgnReleaseIterator(i); i=NULL; |
| #endif |
| #endif |
| #if defined(LIBVNCSERVER_HAVE_LIBJPEG) && defined(LIBVNCSERVER_HAVE_LIBPNG) |
| } else if (cl->preferredEncoding == rfbEncodingTightPng) { |
| nUpdateRegionRects = 0; |
| |
| for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);){ |
| int x = rect.x1; |
| int y = rect.y1; |
| int w = rect.x2 - x; |
| int h = rect.y2 - y; |
| int n; |
| /* We need to count the number of rects in the scaled screen */ |
| if (cl->screen!=cl->scaledScreen) |
| rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h, "rfbSendFramebufferUpdate"); |
| n = rfbNumCodedRectsTight(cl, x, y, w, h); |
| if (n == 0) { |
| nUpdateRegionRects = 0xFFFF; |
| break; |
| } |
| nUpdateRegionRects += n; |
| } |
| sraRgnReleaseIterator(i); i=NULL; |
| #endif |
| } else { |
| nUpdateRegionRects = sraRgnCountRects(updateRegion); |
| } |
| |
| fu->type = rfbFramebufferUpdate; |
| if (nUpdateRegionRects != 0xFFFF) { |
| if(cl->screen->maxRectsPerUpdate>0 |
| /* CoRRE splits the screen into smaller squares */ |
| && cl->preferredEncoding != rfbEncodingCoRRE |
| /* Ultra encoding splits rectangles up into smaller chunks */ |
| && cl->preferredEncoding != rfbEncodingUltra |
| #ifdef LIBVNCSERVER_HAVE_LIBZ |
| /* Zlib encoding splits rectangles up into smaller chunks */ |
| && cl->preferredEncoding != rfbEncodingZlib |
| #ifdef LIBVNCSERVER_HAVE_LIBJPEG |
| /* Tight encoding counts the rectangles differently */ |
| && cl->preferredEncoding != rfbEncodingTight |
| #endif |
| #endif |
| #ifdef LIBVNCSERVER_HAVE_LIBPNG |
| /* Tight encoding counts the rectangles differently */ |
| && cl->preferredEncoding != rfbEncodingTightPng |
| #endif |
| && nUpdateRegionRects>cl->screen->maxRectsPerUpdate) { |
| sraRegion* newUpdateRegion = sraRgnBBox(updateRegion); |
| sraRgnDestroy(updateRegion); |
| updateRegion = newUpdateRegion; |
| nUpdateRegionRects = sraRgnCountRects(updateRegion); |
| } |
| fu->nRects = Swap16IfLE((uint16_t)(sraRgnCountRects(updateCopyRegion) + |
| nUpdateRegionRects + |
| !!sendCursorShape + !!sendCursorPos + !!sendKeyboardLedState + |
| !!sendSupportedMessages + !!sendSupportedEncodings + !!sendServerIdentity)); |
| } else { |
| fu->nRects = 0xFFFF; |
| } |
| cl->ublen = sz_rfbFramebufferUpdateMsg; |
| |
| if (sendCursorShape) { |
| cl->cursorWasChanged = FALSE; |
| if (!rfbSendCursorShape(cl)) |
| goto updateFailed; |
| } |
| |
| if (sendCursorPos) { |
| cl->cursorWasMoved = FALSE; |
| if (!rfbSendCursorPos(cl)) |
| goto updateFailed; |
| } |
| |
| if (sendKeyboardLedState) { |
| if (!rfbSendKeyboardLedState(cl)) |
| goto updateFailed; |
| } |
| |
| if (sendSupportedMessages) { |
| if (!rfbSendSupportedMessages(cl)) |
| goto updateFailed; |
| } |
| if (sendSupportedEncodings) { |
| if (!rfbSendSupportedEncodings(cl)) |
| goto updateFailed; |
| } |
| if (sendServerIdentity) { |
| if (!rfbSendServerIdentity(cl)) |
| goto updateFailed; |
| } |
| |
| if (!sraRgnEmpty(updateCopyRegion)) { |
| if (!rfbSendCopyRegion(cl,updateCopyRegion,dx,dy)) |
| goto updateFailed; |
| } |
| |
| for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);){ |
| int x = rect.x1; |
| int y = rect.y1; |
| int w = rect.x2 - x; |
| int h = rect.y2 - y; |
| |
| /* We need to count the number of rects in the scaled screen */ |
| if (cl->screen!=cl->scaledScreen) |
| rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h, "rfbSendFramebufferUpdate"); |
| |
| switch (cl->preferredEncoding) { |
| case -1: |
| case rfbEncodingRaw: |
| if (!rfbSendRectEncodingRaw(cl, x, y, w, h)) |
| goto updateFailed; |
| break; |
| case rfbEncodingRRE: |
| if (!rfbSendRectEncodingRRE(cl, x, y, w, h)) |
| goto updateFailed; |
| break; |
| case rfbEncodingCoRRE: |
| if (!rfbSendRectEncodingCoRRE(cl, x, y, w, h)) |
| goto updateFailed; |
| break; |
| case rfbEncodingHextile: |
| if (!rfbSendRectEncodingHextile(cl, x, y, w, h)) |
| goto updateFailed; |
| break; |
| case rfbEncodingUltra: |
| if (!rfbSendRectEncodingUltra(cl, x, y, w, h)) |
| goto updateFailed; |
| break; |
| #ifdef LIBVNCSERVER_HAVE_LIBZ |
| case rfbEncodingZlib: |
| if (!rfbSendRectEncodingZlib(cl, x, y, w, h)) |
| goto updateFailed; |
| break; |
| case rfbEncodingZRLE: |
| case rfbEncodingZYWRLE: |
| if (!rfbSendRectEncodingZRLE(cl, x, y, w, h)) |
| goto updateFailed; |
| break; |
| #endif |
| #if defined(LIBVNCSERVER_HAVE_LIBJPEG) && (defined(LIBVNCSERVER_HAVE_LIBZ) || defined(LIBVNCSERVER_HAVE_LIBPNG)) |
| case rfbEncodingTight: |
| if (!rfbSendRectEncodingTight(cl, x, y, w, h)) |
| goto updateFailed; |
| break; |
| #ifdef LIBVNCSERVER_HAVE_LIBPNG |
| case rfbEncodingTightPng: |
| if (!rfbSendRectEncodingTightPng(cl, x, y, w, h)) |
| goto updateFailed; |
| break; |
| #endif |
| #endif |
| } |
| } |
| if (i) { |
| sraRgnReleaseIterator(i); |
| i = NULL; |
| } |
| |
| if ( nUpdateRegionRects == 0xFFFF && |
| !rfbSendLastRectMarker(cl) ) |
| goto updateFailed; |
| |
| if (!rfbSendUpdateBuf(cl)) { |
| updateFailed: |
| result = FALSE; |
| } |
| |
| if (!cl->enableCursorShapeUpdates) { |
| rfbHideCursor(cl); |
| } |
| |
| if(i) |
| sraRgnReleaseIterator(i); |
| sraRgnDestroy(updateRegion); |
| sraRgnDestroy(updateCopyRegion); |
| |
| if(cl->screen->displayFinishedHook) |
| cl->screen->displayFinishedHook(cl, result); |
| return result; |
| } |
| |
| |
| /* |
| * Send the copy region as a string of CopyRect encoded rectangles. |
| * The only slightly tricky thing is that we should send the messages in |
| * the correct order so that an earlier CopyRect will not corrupt the source |
| * of a later one. |
| */ |
| |
| rfbBool |
| rfbSendCopyRegion(rfbClientPtr cl, |
| sraRegionPtr reg, |
| int dx, |
| int dy) |
| { |
| int x, y, w, h; |
| rfbFramebufferUpdateRectHeader rect; |
| rfbCopyRect cr; |
| sraRectangleIterator* i; |
| sraRect rect1; |
| |
| /* printf("copyrect: "); sraRgnPrint(reg); putchar('\n');fflush(stdout); */ |
| i = sraRgnGetReverseIterator(reg,dx>0,dy>0); |
| |
| /* correct for the scale of the screen */ |
| dx = ScaleX(cl->screen, cl->scaledScreen, dx); |
| dy = ScaleX(cl->screen, cl->scaledScreen, dy); |
| |
| while(sraRgnIteratorNext(i,&rect1)) { |
| x = rect1.x1; |
| y = rect1.y1; |
| w = rect1.x2 - x; |
| h = rect1.y2 - y; |
| |
| /* correct for scaling (if necessary) */ |
| rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h, "copyrect"); |
| |
| rect.r.x = Swap16IfLE(x); |
| rect.r.y = Swap16IfLE(y); |
| rect.r.w = Swap16IfLE(w); |
| rect.r.h = Swap16IfLE(h); |
| rect.encoding = Swap32IfLE(rfbEncodingCopyRect); |
| |
| memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, |
| sz_rfbFramebufferUpdateRectHeader); |
| cl->ublen += sz_rfbFramebufferUpdateRectHeader; |
| |
| cr.srcX = Swap16IfLE(x - dx); |
| cr.srcY = Swap16IfLE(y - dy); |
| |
| memcpy(&cl->updateBuf[cl->ublen], (char *)&cr, sz_rfbCopyRect); |
| cl->ublen += sz_rfbCopyRect; |
| |
| rfbStatRecordEncodingSent(cl, rfbEncodingCopyRect, sz_rfbFramebufferUpdateRectHeader + sz_rfbCopyRect, |
| w * h * (cl->scaledScreen->bitsPerPixel / 8)); |
| } |
| sraRgnReleaseIterator(i); |
| |
| return TRUE; |
| } |
| |
| /* |
| * Send a given rectangle in raw encoding (rfbEncodingRaw). |
| */ |
| |
| rfbBool |
| rfbSendRectEncodingRaw(rfbClientPtr cl, |
| int x, |
| int y, |
| int w, |
| int h) |
| { |
| rfbFramebufferUpdateRectHeader rect; |
| int nlines; |
| int bytesPerLine = w * (cl->format.bitsPerPixel / 8); |
| char *fbptr = (cl->scaledScreen->frameBuffer + (cl->scaledScreen->paddedWidthInBytes * y) |
| + (x * (cl->scaledScreen->bitsPerPixel / 8))); |
| |
| /* Flush the buffer to guarantee correct alignment for translateFn(). */ |
| if (cl->ublen > 0) { |
| if (!rfbSendUpdateBuf(cl)) |
| return FALSE; |
| } |
| |
| rect.r.x = Swap16IfLE(x); |
| rect.r.y = Swap16IfLE(y); |
| rect.r.w = Swap16IfLE(w); |
| rect.r.h = Swap16IfLE(h); |
| rect.encoding = Swap32IfLE(rfbEncodingRaw); |
| |
| memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,sz_rfbFramebufferUpdateRectHeader); |
| cl->ublen += sz_rfbFramebufferUpdateRectHeader; |
| |
| |
| rfbStatRecordEncodingSent(cl, rfbEncodingRaw, sz_rfbFramebufferUpdateRectHeader + bytesPerLine * h, |
| sz_rfbFramebufferUpdateRectHeader + bytesPerLine * h); |
| |
| nlines = (UPDATE_BUF_SIZE - cl->ublen) / bytesPerLine; |
| |
| while (TRUE) { |
| if (nlines > h) |
| nlines = h; |
| |
| (*cl->translateFn)(cl->translateLookupTable, |
| &(cl->screen->serverFormat), |
| &cl->format, fbptr, &cl->updateBuf[cl->ublen], |
| cl->scaledScreen->paddedWidthInBytes, w, nlines); |
| |
| cl->ublen += nlines * bytesPerLine; |
| h -= nlines; |
| |
| if (h == 0) /* rect fitted in buffer, do next one */ |
| return TRUE; |
| |
| /* buffer full - flush partial rect and do another nlines */ |
| |
| if (!rfbSendUpdateBuf(cl)) |
| return FALSE; |
| |
| fbptr += (cl->scaledScreen->paddedWidthInBytes * nlines); |
| |
| nlines = (UPDATE_BUF_SIZE - cl->ublen) / bytesPerLine; |
| if (nlines == 0) { |
| rfbErr("rfbSendRectEncodingRaw: send buffer too small for %d " |
| "bytes per line\n", bytesPerLine); |
| rfbCloseClient(cl); |
| return FALSE; |
| } |
| } |
| } |
| |
| |
| |
| /* |
| * Send an empty rectangle with encoding field set to value of |
| * rfbEncodingLastRect to notify client that this is the last |
| * rectangle in framebuffer update ("LastRect" extension of RFB |
| * protocol). |
| */ |
| |
| rfbBool |
| rfbSendLastRectMarker(rfbClientPtr cl) |
| { |
| rfbFramebufferUpdateRectHeader rect; |
| |
| if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) { |
| if (!rfbSendUpdateBuf(cl)) |
| return FALSE; |
| } |
| |
| rect.encoding = Swap32IfLE(rfbEncodingLastRect); |
| rect.r.x = 0; |
| rect.r.y = 0; |
| rect.r.w = 0; |
| rect.r.h = 0; |
| |
| memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,sz_rfbFramebufferUpdateRectHeader); |
| cl->ublen += sz_rfbFramebufferUpdateRectHeader; |
| |
| |
| rfbStatRecordEncodingSent(cl, rfbEncodingLastRect, sz_rfbFramebufferUpdateRectHeader, sz_rfbFramebufferUpdateRectHeader); |
| |
| return TRUE; |
| } |
| |
| |
| /* |
| * Send NewFBSize pseudo-rectangle. This tells the client to change |
| * its framebuffer size. |
| */ |
| |
| rfbBool |
| rfbSendNewFBSize(rfbClientPtr cl, |
| int w, |
| int h) |
| { |
| rfbFramebufferUpdateRectHeader rect; |
| |
| if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) { |
| if (!rfbSendUpdateBuf(cl)) |
| return FALSE; |
| } |
| |
| if (cl->PalmVNC==TRUE) |
| rfbLog("Sending rfbEncodingNewFBSize in response to a PalmVNC style framebuffer resize (%dx%d)\n", w, h); |
| else |
| rfbLog("Sending rfbEncodingNewFBSize for resize to (%dx%d)\n", w, h); |
| |
| rect.encoding = Swap32IfLE(rfbEncodingNewFBSize); |
| rect.r.x = 0; |
| rect.r.y = 0; |
| rect.r.w = Swap16IfLE(w); |
| rect.r.h = Swap16IfLE(h); |
| |
| memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, |
| sz_rfbFramebufferUpdateRectHeader); |
| cl->ublen += sz_rfbFramebufferUpdateRectHeader; |
| |
| rfbStatRecordEncodingSent(cl, rfbEncodingNewFBSize, sz_rfbFramebufferUpdateRectHeader, sz_rfbFramebufferUpdateRectHeader); |
| |
| return TRUE; |
| } |
| |
| |
| /* |
| * Send the contents of cl->updateBuf. Returns 1 if successful, -1 if |
| * not (errno should be set). |
| */ |
| |
| rfbBool |
| rfbSendUpdateBuf(rfbClientPtr cl) |
| { |
| if(cl->sock<0) |
| return FALSE; |
| |
| if (rfbWriteExact(cl, cl->updateBuf, cl->ublen) < 0) { |
| rfbLogPerror("rfbSendUpdateBuf: write"); |
| rfbCloseClient(cl); |
| return FALSE; |
| } |
| |
| cl->ublen = 0; |
| return TRUE; |
| } |
| |
| /* |
| * rfbSendSetColourMapEntries sends a SetColourMapEntries message to the |
| * client, using values from the currently installed colormap. |
| */ |
| |
| rfbBool |
| rfbSendSetColourMapEntries(rfbClientPtr cl, |
| int firstColour, |
| int nColours) |
| { |
| char buf[sz_rfbSetColourMapEntriesMsg + 256 * 3 * 2]; |
| char *wbuf = buf; |
| rfbSetColourMapEntriesMsg *scme; |
| uint16_t *rgb; |
| rfbColourMap* cm = &cl->screen->colourMap; |
| int i, len; |
| |
| if (nColours > 256) { |
| /* some rare hardware has, e.g., 4096 colors cells: PseudoColor:12 */ |
| wbuf = (char *) malloc(sz_rfbSetColourMapEntriesMsg + nColours * 3 * 2); |
| } |
| |
| scme = (rfbSetColourMapEntriesMsg *)wbuf; |
| rgb = (uint16_t *)(&wbuf[sz_rfbSetColourMapEntriesMsg]); |
| |
| scme->type = rfbSetColourMapEntries; |
| |
| scme->firstColour = Swap16IfLE(firstColour); |
| scme->nColours = Swap16IfLE(nColours); |
| |
| len = sz_rfbSetColourMapEntriesMsg; |
| |
| for (i = 0; i < nColours; i++) { |
| if(i<(int)cm->count) { |
| if(cm->is16) { |
| rgb[i*3] = Swap16IfLE(cm->data.shorts[i*3]); |
| rgb[i*3+1] = Swap16IfLE(cm->data.shorts[i*3+1]); |
| rgb[i*3+2] = Swap16IfLE(cm->data.shorts[i*3+2]); |
| } else { |
| rgb[i*3] = Swap16IfLE((unsigned short)cm->data.bytes[i*3]); |
| rgb[i*3+1] = Swap16IfLE((unsigned short)cm->data.bytes[i*3+1]); |
| rgb[i*3+2] = Swap16IfLE((unsigned short)cm->data.bytes[i*3+2]); |
| } |
| } |
| } |
| |
| len += nColours * 3 * 2; |
| |
| LOCK(cl->sendMutex); |
| if (rfbWriteExact(cl, wbuf, len) < 0) { |
| rfbLogPerror("rfbSendSetColourMapEntries: write"); |
| rfbCloseClient(cl); |
| if (wbuf != buf) free(wbuf); |
| UNLOCK(cl->sendMutex); |
| return FALSE; |
| } |
| UNLOCK(cl->sendMutex); |
| |
| rfbStatRecordMessageSent(cl, rfbSetColourMapEntries, len, len); |
| if (wbuf != buf) free(wbuf); |
| return TRUE; |
| } |
| |
| /* |
| * rfbSendBell sends a Bell message to all the clients. |
| */ |
| |
| void |
| rfbSendBell(rfbScreenInfoPtr rfbScreen) |
| { |
| rfbClientIteratorPtr i; |
| rfbClientPtr cl; |
| rfbBellMsg b; |
| |
| i = rfbGetClientIterator(rfbScreen); |
| while((cl=rfbClientIteratorNext(i))) { |
| b.type = rfbBell; |
| LOCK(cl->sendMutex); |
| if (rfbWriteExact(cl, (char *)&b, sz_rfbBellMsg) < 0) { |
| rfbLogPerror("rfbSendBell: write"); |
| rfbCloseClient(cl); |
| } |
| UNLOCK(cl->sendMutex); |
| } |
| rfbStatRecordMessageSent(cl, rfbBell, sz_rfbBellMsg, sz_rfbBellMsg); |
| rfbReleaseClientIterator(i); |
| } |
| |
| |
| /* |
| * rfbSendServerCutText sends a ServerCutText message to all the clients. |
| */ |
| |
| void |
| rfbSendServerCutText(rfbScreenInfoPtr rfbScreen,char *str, int len) |
| { |
| rfbClientPtr cl; |
| rfbServerCutTextMsg sct; |
| rfbClientIteratorPtr iterator; |
| |
| iterator = rfbGetClientIterator(rfbScreen); |
| while ((cl = rfbClientIteratorNext(iterator)) != NULL) { |
| sct.type = rfbServerCutText; |
| sct.length = Swap32IfLE(len); |
| LOCK(cl->sendMutex); |
| if (rfbWriteExact(cl, (char *)&sct, |
| sz_rfbServerCutTextMsg) < 0) { |
| rfbLogPerror("rfbSendServerCutText: write"); |
| rfbCloseClient(cl); |
| UNLOCK(cl->sendMutex); |
| continue; |
| } |
| if (rfbWriteExact(cl, str, len) < 0) { |
| rfbLogPerror("rfbSendServerCutText: write"); |
| rfbCloseClient(cl); |
| } |
| UNLOCK(cl->sendMutex); |
| rfbStatRecordMessageSent(cl, rfbServerCutText, sz_rfbServerCutTextMsg+len, sz_rfbServerCutTextMsg+len); |
| } |
| rfbReleaseClientIterator(iterator); |
| } |
| |
| /***************************************************************************** |
| * |
| * UDP can be used for keyboard and pointer events when the underlying |
| * network is highly reliable. This is really here to support ORL's |
| * videotile, whose TCP implementation doesn't like sending lots of small |
| * packets (such as 100s of pen readings per second!). |
| */ |
| |
| static unsigned char ptrAcceleration = 50; |
| |
| void |
| rfbNewUDPConnection(rfbScreenInfoPtr rfbScreen, |
| int sock) |
| { |
| if (write(sock, (char*) &ptrAcceleration, 1) < 0) { |
| rfbLogPerror("rfbNewUDPConnection: write"); |
| } |
| } |
| |
| /* |
| * Because UDP is a message based service, we can't read the first byte and |
| * then the rest of the packet separately like we do with TCP. We will always |
| * get a whole packet delivered in one go, so we ask read() for the maximum |
| * number of bytes we can possibly get. |
| */ |
| |
| void |
| rfbProcessUDPInput(rfbScreenInfoPtr rfbScreen) |
| { |
| int n; |
| rfbClientPtr cl=rfbScreen->udpClient; |
| rfbClientToServerMsg msg; |
| |
| if((!cl) || cl->onHold) |
| return; |
| |
| if ((n = read(rfbScreen->udpSock, (char *)&msg, sizeof(msg))) <= 0) { |
| if (n < 0) { |
| rfbLogPerror("rfbProcessUDPInput: read"); |
| } |
| rfbDisconnectUDPSock(rfbScreen); |
| return; |
| } |
| |
| switch (msg.type) { |
| |
| case rfbKeyEvent: |
| if (n != sz_rfbKeyEventMsg) { |
| rfbErr("rfbProcessUDPInput: key event incorrect length\n"); |
| rfbDisconnectUDPSock(rfbScreen); |
| return; |
| } |
| cl->screen->kbdAddEvent(msg.ke.down, (rfbKeySym)Swap32IfLE(msg.ke.key), cl); |
| break; |
| |
| case rfbPointerEvent: |
| if (n != sz_rfbPointerEventMsg) { |
| rfbErr("rfbProcessUDPInput: ptr event incorrect length\n"); |
| rfbDisconnectUDPSock(rfbScreen); |
| return; |
| } |
| cl->screen->ptrAddEvent(msg.pe.buttonMask, |
| Swap16IfLE(msg.pe.x), Swap16IfLE(msg.pe.y), cl); |
| break; |
| |
| default: |
| rfbErr("rfbProcessUDPInput: unknown message type %d\n", |
| msg.type); |
| rfbDisconnectUDPSock(rfbScreen); |
| } |
| } |
| |
| |