diff options
-rw-r--r-- | ChangeLog | 20 | ||||
-rw-r--r-- | client_examples/SDLvncviewer.c | 22 | ||||
-rw-r--r-- | libvncclient/rfbproto.c | 366 | ||||
-rw-r--r-- | libvncclient/ultra.c | 4 | ||||
-rw-r--r-- | libvncclient/zrle.c | 2 | ||||
-rwxr-xr-x | libvncserver/auth.c | 7 | ||||
-rwxr-xr-x | libvncserver/corre.c | 8 | ||||
-rw-r--r-- | libvncserver/cursor.c | 11 | ||||
-rwxr-xr-x | libvncserver/hextile.c | 17 | ||||
-rw-r--r-- | libvncserver/main.c | 32 | ||||
-rw-r--r-- | libvncserver/rfbserver.c | 1013 | ||||
-rwxr-xr-x | libvncserver/rre.c | 8 | ||||
-rw-r--r-- | libvncserver/scale.c | 8 | ||||
-rwxr-xr-x | libvncserver/sockets.c | 22 | ||||
-rwxr-xr-x | libvncserver/stats.c | 476 | ||||
-rw-r--r-- | libvncserver/tight.c | 32 | ||||
-rw-r--r-- | libvncserver/tightvnc-filetransfer/rfbtightproto.h | 6 | ||||
-rw-r--r-- | libvncserver/ultra.c | 4 | ||||
-rw-r--r-- | libvncserver/zlib.c | 6 | ||||
-rw-r--r-- | libvncserver/zrle.c | 5 | ||||
-rw-r--r-- | rfb/rfb.h | 111 | ||||
-rw-r--r-- | rfb/rfbclient.h | 21 | ||||
-rw-r--r-- | rfb/rfbproto.h | 22 | ||||
-rw-r--r-- | x11vnc/rates.c | 11 | ||||
-rw-r--r-- | x11vnc/userinput.c | 2 |
25 files changed, 1935 insertions, 301 deletions
@@ -1,3 +1,23 @@ +2006-05-15 Steven Carr <scarr@jsa-usa.com> + * The great UltraVNC Compatibility Commit! + libvncserver now supports the following messages: + SetSingleWindow - Select a single window to be the source of the + framebuffer. + ServerInput - Disable and blank the servers display + TextChat - TextChat between the remote/local user + (Bandwidth friendly VS the Notepad approach) + FileTransfer - Emulates a Windows Filesystem to the viewer + (Currently does not support Delta Transfers) + (Currently does not support Sending Directories) + UltraZip - Improved UltraZip support + * Improved Statistics SubSystem, now supports all encodings + * RFB 3.8 support! Error Messages are a 'Good Thing' (tm) + * Default to identify as RFB 3.6 to emulate UltraVNC server + (Server now has the ability to set the RFB version reported) + (permits the viewer to identify the server has FileTransfer ability) + * Client Encoding AutoSelection Supported (UltraViewer is speed aware) + * libvncclient has improved server detection/capabilities logic! + 2006-05-13 Karl Runge <runge@karlrunge.com> * minilzo.c,minilzo.h,lzoconf.h: switch to non-CRLF versions. * libvncclient/Makefile.am: add minilzo.c, minilzo.h, lzoconf.h diff --git a/client_examples/SDLvncviewer.c b/client_examples/SDLvncviewer.c index 8bb6efa..99c8e85 100644 --- a/client_examples/SDLvncviewer.c +++ b/client_examples/SDLvncviewer.c @@ -150,6 +150,26 @@ static void kbd_leds(rfbClient* cl, int value, int pad) { fflush(stderr); } +/* trivial support for textchat */ +static void text_chat(rfbClient* cl, int value, char *text) { + switch(value) { + case rfbTextChatOpen: + fprintf(stderr,"TextChat: We should open a textchat window!\n"); + TextChatOpen(cl); + break; + case rfbTextChatClose: + fprintf(stderr,"TextChat: We should close our window!\n"); + break; + case rfbTextChatFinished: + fprintf(stderr,"TextChat: We should close our window!\n"); + break; + default: + fprintf(stderr,"TextChat: Received \"%s\"\n", text); + break; + } + fflush(stderr); +} + #ifdef __MINGW32__ #define LOG_TO_FILE #endif @@ -212,7 +232,7 @@ int main(int argc,char** argv) { cl->canHandleNewFBSize = TRUE; cl->GotFrameBufferUpdate=update; cl->HandleKeyboardLedState=kbd_leds; - + cl->HandleTextChat=text_chat; if(!rfbInitClient(cl,&argc,argv)) return 1; diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c index a7ad216..a7aab60 100644 --- a/libvncclient/rfbproto.c +++ b/libvncclient/rfbproto.c @@ -224,6 +224,99 @@ static rfbBool HandleZRLE32(rfbClient* client, int rx, int ry, int rw, int rh); #endif /* + * Server Capability Functions + */ +rfbBool +SupportsClient2Server(rfbClient* client, int messageType) +{ + return (client->supportedMessages.client2server[((messageType & 0xFF)/8)] & (1<<(messageType % 8)) ? TRUE : FALSE); +} + +rfbBool +SupportsServer2Client(rfbClient* client, int messageType) +{ + return (client->supportedMessages.server2client[((messageType & 0xFF)/8)] & (1<<(messageType % 8)) ? TRUE : FALSE); +} + +void +SetClient2Server(rfbClient* client, int messageType) +{ + client->supportedMessages.client2server[((messageType & 0xFF)/8)] |= (1<<(messageType % 8)); +} + +void +SetServer2Client(rfbClient* client, int messageType) +{ + client->supportedMessages.server2client[((messageType & 0xFF)/8)] |= (1<<(messageType % 8)); +} + +void +ClearClient2Server(rfbClient* client, int messageType) +{ + client->supportedMessages.client2server[((messageType & 0xFF)/8)] &= (!(1<<(messageType % 8))); +} + +void +ClearServer2Client(rfbClient* client, int messageType) +{ + client->supportedMessages.server2client[((messageType & 0xFF)/8)] &= (!(1<<(messageType % 8))); +} + + +void +DefaultSupportedMessages(rfbClient* client) +{ + memset((char *)&client->supportedMessages,0,sizeof(client->supportedMessages)); + + /* Default client supported messages (universal RFB 3.3 protocol) */ + SetClient2Server(client, rfbSetPixelFormat); + /* SetClient2Server(client, rfbFixColourMapEntries); Not currently supported */ + SetClient2Server(client, rfbSetEncodings); + SetClient2Server(client, rfbFramebufferUpdateRequest); + SetClient2Server(client, rfbKeyEvent); + SetClient2Server(client, rfbPointerEvent); + SetClient2Server(client, rfbClientCutText); + /* technically, we only care what we can *send* to the server + * but, we set Server2Client Just in case it ever becomes useful + */ + SetServer2Client(client, rfbFramebufferUpdate); + SetServer2Client(client, rfbSetColourMapEntries); + SetServer2Client(client, rfbBell); + SetServer2Client(client, rfbServerCutText); +} + +void +DefaultSupportedMessagesUltraVNC(rfbClient* client) +{ + DefaultSupportedMessages(client); + SetClient2Server(client, rfbFileTransfer); + SetClient2Server(client, rfbSetScale); + SetClient2Server(client, rfbSetServerInput); + SetClient2Server(client, rfbSetSW); + SetClient2Server(client, rfbTextChat); + SetClient2Server(client, rfbPalmVNCSetScaleFactor); + /* technically, we only care what we can *send* to the server */ + SetServer2Client(client, rfbResizeFrameBuffer); + SetServer2Client(client, rfbPalmVNCReSizeFrameBuffer); + SetServer2Client(client, rfbFileTransfer); + SetServer2Client(client, rfbTextChat); +} + + +void +DefaultSupportedMessagesTightVNC(rfbClient* client) +{ + DefaultSupportedMessages(client); + SetClient2Server(client, rfbFileTransfer); + SetClient2Server(client, rfbSetServerInput); + SetClient2Server(client, rfbSetSW); + /* SetClient2Server(client, rfbTextChat); */ + /* technically, we only care what we can *send* to the server */ + SetServer2Client(client, rfbFileTransfer); + SetServer2Client(client, rfbTextChat); +} + +/* * ConnectToRFBServer. */ @@ -310,26 +403,87 @@ InitialiseRFBConnection(rfbClient* client) return FALSE; } -#if rfbProtocolMinorVersion == 7 - /* work around LibVNCClient not yet speaking RFB 3.7 */ -#undef rfbProtocolMinorVersion -#define rfbProtocolMinorVersion 3 -#endif + + DefaultSupportedMessages(client); + client->major = major; + client->minor = minor; + + /* fall back to viewer supported version */ + if ((major==rfbProtocolMajorVersion) && (minor>rfbProtocolMinorVersion)) + client->minor = rfbProtocolMinorVersion; + + /* UltraVNC uses minor codes 4 and 6 for the server */ + if (major==3 && (minor==4 || minor==6)) { + rfbClientLog("UltraVNC server detected, enabling UltraVNC specific messages\n",pv); + DefaultSupportedMessagesUltraVNC(client); + } + + /* TightVNC uses minor codes 5 for the server */ + if (major==3 && minor==5) { + rfbClientLog("TightVNC server detected, enabling TightVNC specific messages\n",pv); + DefaultSupportedMessagesTightVNC(client); + } + + /* we do not support > RFB3.8 */ + if (major==3 && minor>8) + client->minor=8; rfbClientLog("VNC server supports protocol version %d.%d (viewer %d.%d)\n", major, minor, rfbProtocolMajorVersion, rfbProtocolMinorVersion); - major = rfbProtocolMajorVersion; - minor = rfbProtocolMinorVersion; - - sprintf(pv,rfbProtocolVersionFormat,major,minor); + sprintf(pv,rfbProtocolVersionFormat,client->major,client->minor); if (!WriteToRFBServer(client, pv, sz_rfbProtocolVersionMsg)) return FALSE; - if (!ReadFromRFBServer(client, (char *)&authScheme, 4)) return FALSE; - authScheme = rfbClientSwap32IfLE(authScheme); + /* 3.7 and onwards sends a # of security types first */ + if (client->major==3 && client->minor > 6) + { + uint8_t count=0; + uint8_t loop=0; + uint8_t flag=0; + uint8_t tAuth=0; + + if (!ReadFromRFBServer(client, (char *)&count, 1)) return FALSE; + + if (count==0) + { + rfbClientLog("List of security types is ZERO, expecting an error to follow\n"); + + /* we have an error following */ + if (!ReadFromRFBServer(client, (char *)&reasonLen, 4)) return FALSE; + reasonLen = rfbClientSwap32IfLE(reasonLen); + reason = malloc(reasonLen); + if (!ReadFromRFBServer(client, reason, reasonLen)) { free(reason); return FALSE; } + rfbClientLog("VNC connection failed: %.*s\n",(int)reasonLen, reason); + free(reason); + return FALSE; + } + rfbClientLog("We have %d security types to read\n", count); + /* now, we have a list of available security types to read ( uint8_t[] ) */ + for (loop=0;loop<count;loop++) + { + if (!ReadFromRFBServer(client, (char *)&tAuth, 1)) return FALSE; + rfbClientLog("%d) Received security type %d\n", loop, tAuth); + if ((flag==0) && ((tAuth==rfbVncAuth) || (tAuth==rfbNoAuth))) + { + flag++; + authScheme=tAuth; + rfbClientLog("Selecting security type %d (%d/%d in the list)\n", authScheme, loop, count); + /* send back a single byte indicating which security type to use */ + if (!WriteToRFBServer(client, (char *)&tAuth, 1)) return FALSE; + } + } + } + else + { + if (!ReadFromRFBServer(client, (char *)&authScheme, 4)) return FALSE; + authScheme = rfbClientSwap32IfLE(authScheme); + } + + rfbClientLog("Selected Security Scheme %d\n", authScheme); + switch (authScheme) { case rfbConnFailed: @@ -338,9 +492,10 @@ InitialiseRFBConnection(rfbClient* client) reason = malloc(reasonLen); - if (!ReadFromRFBServer(client, reason, reasonLen)) return FALSE; + if (!ReadFromRFBServer(client, reason, reasonLen)) { free(reason); return FALSE; } rfbClientLog("VNC connection failed: %.*s\n",(int)reasonLen, reason); + free(reason); return FALSE; case rfbNoAuth: @@ -382,6 +537,17 @@ InitialiseRFBConnection(rfbClient* client) rfbClientLog("VNC authentication succeeded\n"); break; case rfbVncAuthFailed: + if (client->major==3 && client->minor>7) + { + /* we have an error following */ + if (!ReadFromRFBServer(client, (char *)&reasonLen, 4)) return FALSE; + reasonLen = rfbClientSwap32IfLE(reasonLen); + reason = malloc(reasonLen); + if (!ReadFromRFBServer(client, reason, reasonLen)) { free(reason); return FALSE; } + rfbClientLog("VNC connection failed: %.*s\n",(int)reasonLen, reason); + free(reason); + return FALSE; + } rfbClientLog("VNC authentication failed\n"); return FALSE; case rfbVncAuthTooMany: @@ -427,7 +593,7 @@ InitialiseRFBConnection(rfbClient* client) rfbClientLog("Desktop name \"%s\"\n",client->desktopName); rfbClientLog("Connected to VNC server, using protocol version %d.%d\n", - rfbProtocolMajorVersion, rfbProtocolMinorVersion); + client->major, client->minor); rfbClientLog("VNC server default format:\n"); PrintPixelFormat(&client->si.format); @@ -445,6 +611,7 @@ SetFormatAndEncodings(rfbClient* client) { rfbSetPixelFormatMsg spf; char buf[sz_rfbSetEncodingsMsg + MAX_ENCODINGS * 4]; + rfbSetEncodingsMsg *se = (rfbSetEncodingsMsg *)buf; uint32_t *encs = (uint32_t *)(&buf[sz_rfbSetEncodingsMsg]); int len = 0; @@ -453,6 +620,8 @@ SetFormatAndEncodings(rfbClient* client) rfbBool requestLastRectEncoding = FALSE; rfbClientProtocolExtension* e; + if (!SupportsClient2Server(client, rfbSetPixelFormat)) return TRUE; + spf.type = rfbSetPixelFormat; spf.format = client->format; spf.format.redMax = rfbClientSwap16IfLE(spf.format.redMax); @@ -462,6 +631,9 @@ SetFormatAndEncodings(rfbClient* client) if (!WriteToRFBServer(client, (char *)&spf, sz_rfbSetPixelFormatMsg)) return FALSE; + + if (!SupportsClient2Server(client, rfbSetEncodings)) return TRUE; + se->type = rfbSetEncodings; se->nEncodings = 0; @@ -641,7 +813,7 @@ SetFormatAndEncodings(rfbClient* client) rfbBool SendIncrementalFramebufferUpdateRequest(rfbClient* client) { - return SendFramebufferUpdateRequest(client, 0, 0, client->width, + return SendFramebufferUpdateRequest(client, 0, 0, client->width, client->height, TRUE); } @@ -655,6 +827,8 @@ SendFramebufferUpdateRequest(rfbClient* client, int x, int y, int w, int h, rfbB { rfbFramebufferUpdateRequestMsg fur; + if (!SupportsClient2Server(client, rfbFramebufferUpdateRequest)) return TRUE; + fur.type = rfbFramebufferUpdateRequest; fur.incremental = incremental ? 1 : 0; fur.x = rfbClientSwap16IfLE(x); @@ -677,20 +851,106 @@ SendScaleSetting(rfbClient* client,int scaleSetting) { rfbSetScaleMsg ssm; - if (client->appData.palmVNC) - ssm.type = rfbPalmVNCSetScaleFactor; - else - ssm.type = rfbSetScale; ssm.scale = scaleSetting; ssm.pad = 0; - if (!WriteToRFBServer(client, (char *)&ssm, sz_rfbSetScaleMsg)) - return FALSE; + /* favor UltraVNC SetScale if both are supported */ + if (SupportsClient2Server(client, rfbSetScale)) { + ssm.type = rfbSetScale; + if (!WriteToRFBServer(client, (char *)&ssm, sz_rfbSetScaleMsg)) + return FALSE; + } + + if (SupportsClient2Server(client, rfbPalmVNCSetScaleFactor)) { + ssm.type = rfbPalmVNCSetScaleFactor; + if (!WriteToRFBServer(client, (char *)&ssm, sz_rfbSetScaleMsg)) + return FALSE; + } return TRUE; } /* + * TextChatFunctions (UltraVNC) + * Extremely bandwidth friendly method of communicating with a user + * (Think HelpDesk type applications) + */ + +rfbBool TextChatSend(rfbClient* client, char *text) +{ + rfbTextChatMsg chat; + int count = strlen(text); + + if (!SupportsClient2Server(client, rfbTextChat)) return TRUE; + chat.type = rfbTextChat; + chat.pad1 = 0; + chat.pad2 = 0; + chat.length = (uint32_t)count; + chat.length = rfbClientSwap32IfLE(chat.length); + + if (!WriteToRFBServer(client, (char *)&chat, sz_rfbTextChatMsg)) + return FALSE; + + if (count>0) { + if (!WriteToRFBServer(client, text, count)) + return FALSE; + } + return TRUE; +} + +rfbBool TextChatOpen(rfbClient* client) +{ + rfbTextChatMsg chat; + + if (!SupportsClient2Server(client, rfbTextChat)) return TRUE; + chat.type = rfbTextChat; + chat.pad1 = 0; + chat.pad2 = 0; + chat.length = rfbClientSwap32IfLE(rfbTextChatOpen); + return (WriteToRFBServer(client, (char *)&chat, sz_rfbTextChatMsg) ? TRUE : FALSE); +} + +rfbBool TextChatClose(rfbClient* client) +{ + rfbTextChatMsg chat; + if (!SupportsClient2Server(client, rfbTextChat)) return TRUE; + chat.type = rfbTextChat; + chat.pad1 = 0; + chat.pad2 = 0; + chat.length = rfbClientSwap32IfLE(rfbTextChatClose); + return (WriteToRFBServer(client, (char *)&chat, sz_rfbTextChatMsg) ? TRUE : FALSE); +} + +rfbBool TextChatFinish(rfbClient* client) +{ + rfbTextChatMsg chat; + if (!SupportsClient2Server(client, rfbTextChat)) return TRUE; + chat.type = rfbTextChat; + chat.pad1 = 0; + chat.pad2 = 0; + chat.length = rfbClientSwap32IfLE(rfbTextChatFinished); + return (WriteToRFBServer(client, (char *)&chat, sz_rfbTextChatMsg) ? TRUE : FALSE); +} + +/* + * UltraVNC Server Input Disable + * Apparently, the remote client can *prevent* the local user from interacting with the display + * I would think this is extremely helpful when used in a HelpDesk situation + */ +rfbBool PermitServerInput(rfbClient* client, int enabled) +{ + rfbSetServerInputMsg msg; + + if (!SupportsClient2Server(client, rfbSetServerInput)) return TRUE; + /* enabled==1, then server input from local keyboard is disabled */ + msg.type = rfbSetServerInput; + msg.status = (enabled ? 1 : 0); + msg.pad = 0; + return (WriteToRFBServer(client, (char *)&msg, sz_rfbSetServerInputMsg) ? TRUE : FALSE); +} + + +/* * SendPointerEvent. */ @@ -699,6 +959,8 @@ SendPointerEvent(rfbClient* client,int x, int y, int buttonMask) { rfbPointerEventMsg pe; + if (!SupportsClient2Server(client, rfbPointerEvent)) return TRUE; + pe.type = rfbPointerEvent; pe.buttonMask = buttonMask; if (x < 0) x = 0; @@ -719,6 +981,8 @@ SendKeyEvent(rfbClient* client, uint32_t key, rfbBool down) { rfbKeyEventMsg ke; + if (!SupportsClient2Server(client, rfbKeyEvent)) return TRUE; + ke.type = rfbKeyEvent; ke.down = down ? 1 : 0; ke.key = rfbClientSwap32IfLE(key); @@ -739,6 +1003,8 @@ SendClientCutText(rfbClient* client, char *str, int len) free(client->serverCutText); client->serverCutText = NULL; + if (!SupportsClient2Server(client, rfbClientCutText)) return TRUE; + cct.type = rfbClientCutText; cct.length = rfbClientSwap32IfLE(len); return (WriteToRFBServer(client, (char *)&cct, sz_rfbClientCutTextMsg) && @@ -858,9 +1124,8 @@ HandleRFBServerMessage(rfbClient* client) /* rect.r.w=byte count */ if (rect.encoding == rfbEncodingSupportedMessages) { - rfbSupportedMessages msgs; int loop; - if (!ReadFromRFBServer(client, (char *)&msgs, sz_rfbSupportedMessages)) + if (!ReadFromRFBServer(client, (char *)&client->supportedMessages, sz_rfbSupportedMessages)) return FALSE; /* msgs is two sets of bit flags of supported messages client2server[] and server2client[] */ @@ -869,18 +1134,18 @@ HandleRFBServerMessage(rfbClient* client) rfbClientLog("client2server supported messages (bit flags)\n"); for (loop=0;loop<32;loop+=8) rfbClientLog("%02X: %04x %04x %04x %04x - %04x %04x %04x %04x\n", loop, - msgs.client2server[loop], msgs.client2server[loop+1], - msgs.client2server[loop+2], msgs.client2server[loop+3], - msgs.client2server[loop+4], msgs.client2server[loop+5], - msgs.client2server[loop+6], msgs.client2server[loop+7]); + client->supportedMessages.client2server[loop], client->supportedMessages.client2server[loop+1], + client->supportedMessages.client2server[loop+2], client->supportedMessages.client2server[loop+3], + client->supportedMessages.client2server[loop+4], client->supportedMessages.client2server[loop+5], + client->supportedMessages.client2server[loop+6], client->supportedMessages.client2server[loop+7]); rfbClientLog("server2client supported messages (bit flags)\n"); for (loop=0;loop<32;loop+=8) rfbClientLog("%02X: %04x %04x %04x %04x - %04x %04x %04x %04x\n", loop, - msgs.server2client[loop], msgs.server2client[loop+1], - msgs.server2client[loop+2], msgs.server2client[loop+3], - msgs.server2client[loop+4], msgs.server2client[loop+5], - msgs.server2client[loop+6], msgs.server2client[loop+7]); + client->supportedMessages.server2client[loop], client->supportedMessages.server2client[loop+1], + client->supportedMessages.server2client[loop+2], client->supportedMessages.server2client[loop+3], + client->supportedMessages.server2client[loop+4], client->supportedMessages.server2client[loop+5], + client->supportedMessages.server2client[loop+6], client->supportedMessages.server2client[loop+7]); continue; } @@ -1217,6 +1482,47 @@ HandleRFBServerMessage(rfbClient* client) break; } + case rfbTextChat: + { + char *buffer=NULL; + if (!ReadFromRFBServer(client, ((char *)&msg) + 1, + sz_rfbTextChatMsg- 1)) + return FALSE; + msg.tc.length = rfbClientSwap32IfLE(msg.sct.length); + switch(msg.tc.length) { + case rfbTextChatOpen: + rfbClientLog("Received TextChat Open\n"); + if (client->HandleTextChat!=NULL) + client->HandleTextChat(client, (int)rfbTextChatOpen, NULL); + break; + case rfbTextChatClose: + rfbClientLog("Received TextChat Close\n"); + if (client->HandleTextChat!=NULL) + client->HandleTextChat(client, (int)rfbTextChatClose, NULL); + break; + case rfbTextChatFinished: + rfbClientLog("Received TextChat Finished\n"); + if (client->HandleTextChat!=NULL) + client->HandleTextChat(client, (int)rfbTextChatFinished, NULL); + break; + default: + buffer=malloc(msg.tc.length+1); + if (!ReadFromRFBServer(client, buffer, msg.tc.length)) + { + free(buffer); + return FALSE; + } + /* Null Terminate <just in case> */ + buffer[msg.tc.length]=0; + rfbClientLog("Received TextChat \"%s\"\n", buffer); + if (client->HandleTextChat!=NULL) + client->HandleTextChat(client, (int)msg.tc.length, buffer); + free(buffer); + break; + } + break; + } + case rfbResizeFrameBuffer: { if (!ReadFromRFBServer(client, ((char *)&msg) + 1, diff --git a/libvncclient/ultra.c b/libvncclient/ultra.c index 641200f..3be150d 100644 --- a/libvncclient/ultra.c +++ b/libvncclient/ultra.c @@ -18,8 +18,6 @@ * USA. */ -#ifdef LIBVNCSERVER_HAVE_LIBZ - /* * ultrazip.c - handle ultrazip encoding. * @@ -210,5 +208,3 @@ HandleUltraZipBPP (rfbClient* client, int rx, int ry, int rw, int rh) } #undef CARDBPP - -#endif diff --git a/libvncclient/zrle.c b/libvncclient/zrle.c index 50654b3..701fb43 100644 --- a/libvncclient/zrle.c +++ b/libvncclient/zrle.c @@ -377,7 +377,7 @@ static int HandleZRLETile(rfbClient* client, #undef HandleZRLETile #undef UncompressCPixel #undef REALBPP -#undef UNCOMP #endif +#undef UNCOMP diff --git a/libvncserver/auth.c b/libvncserver/auth.c index fd4c487..6e7a617 100755 --- a/libvncserver/auth.c +++ b/libvncserver/auth.c @@ -317,7 +317,12 @@ rfbAuthProcessClientMessage(rfbClientPtr cl) if (rfbWriteExact(cl, (char *)&authResult, 4) < 0) { rfbLogPerror("rfbAuthProcessClientMessage: write"); } - rfbCloseClient(cl); + /* support RFB 3.8 clients, they expect a reason *why* it was disconnected */ + if (cl->protocolMinorVersion > 7) { + rfbClientConnFailed(cl, "password check failed!"); + } + else + rfbCloseClient(cl); return; } diff --git a/libvncserver/corre.c b/libvncserver/corre.c index c1164c0..75f1211 100755 --- a/libvncserver/corre.c +++ b/libvncserver/corre.c @@ -41,7 +41,7 @@ static char *rreBeforeBuf = NULL; static int rreAfterBufSize = 0; static char *rreAfterBuf = NULL; -static int rreAfterBufLen; +static int rreAfterBufLen = 0; static int subrectEncode8(uint8_t *data, int w, int h); static int subrectEncode16(uint16_t *data, int w, int h); @@ -145,9 +145,9 @@ rfbSendSmallRectEncodingCoRRE(rfbClientPtr cl, return rfbSendRectEncodingRaw(cl, x, y, w, h); } - cl->rectanglesSent[rfbEncodingCoRRE]++; - cl->bytesSent[rfbEncodingCoRRE] += (sz_rfbFramebufferUpdateRectHeader - + sz_rfbRREHeader + rreAfterBufLen); + rfbStatRecordEncodingSent(cl,rfbEncodingCoRRE, + sz_rfbFramebufferUpdateRectHeader + sz_rfbRREHeader + rreAfterBufLen, + sz_rfbFramebufferUpdateRectHeader + w * h * (cl->format.bitsPerPixel / 8)); if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbRREHeader > UPDATE_BUF_SIZE) diff --git a/libvncserver/cursor.c b/libvncserver/cursor.c index 2accc25..8cc5ecb 100644 --- a/libvncserver/cursor.c +++ b/libvncserver/cursor.c @@ -78,9 +78,6 @@ rfbSendCursorShape(rfbClientPtr cl) sz_rfbFramebufferUpdateRectHeader); cl->ublen += sz_rfbFramebufferUpdateRectHeader; - cl->cursorShapeBytesSent += sz_rfbFramebufferUpdateRectHeader; - cl->cursorShapeUpdatesSent++; - if (!rfbSendUpdateBuf(cl)) return FALSE; @@ -167,9 +164,8 @@ rfbSendCursorShape(rfbClientPtr cl) } /* Send everything we have prepared in the cl->updateBuf[]. */ - - cl->cursorShapeBytesSent += (cl->ublen - saved_ublen); - cl->cursorShapeUpdatesSent++; + rfbStatRecordMessageSent(cl, (cl->useRichCursorEncoding ? rfbEncodingRichCursor : rfbEncodingXCursor), + sz_rfbFramebufferUpdateRectHeader + (cl->ublen - saved_ublen), sz_rfbFramebufferUpdateRectHeader + (cl->ublen - saved_ublen)); if (!rfbSendUpdateBuf(cl)) return FALSE; @@ -201,8 +197,7 @@ rfbSendCursorPos(rfbClientPtr cl) sz_rfbFramebufferUpdateRectHeader); cl->ublen += sz_rfbFramebufferUpdateRectHeader; - cl->cursorPosBytesSent += sz_rfbFramebufferUpdateRectHeader; - cl->cursorPosUpdatesSent++; + rfbStatRecordMessageSent(cl, rfbEncodingPointerPos, sz_rfbFramebufferUpdateRectHeader, sz_rfbFramebufferUpdateRectHeader); if (!rfbSendUpdateBuf(cl)) return FALSE; diff --git a/libvncserver/hextile.c b/libvncserver/hextile.c index cd598ac..52920d8 100755 --- a/libvncserver/hextile.c +++ b/libvncserver/hextile.c @@ -44,7 +44,7 @@ rfbSendRectEncodingHextile(rfbClientPtr cl, int h) { rfbFramebufferUpdateRectHeader rect; - + if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) { if (!rfbSendUpdateBuf(cl)) return FALSE; @@ -60,8 +60,9 @@ rfbSendRectEncodingHextile(rfbClientPtr cl, sz_rfbFramebufferUpdateRectHeader); cl->ublen += sz_rfbFramebufferUpdateRectHeader; - cl->rectanglesSent[rfbEncodingHextile]++; - cl->bytesSent[rfbEncodingHextile] += sz_rfbFramebufferUpdateRectHeader; + rfbStatRecordEncodingSent(cl, rfbEncodingHextile, + sz_rfbFramebufferUpdateRectHeader, + sz_rfbFramebufferUpdateRectHeader + w * (cl->format.bitsPerPixel / 8) * h); switch (cl->format.bitsPerPixel) { case 8: @@ -136,6 +137,7 @@ sendHextiles##bpp(rfbClientPtr cl, int rx, int ry, int rw, int rh) { startUblen = cl->ublen; \ cl->updateBuf[startUblen] = 0; \ cl->ublen++; \ + rfbStatRecordEncodingSentAdd(cl, rfbEncodingHextile, 1); \ \ testColours##bpp(clientPixelData, w * h, \ &mono, &solid, &newBg, &newFg); \ @@ -148,7 +150,6 @@ sendHextiles##bpp(rfbClientPtr cl, int rx, int ry, int rw, int rh) { } \ \ if (solid) { \ - cl->bytesSent[rfbEncodingHextile] += cl->ublen - startUblen; \ continue; \ } \ \ @@ -175,15 +176,15 @@ sendHextiles##bpp(rfbClientPtr cl, int rx, int ry, int rw, int rh) { (*cl->translateFn)(cl->translateLookupTable, \ &(cl->screen->serverFormat), &cl->format, fbptr, \ (char *)clientPixelData, \ - cl->scaledScreen->paddedWidthInBytes, w, h); \ + cl->scaledScreen->paddedWidthInBytes, w, h); \ \ memcpy(&cl->updateBuf[cl->ublen], (char *)clientPixelData, \ w * h * (bpp/8)); \ \ cl->ublen += w * h * (bpp/8); \ + rfbStatRecordEncodingSentAdd(cl, rfbEncodingHextile, \ + w * h * (bpp/8)); \ } \ - \ - cl->bytesSent[rfbEncodingHextile] += cl->ublen - startUblen; \ } \ } \ \ @@ -210,6 +211,7 @@ subrectEncode##bpp(rfbClientPtr cl, uint##bpp##_t *data, int w, int h, \ nSubrectsUblen = cl->ublen; \ cl->ublen++; \ + rfbStatRecordEncodingSentAdd(cl, rfbEncodingHextile, 1); \ \ for (y=0; y<h; y++) { \ line = data+(y*w); \ @@ -268,6 +270,7 @@ subrectEncode##bpp(rfbClientPtr cl, uint##bpp##_t *data, int w, int h, \ cl->updateBuf[cl->ublen++] = rfbHextilePackXY(thex,they); \ cl->updateBuf[cl->ublen++] = rfbHextilePackWH(thew,theh); \ + rfbStatRecordEncodingSentAdd(cl, rfbEncodingHextile, 1); \ \ /* \ * Now mark the subrect as done. \ diff --git a/libvncserver/main.c b/libvncserver/main.c index 6a34980..cded456 100644 --- a/libvncserver/main.c +++ b/libvncserver/main.c @@ -495,22 +495,40 @@ clientInput(void *data) pthread_create(&output_thread, NULL, clientOutput, (void *)cl); while (1) { - fd_set fds; + fd_set rfds, wfds, efds; struct timeval tv; int n; - FD_ZERO(&fds); - FD_SET(cl->sock, &fds); + FD_ZERO(&rfds); + FD_SET(cl->sock, &rfds); + FD_ZERO(&efds); + FD_SET(cl->sock, &efds); + + /* Are we transferring a file in the background? */ + FD_ZERO(&wfds); + if ((cl->fileTransfer.fd!=-1) && (cl->fileTransfer.sending==1)) + FD_SET(cl->sock, &wfds); + tv.tv_sec = 60; /* 1 minute */ tv.tv_usec = 0; - n = select(cl->sock + 1, &fds, NULL, &fds, &tv); + n = select(cl->sock + 1, &rfds, &wfds, &efds, &tv); if (n < 0) { rfbLogPerror("ReadExact: select"); break; } if (n == 0) /* timeout */ + { + rfbSendFileTransferChunk(cl); continue; - rfbProcessClientMessage(cl); + } + + /* We have some space on the transmit queue, send some data */ + if (FD_ISSET(cl->sock, &wfds)) + rfbSendFileTransferChunk(cl); + + if (FD_ISSET(cl->sock, &rfds) || FD_ISSET(cl->sock, &efds)) + rfbProcessClientMessage(cl); + if (cl->sock == -1) { /* Client has disconnected. */ break; @@ -818,6 +836,10 @@ rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv, screen->handleEventsEagerly = FALSE; + /* Emulate UltraVNC Server by default */ + screen->protocolMajorVersion = 3; + screen->protocolMinorVersion = 6; + if(!rfbProcessArguments(screen,argc,argv)) { free(screen); return NULL; diff --git a/libvncserver/rfbserver.c b/libvncserver/rfbserver.c index 1945ae1..ab4e04f 100644 --- a/libvncserver/rfbserver.c +++ b/libvncserver/rfbserver.c @@ -66,6 +66,14 @@ #endif #include <stdarg.h> #include <scale.h> +/* stst() */ +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +/* readdir() */ +#include <dirent.h> +/* errno */ +#include <errno.h> static void rfbProcessClientProtocolVersion(rfbClientPtr cl); static void rfbProcessClientNormalMessage(rfbClientPtr cl); @@ -229,6 +237,20 @@ rfbReverseConnection(rfbScreenInfoPtr rfbScreen, } +void +rfbSetProtocolVersion(rfbScreenInfoPtr rfbScreen, int major_, int minor_) +{ + /* Permit the server to set the version to report */ + /* TODO: sanity checking */ + if ((major_==3) && (minor_ > 2 && minor_ < 9)) + { + rfbScreen->protocolMajorVersion = major_; + rfbScreen->protocolMinorVersion = minor_; + } + else + rfbLog("rfbSetProtocolVersion(%d,%d) set to invalid values\n", major_, minor_); +} + /* * rfbNewClient is called when a new connection has been made by whatever * means. @@ -348,6 +370,8 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen, #endif #endif + cl->fileTransfer.fd = -1; + cl->enableCursorShapeUpdates = FALSE; cl->enableCursorPosUpdates = FALSE; cl->useRichCursorEncoding = FALSE; @@ -378,8 +402,8 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen, cl->lastPtrX = -1; - sprintf(pv,rfbProtocolVersionFormat,rfbProtocolMajorVersion, - rfbProtocolMinorVersion); + sprintf(pv,rfbProtocolVersionFormat,rfbScreen->protocolMajorVersion, + rfbScreen->protocolMinorVersion); if (rfbWriteExact(cl, pv, sz_rfbProtocolVersionMsg) < 0) { rfbLogPerror("rfbNewClient: write"); @@ -583,7 +607,7 @@ rfbProcessClientProtocolVersion(rfbClientPtr cl) free(cl->host); cl->host=strdup(name); } - rfbLog("Protocol version %d.%d\n", major_, minor_); + rfbLog("Client Protocol Version %d.%d\n", major_, minor_); if (major_ != rfbProtocolMajorVersion) { /* Major version mismatch - send a ConnFailed message */ @@ -591,23 +615,24 @@ rfbProcessClientProtocolVersion(rfbClientPtr cl) rfbErr("Major version mismatch\n"); sprintf(failureReason, "RFB protocol version mismatch - server %d.%d, client %d.%d", - rfbProtocolMajorVersion,rfbProtocolMinorVersion,major_,minor_); + cl->screen->protocolMajorVersion, cl->screen->protocolMinorVersion, + major_,minor_); rfbClientConnFailed(cl, failureReason); return; } - /* Chk for the minor version use either of the two standard version of RFB */ + /* Check for the minor version use either of the two standard version of RFB */ + /* + * UltraVNC Viewer detects FileTransfer compatible servers via rfb versions + * 3.4, 3.6, 3.14, 3.16 + * It's a bad method, but it is what they use to enable features... + * maintaining RFB version compatibility across multiple servers is a pain + * Should use something like ServerIdentity encoding + */ cl->protocolMinorVersion = minor_; - if (minor_ > rfbProtocolMinorVersion) { - cl->protocolMinorVersion = rfbProtocolMinorVersion; - } else if (minor_ < rfbProtocolMinorVersion) { - cl->protocolMinorVersion = rfbProtocolFallbackMinorVersion; - } - if (minor_ != rfbProtocolMinorVersion && - minor_ != rfbProtocolFallbackMinorVersion) { - rfbLog("Non-standard protocol version %d.%d, using %d.%d instead\n", + + rfbLog("Protocol version sent %d.%d, using %d.%d\n", major_, minor_, rfbProtocolMajorVersion, cl->protocolMinorVersion); - } rfbAuthNewClient(cl); } @@ -775,8 +800,7 @@ rfbSendKeyboardLedState(rfbClientPtr cl) sz_rfbFramebufferUpdateRectHeader); cl->ublen += sz_rfbFramebufferUpdateRectHeader; - cl->cursorPosBytesSent += sz_rfbFramebufferUpdateRectHeader; - cl->cursorPosUpdatesSent++; + rfbStatRecordEncodingSent(cl, rfbEncodingKeyboardLedState, sz_rfbFramebufferUpdateRectHeader, sz_rfbFramebufferUpdateRectHeader); if (!rfbSendUpdateBuf(cl)) return FALSE; @@ -806,7 +830,7 @@ rfbSendSupportedMessages(rfbClientPtr cl) rect.encoding = Swap32IfLE(rfbEncodingSupportedMessages); rect.r.x = 0; rect.r.y = 0; - rect.r.w = Swap16IfLE(sz_rfbFramebufferUpdateRectHeader); + rect.r.w = Swap16IfLE(sz_rfbSupportedMessages); rect.r.h = 0; memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, @@ -823,10 +847,10 @@ rfbSendSupportedMessages(rfbClientPtr cl) rfbSetBit(msgs.client2server, rfbClientCutText); rfbSetBit(msgs.client2server, rfbFileTransfer); rfbSetBit(msgs.client2server, rfbSetScale); - //rfbSetBit(msgs.client2server, rfbSetServerInput); - //rfbSetBit(msgs.client2server, rfbSetSW); - //rfbSetBit(msgs.client2server, rfbTextChat); - //rfbSetBit(msgs.client2server, rfbKeyFrameRequest); + /*rfbSetBit(msgs.client2server, rfbSetServerInput); */ + /*rfbSetBit(msgs.client2server, rfbSetSW); */ + /*rfbSetBit(msgs.client2server, rfbTextChat); */ + /*rfbSetBit(msgs.client2server, rfbKeyFrameRequest); */ rfbSetBit(msgs.client2server, rfbPalmVNCSetScaleFactor); rfbSetBit(msgs.server2client, rfbFramebufferUpdate); @@ -834,12 +858,15 @@ rfbSendSupportedMessages(rfbClientPtr cl) rfbSetBit(msgs.server2client, rfbBell); rfbSetBit(msgs.server2client, rfbServerCutText); rfbSetBit(msgs.server2client, rfbResizeFrameBuffer); - //rfbSetBit(msgs.server2client, rfbKeyFrameUpdate); + /*rfbSetBit(msgs.server2client, rfbKeyFrameUpdate); */ rfbSetBit(msgs.server2client, rfbPalmVNCReSizeFrameBuffer); memcpy(&cl->updateBuf[cl->ublen], (char *)&msgs, sz_rfbSupportedMessages); cl->ublen += sz_rfbSupportedMessages; + rfbStatRecordEncodingSent(cl, rfbEncodingSupportedMessages, + sz_rfbFramebufferUpdateRectHeader+sz_rfbSupportedMessages, + sz_rfbFramebufferUpdateRectHeader+sz_rfbSupportedMessages); if (!rfbSendUpdateBuf(cl)) return FALSE; @@ -918,6 +945,10 @@ rfbSendSupportedEncodings(rfbClientPtr cl) rfbSendSupporteddEncodings_SendEncoding(cl, rfbEncodingSupportedEncodings); rfbSendSupporteddEncodings_SendEncoding(cl, rfbEncodingServerIdentity); + rfbStatRecordEncodingSent(cl, rfbEncodingSupportedEncodings, + sz_rfbFramebufferUpdateRectHeader+(nEncodings * sizeof(uint32_t)), + sz_rfbFramebufferUpdateRectHeader+(nEncodings * sizeof(uint32_t))); + if (!rfbSendUpdateBuf(cl)) return FALSE; @@ -973,17 +1004,655 @@ rfbSendServerIdentity(rfbClientPtr cl) memcpy(&cl->updateBuf[cl->ublen], buffer, strlen(buffer)+1); cl->ublen += strlen(buffer)+1; + rfbStatRecordEncodingSent(cl, rfbEncodingServerIdentity, + sz_rfbFramebufferUpdateRectHeader+strlen(buffer)+1, + sz_rfbFramebufferUpdateRectHeader+strlen(buffer)+1); + + if (!rfbSendUpdateBuf(cl)) return FALSE; return TRUE; } +rfbBool rfbSendFileTransferMessage(rfbClientPtr cl, uint8_t contentType, uint8_t contentParam, uint32_t size, uint32_t length, char *buffer) +{ + rfbFileTransferMsg ft; + ft.type = rfbFileTransfer; + ft.contentType = contentType; + ft.contentParam = contentParam; + ft.pad = 0; /* UltraVNC did not Swap16LE(ft.contentParam) (Looks like it might be BigEndian) */ + ft.size = Swap32IfLE(size); + ft.length = Swap32IfLE(length); + + /* + rfbLog("rfbSendFileTransferMessage( %dtype, %dparam, %dsize, %dlen, %p)\n", contentType, contentParam, size, length, buffer); + */ + if (rfbWriteExact(cl, (char *)&ft, sz_rfbFileTransferMsg) < 0) { + rfbLogPerror("rfbSendFileTransferMessage: write"); + rfbCloseClient(cl); + return FALSE; + } + if (length>0) + { + if (rfbWriteExact(cl, buffer, length) < 0) { + rfbLogPerror("rfbSendFileTransferMessage: write"); + rfbCloseClient(cl); + return FALSE; + } + } + + rfbStatRecordMessageSent(cl, rfbFileTransfer, sz_rfbFileTransferMsg+length, sz_rfbFileTransferMsg+length); + return TRUE; +} /* + * UltraVNC uses Windows Structures + */ +#define MAX_PATH 260 + +typedef struct _FILETIME { + uint32_t dwLowDateTime; + uint32_t dwHighDateTime; +} FILETIME; + +typedef struct _WIN32_FIND_DATA { + uint32_t dwFileAttributes; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + uint32_t nFileSizeHigh; + uint32_t nFileSizeLow; + uint32_t dwReserved0; + uint32_t dwReserved1; + uint8_t cFileName[ MAX_PATH ]; + uint8_t cAlternateFileName[ 14 ]; +} WIN32_FIND_DATA; + +#define FILE_ATTRIBUTE_READONLY 0x1 +#define FILE_ATTRIBUTE_HIDDEN 0x2 +#define FILE_ATTRIBUTE_SYSTEM 0x4 +#define FILE_ATTRIBUTE_DIRECTORY 0x10 +#define FILE_ATTRIBUTE_ARCHIVE 0x20 +#define FILE_ATTRIBUTE_NORMAL 0x80 +#define FILE_ATTRIBUTE_TEMPORARY 0x100 +#define FILE_ATTRIBUTE_COMPRESSED 0x800 + +rfbBool rfbFilenameTranslate2UNIX(rfbClientPtr cl, char *path, char *unixPath) +{ + int x; + char *home=NULL; + /* C: */ + if (path[0]=='C' && path[1]==':') + strcpy(unixPath, &path[2]); + else + { + home = getenv("HOME"); + if (home!=NULL) + { + strcpy(unixPath, home); + strcat(unixPath,"/"); + strcat(unixPath, path); + } + else + strcpy(unixPath, path); + } + for (x=0;x<strlen(unixPath);x++) + if (unixPath[x]=='\\') unixPath[x]='/'; + return TRUE; +} + +rfbBool rfbFilenameTranslate2DOS(rfbClientPtr cl, char *unixPath, char *path) +{ + int x; + 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; + WIN32_FIND_DATA win32filename; + int nOptLen = 0, retval=0; + DIR *dirp=NULL; + struct dirent *direntp=NULL; + + /* Client thinks we are Winblows */ + rfbFilenameTranslate2UNIX(cl, buffer, path); + +// /* + rfbLog("rfbProcessFileTransfer() rfbDirContentRequest: rfbRDirContent: \"%s\"->\"%s\"\n",buffer, path); +// */ + dirp=opendir(path); + if (dirp==NULL) + return rfbSendFileTransferMessage(cl, rfbDirPacket, rfbADirectory, 0, 0, NULL); + /* send back the path name (necessary for links) */ + if (rfbSendFileTransferMessage(cl, rfbDirPacket, rfbADirectory, 0, length, buffer)==FALSE) return FALSE; + for (direntp=readdir(dirp); direntp!=NULL; direntp=readdir(dirp)) + { + /* get stats */ + snprintf(retfilename,sizeof(retfilename),"%s/%s", path, direntp->d_name); + retval = stat(retfilename, &statbuf); + + if (retval==0) + { + memset((char *)&win32filename, 0, sizeof(win32filename)); + win32filename.dwFileAttributes = Swap32IfBE(FILE_ATTRIBUTE_NORMAL); + if (S_ISDIR(statbuf.st_mode)) + win32filename.dwFileAttributes = Swap32IfBE(FILE_ATTRIBUTE_DIRECTORY); + win32filename.ftCreationTime.dwLowDateTime = Swap32IfBE(statbuf.st_ctime); /* Intel Order */ + win32filename.ftCreationTime.dwHighDateTime = 0; + win32filename.ftLastAccessTime.dwLowDateTime = Swap32IfBE(statbuf.st_atime); /* Intel Order */ + win32filename.ftLastAccessTime.dwHighDateTime = 0; + win32filename.ftLastWriteTime.dwLowDateTime = Swap32IfBE(statbuf.st_mtime); /* Intel Order */ + win32filename.ftLastWriteTime.dwHighDateTime = 0; + win32filename.nFileSizeLow = Swap32IfBE(statbuf.st_size); /* Intel Order */ + win32filename.nFileSizeHigh = 0; + win32filename.dwReserved0 = 0; + win32filename.dwReserved1 = 0; + + /* If this had the full path, we would need to translate to DOS format ("C:\") */ + /* rfbFilenameTranslate2DOS(cl, retfilename, win32filename.cFileName); */ + strcpy((char *)win32filename.cFileName, direntp->d_name); + + /* Do not show hidden files (but show how to move up the tree) */ + if ((strcmp(direntp->d_name, "..")==0) || (direntp->d_name[0]!='.')) + { + nOptLen = sizeof(WIN32_FIND_DATA) - MAX_PATH - 14 + strlen((char *)win32filename.cFileName); + /* + rfbLog("rfbProcessFileTransfer() rfbDirContentRequest: rfbRDirContent: Sending \"%s\"\n", (char *)win32filename.cFileName); + */ + if (rfbSendFileTransferMessage(cl, rfbDirPacket, rfbADirectory, 0, nOptLen, (char *)&win32filename)==FALSE) return FALSE; + } + } + } + closedir(dirp); + /* End of the transfer */ + return rfbSendFileTransferMessage(cl, rfbDirPacket, 0, 0, 0, NULL); +} + + +char *rfbProcessFileTransferReadBuffer(rfbClientPtr cl, uint32_t length) +{ + char *buffer=NULL; + int n=0; + /* + rfbLog("rfbProcessFileTransferReadBuffer(%dlen)\n", length); + */ + if (length>0) { + buffer=malloc(length+1); + if (buffer!=NULL) { + if ((n = rfbReadExact(cl, (char *)buffer, length)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessFileTransferReadBuffer: read"); + rfbCloseClient(cl); + /* NOTE: don't forget to free(buffer) if you return early! */ + if (buffer!=NULL) free(buffer); + return NULL; + } + /* Null Terminate */ + buffer[length]=0; + } + } + return buffer; +} + + +rfbBool rfbSendFileTransferChunk(rfbClientPtr cl) +{ + /* Allocate buffer for compression */ + unsigned char readBuf[sz_rfbBlockSize]; + int bytesRead=0; + int retval=0; + fd_set wfds; + struct timeval tv; + int n; +#ifdef LIBVNCSERVER_HAVE_LIBZ + unsigned char compBuf[sz_rfbBlockSize + 1024]; + unsigned long nMaxCompSize = sizeof(compBuf); + int nRetC = 0; +#endif + + /* If not sending, or no file open... Return as if we sent something! */ + if ((cl->fileTransfer.fd!=-1) && (cl->fileTransfer.sending==1)) + { + FD_ZERO(&wfds); + FD_SET(cl->sock, &wfds); + + /* return immediately */ + tv.tv_sec = 0; + tv.tv_usec = 0; + n = select(cl->sock + 1, NULL, &wfds, NULL, &tv); + + if (n<1) + rfbLog("rfbSendFileTransferChunk() select failed: %s\n", strerror(errno)); + /* We have space on the transmit queue */ + if (n > 0) + { + bytesRead = read(cl->fileTransfer.fd, readBuf, sz_rfbBlockSize); + switch (bytesRead) { + case 0: + /* + rfbLog("rfbSendFileTransferChunk(): End-Of-File Encountered\n"); + */ + retval = rfbSendFileTransferMessage(cl, rfbEndOfFile, 0, 0, 0, NULL); + close(cl->fileTransfer.fd); + cl->fileTransfer.fd = -1; + cl->fileTransfer.sending = 0; + cl->fileTransfer.receiving = 0; + return retval; + case -1: + /* TODO : send an error msg to the client... */ + rfbLog("rfbSendFileTransferChunk(): %s\n",strerror(errno)); + retval = rfbSendFileTransferMessage(cl, rfbAbortFileTransfer, 0, 0, 0, NULL); + close(cl->fileTransfer.fd); + cl->fileTransfer.fd = -1; + cl->fileTransfer.sending = 0; + cl->fileTransfer.receiving = 0; + return retval; + default: + /* + rfbLog("rfbSendFileTransferChunk(): Read %d bytes\n", bytesRead); + */ + if (!cl->fileTransfer.compressionEnabled) + return rfbSendFileTransferMessage(cl, rfbFilePacket, 0, 0, bytesRead, (char *)readBuf); + else + { +#ifdef LIBVNCSERVER_HAVE_LIBZ + nRetC = compress(compBuf, &nMaxCompSize, readBuf, bytesRead); + /* + rfbLog("Compressed the packet from %d -> %d bytes\n", nMaxCompSize, bytesRead); + */ + + if ((nRetC==0) && (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 + + /* + 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 */ + rfbFilenameTranslate2UNIX(cl, buffer, filename1); + cl->fileTransfer.fd=open(filename1, O_RDONLY, 0744); + + /* + rfbLog("rfbProcessFileTransfer() rfbFileTransferRequest(\"%s\"->\"%s\") Open: %s\n", buffer, filename1, (cl->fileTransfer.fd==-1?"Failed":"Success")); + */ + + if (cl->fileTransfer.fd!=-1) { + if (fstat(cl->fileTransfer.fd, &statbuf)!=0) { + close(cl->fileTransfer.fd); + cl->fileTransfer.fd=-1; + } + else + { + /* Add the File Time Stamp to the filename */ + strftime(timespec, sizeof(timespec), "%m/%d/%Y %H:%M",gmtime(&statbuf.st_ctime)); + buffer=realloc(buffer, length + strlen(timespec) + 2); /* comma, and Null term */ + if (buffer==NULL) { + rfbLog("rfbProcessFileTransfer() rfbFileTransferRequest: Failed to malloc %d bytes\n", length + strlen(timespec) + 2); + return FALSE; + } + strcat(buffer,","); + strcat(buffer, timespec); + length = strlen(buffer); + } + } + + /* The viewer supports compression if size==1 */ + cl->fileTransfer.compressionEnabled = (size==1); + + /* + rfbLog("rfbProcessFileTransfer() rfbFileTransferRequest(\"%s\"->\"%s\")%s\n", buffer, filename1, (size==1?" <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; + if (rfbWriteExact(cl, (char *)&sizeHtmp, 4) < 0) { + rfbLogPerror("rfbProcessFileTransfer: write"); + rfbCloseClient(cl); + if (buffer!=NULL) free(buffer); + return FALSE; + } + break; + + case rfbFileHeader: + /* Destination file (viewer side) is ready for reception (size > 0) or not (size = -1) */ + if (size==-1) { + rfbLog("rfbProcessFileTransfer() rfbFileHeader (error, aborting)\n"); + close(cl->fileTransfer.fd); + cl->fileTransfer.fd=-1; + return TRUE; + } + + /* + rfbLog("rfbProcessFileTransfer() rfbFileHeader (%d bytes of a file)\n", size); + */ + + /* Starts the transfer! */ + cl->fileTransfer.sending=1; + return rfbSendFileTransferChunk(cl); + break; + + + /* + * sending from the viewer to the server + */ + + case rfbFileTransferOffer: + /* client is sending a file to us */ + /* buffer contains full path name (plus FileTime) */ + /* size contains size of the file */ + /* + rfbLog("rfbProcessFileTransfer() rfbFileTransferOffer:\n"); + */ + if ((buffer = rfbProcessFileTransferReadBuffer(cl, length))==NULL) return FALSE; + + /* Parse the FileTime */ + p = strrchr(buffer, ','); + if (p!=NULL) { + *p = '\0'; + strcpy(szFileTime, p+1); + } else + szFileTime[0]=0; + + + + /* Need to read in sizeHtmp */ + if ((n = rfbReadExact(cl, (char *)&sizeHtmp, 4)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessFileTransfer: read sizeHtmp"); + rfbCloseClient(cl); + /* NOTE: don't forget to free(buffer) if you return early! */ + if (buffer!=NULL) free(buffer); + return FALSE; + } + sizeHtmp = Swap32IfLE(sizeHtmp); + + rfbFilenameTranslate2UNIX(cl, buffer, filename1); + + /* If the file exists... We can send a rfbFileChecksums back to the client before we send an rfbFileAcceptHeader */ + /* TODO: Delta Transfer */ + + cl->fileTransfer.fd=open(filename1, O_CREAT|O_WRONLY|O_TRUNC, 0744); + /* + rfbLog("rfbProcessFileTransfer() rfbFileTransferOffer(\"%s\"->\"%s\") %s %s\n", buffer, filename1, (cl->fileTransfer.fd==-1?"Failed":"Success"), (cl->fileTransfer.fd==-1?strerror(errno):"")); + */ + + /* File Size in bytes, 0xFFFFFFFF (-1) means error */ + retval = rfbSendFileTransferMessage(cl, rfbFileAcceptHeader, 0, (cl->fileTransfer.fd==-1 ? -1 : 0), length, buffer); + if (cl->fileTransfer.fd==-1) { + free(buffer); + return retval; + } + + /* setup filetransfer stuff */ + cl->fileTransfer.fileSize = size; + cl->fileTransfer.numPackets = size / sz_rfbBlockSize; + cl->fileTransfer.receiving = 1; + cl->fileTransfer.sending = 0; + break; + + case rfbFilePacket: + /* + rfbLog("rfbProcessFileTransfer() rfbFilePacket:\n"); + */ + if ((buffer = rfbProcessFileTransferReadBuffer(cl, length))==NULL) return FALSE; + if (cl->fileTransfer.fd!=-1) { + /* buffer contains the contents of the file */ + if (size==0) + retval=write(cl->fileTransfer.fd, buffer, length); + else + { +#ifdef LIBVNCSERVER_HAVE_LIBZ + /* compressed packet */ + nRet = uncompress(compBuff,&nRawBytes,(const unsigned char*)buffer, length); + retval=write(cl->fileTransfer.fd, compBuff, nRawBytes); +#else + /* Write the file out as received... */ + retval=write(cl->fileTransfer.fd, buffer, length); +#endif + } + if (retval==-1) + { + close(cl->fileTransfer.fd); + cl->fileTransfer.fd=-1; + cl->fileTransfer.sending = 0; + cl->fileTransfer.receiving = 0; + } + } + break; + + case rfbEndOfFile: + /* + rfbLog("rfbProcessFileTransfer() rfbEndOfFile\n"); + */ + if (cl->fileTransfer.fd!=-1) + close(cl->fileTransfer.fd); + cl->fileTransfer.fd=-1; + cl->fileTransfer.sending = 0; + cl->fileTransfer.receiving = 0; + break; + + case rfbAbortFileTransfer: + /* + rfbLog("rfbProcessFileTransfer() rfbAbortFileTransfer\n"); + */ + if (cl->fileTransfer.fd!=-1) + { + close(cl->fileTransfer.fd); + cl->fileTransfer.fd=-1; + cl->fileTransfer.sending = 0; + cl->fileTransfer.receiving = 0; + } + else + { + /* We use this message for FileTransfer rights (<=RC18 versions) + * The client asks for FileTransfer permission + */ + if (contentParam == 0) + { + rfbLog("rfbProcessFileTransfer() File Transfer Permission DENIED! (Client Version <=RC18)\n"); + /* Old method for FileTransfer handshake perimssion (<=RC18) (Deny it)*/ + return rfbSendFileTransferMessage(cl, rfbAbortFileTransfer, 0, -1, 0, ""); + } + /* New method is allowed */ + if (cl->screen->getFileTransferPermission!=NULL) + { + if (cl->screen->getFileTransferPermission(cl)==TRUE) + { + rfbLog("rfbProcessFileTransfer() File Transfer Permission Granted!\n"); + return rfbSendFileTransferMessage(cl, rfbFileTransferAccess, 0, 1 , 0, ""); /* Permit */ + } + else + { + rfbLog("rfbProcessFileTransfer() File Transfer Permission DENIED!\n"); + return rfbSendFileTransferMessage(cl, rfbFileTransferAccess, 0, -1 , 0, ""); /* Deny */ + } + } + rfbLog("rfbProcessFileTransfer() File Transfer Permission DENIED by default!\n"); + return rfbSendFileTransferMessage(cl, rfbFileTransferAccess, 0, -1 , 0, ""); /* DEFAULT: DENY (for security) */ + } + break; + + + case rfbCommand: + /* + rfbLog("rfbProcessFileTransfer() rfbCommand:\n"); + */ + if ((buffer = rfbProcessFileTransferReadBuffer(cl, length))==NULL) return FALSE; + switch (contentParam) { + case rfbCDirCreate: /* Client requests the creation of a directory */ + rfbFilenameTranslate2UNIX(cl, buffer, filename1); + retval = mkdir(filename1, 0755); + /* + rfbLog("rfbProcessFileTransfer() rfbCommand: rfbCDirCreate(\"%s\"->\"%s\") %s\n", buffer, filename1, (retval==-1?"Failed":"Success")); + */ + retval = rfbSendFileTransferMessage(cl, rfbCommandReturn, rfbADirCreate, retval, length, buffer); + if (buffer!=NULL) free(buffer); + return retval; + case rfbCFileDelete: /* Client requests the deletion of a file */ + rfbFilenameTranslate2UNIX(cl, buffer, filename1); + if (stat(filename1,&statbuf)==0) + { + if (S_ISDIR(statbuf.st_mode)) + retval = rmdir(filename1); + else + retval = unlink(filename1); + } + else retval=-1; + retval = rfbSendFileTransferMessage(cl, rfbCommandReturn, rfbAFileDelete, retval, length, buffer); + if (buffer!=NULL) free(buffer); + return retval; + case rfbCFileRename: /* Client requests the Renaming of a file/directory */ + p = strrchr(buffer, '*'); + if (p != NULL) + { + /* Split into 2 filenames ('*' is a seperator) */ + *p = '\0'; + rfbFilenameTranslate2UNIX(cl, buffer, filename1); + rfbFilenameTranslate2UNIX(cl, p+1, filename2); + retval = rename(filename1,filename2); + /* + rfbLog("rfbProcessFileTransfer() rfbCommand: rfbCFileRename(\"%s\"->\"%s\" -->> \"%s\"->\"%s\") %s\n", buffer, filename1, p+1, filename2, (retval==-1?"Failed":"Success")); + */ + /* Restore the buffer so the reply is good */ + *p = '*'; + retval = rfbSendFileTransferMessage(cl, rfbCommandReturn, rfbAFileRename, retval, length, buffer); + if (buffer!=NULL) free(buffer); + return retval; + } + break; + } + + break; + } + + /* NOTE: don't forget to free(buffer) if you return early! */ + if (buffer!=NULL) free(buffer); + return TRUE; +} + +/* * rfbProcessClientNormalMessage is called when the client has sent a normal * protocol message. */ @@ -994,6 +1663,11 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) int n=0; rfbClientToServerMsg msg; char *str; + int i; + uint32_t enc=0; + uint32_t lastPreferredEncoding = -1; + char encBuf[64]; + char encBuf2[64]; if ((n = rfbReadExact(cl, (char *)&msg, 1)) <= 0) { if (n != 0) @@ -1028,6 +1702,8 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) cl->readyForSetColourMapEntries = TRUE; cl->screen->setTranslateFunction(cl); + rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbSetPixelFormatMsg, sz_rfbSetPixelFormatMsg); + return; @@ -1039,16 +1715,23 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) rfbCloseClient(cl); return; } + rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbSetPixelFormatMsg, sz_rfbSetPixelFormatMsg); rfbLog("rfbProcessClientNormalMessage: %s", "FixColourMapEntries unsupported\n"); rfbCloseClient(cl); return; + /* NOTE: Some clients send us a set of encodings (ie: PointerPos) designed to enable/disable features... + * We may want to look into this... + * Example: + * case rfbEncodingXCursor: + * cl->enableCursorShapeUpdates = TRUE; + * + * Currently: cl->enableCursorShapeUpdates can *never* be turned off... + */ case rfbSetEncodings: { - int i; - uint32_t enc; if ((n = rfbReadExact(cl, ((char *)&msg) + 1, sz_rfbSetEncodingsMsg - 1)) <= 0) { @@ -1060,6 +1743,31 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) msg.se.nEncodings = Swap16IfLE(msg.se.nEncodings); + rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbSetEncodingsMsg+(msg.se.nEncodings*4),sz_rfbSetEncodingsMsg+(msg.se.nEncodings*4)); + + /* + * UltraVNC Client has the ability to adapt to changing network environments + * So, let's give it a change to tell us what it wants now! + */ + if (cl->preferredEncoding!=-1) + lastPreferredEncoding = cl->preferredEncoding; + + /* Reset all flags to defaults (allows us to switch between PointerPos and Server Drawn Cursors) */ + cl->preferredEncoding=-1; + cl->useCopyRect = FALSE; + cl->useNewFBSize = FALSE; + cl->cursorWasChanged = FALSE; + cl->useRichCursorEncoding = FALSE; + cl->enableCursorPosUpdates = FALSE; + cl->enableCursorShapeUpdates = FALSE; + cl->enableCursorShapeUpdates = FALSE; + cl->enableLastRectEncoding = FALSE; + cl->enableKeyboardLedState = FALSE; + cl->enableSupportedMessages = FALSE; + cl->enableSupportedEncodings = FALSE; + cl->enableServerIdentity = FALSE; + + for (i = 0; i < msg.se.nEncodings; i++) { if ((n = rfbReadExact(cl, (char *)&enc, 4)) <= 0) { if (n != 0) @@ -1075,58 +1783,23 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) cl->useCopyRect = TRUE; break; case rfbEncodingRaw: - if (cl->preferredEncoding == -1) { - cl->preferredEncoding = enc; - rfbLog("Using raw encoding for client %s\n", - cl->host); - } - break; case rfbEncodingRRE: - if (cl->preferredEncoding == -1) { - cl->preferredEncoding = enc; - rfbLog("Using rre encoding for client %s\n", - cl->host); - } - break; case rfbEncodingCoRRE: - if (cl->preferredEncoding == -1) { - cl->preferredEncoding = enc; - rfbLog("Using CoRRE encoding for client %s\n", - cl->host); - } - break; case rfbEncodingHextile: - if (cl->preferredEncoding == -1) { - cl->preferredEncoding = enc; - rfbLog("Using hextile encoding for client %s\n", - cl->host); - } - break; case rfbEncodingUltra: - if (cl->preferredEncoding == -1) { - cl->preferredEncoding = enc; - rfbLog("Using Ultra encoding for client %s\n", - cl->host); - } - break; #ifdef LIBVNCSERVER_HAVE_LIBZ case rfbEncodingZlib: - if (cl->preferredEncoding == -1) { - cl->preferredEncoding = enc; - rfbLog("Using zlib encoding for client %s\n", - cl->host); - } - break; + case rfbEncodingZRLE: #ifdef LIBVNCSERVER_HAVE_LIBJPEG case rfbEncodingTight: - if (cl->preferredEncoding == -1) { - cl->preferredEncoding = enc; - rfbLog("Using tight encoding for client %s\n", - cl->host); - } - break; #endif #endif + /* The first supported encoding is the 'preferred' encoding */ + if (cl->preferredEncoding == -1) + cl->preferredEncoding = enc; + + + break; case rfbEncodingXCursor: if(!cl->screen->dontConvertRichCursorToXCursor) { rfbLog("Enabling X-style cursor updates for client %s\n", @@ -1200,15 +1873,6 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) cl->enableServerIdentity = TRUE; } break; -#ifdef LIBVNCSERVER_HAVE_LIBZ - case rfbEncodingZRLE: - if (cl->preferredEncoding == -1) { - cl->preferredEncoding = enc; - rfbLog("Using ZRLE encoding for client %s\n", - cl->host); - } - break; -#endif default: #ifdef LIBVNCSERVER_HAVE_LIBZ if ( enc >= (uint32_t)rfbEncodingCompressLevel0 && @@ -1268,17 +1932,36 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) if(!handled) rfbLog("rfbProcessClientNormalMessage: " - "ignoring unknown encoding type %d\n", - (int)enc); + "ignoring unsupported encoding type %s\n", + encodingName(enc,encBuf,sizeof(encBuf))); } } } } + + if (cl->preferredEncoding == -1) { - cl->preferredEncoding = rfbEncodingRaw; + if (lastPreferredEncoding==-1) { + cl->preferredEncoding = rfbEncodingRaw; + rfbLog("Defaulting to %s encoding for client %s\n", encodingName(cl->preferredEncoding,encBuf,sizeof(encBuf)),cl->host); + } + else { + cl->preferredEncoding = lastPreferredEncoding; + rfbLog("Sticking with %s encoding for client %s\n", encodingName(cl->preferredEncoding,encBuf,sizeof(encBuf)),cl->host); + } } - + else + { + if (lastPreferredEncoding==-1) { + rfbLog("Using %s encoding for client %s\n", encodingName(cl->preferredEncoding,encBuf,sizeof(encBuf)),cl->host); + } else { + rfbLog("Switching from %s to %s Encoding for client %s\n", + encodingName(lastPreferredEncoding,encBuf2,sizeof(encBuf2)), + encodingName(cl->preferredEncoding,encBuf,sizeof(encBuf)), cl->host); + } + } + if (cl->enableCursorPosUpdates && !cl->enableCursorShapeUpdates) { rfbLog("Disabling cursor position updates for client %s\n", cl->host); @@ -1301,6 +1984,7 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) return; } + rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbFramebufferUpdateRequestMsg,sz_rfbFramebufferUpdateRequestMsg); /* The values come in based on the scaled screen, we need to convert them to * values based on the main screen's coordinate system @@ -1347,8 +2031,6 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) case rfbKeyEvent: - cl->keyEventsRcvd++; - if ((n = rfbReadExact(cl, ((char *)&msg) + 1, sz_rfbKeyEventMsg - 1)) <= 0) { if (n != 0) @@ -1357,6 +2039,8 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) return; } + rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbKeyEventMsg, sz_rfbKeyEventMsg); + if(!cl->viewOnly) { cl->screen->kbdAddEvent(msg.ke.down, (rfbKeySym)Swap32IfLE(msg.ke.key), cl); } @@ -1366,8 +2050,6 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) case rfbPointerEvent: - cl->pointerEventsRcvd++; - if ((n = rfbReadExact(cl, ((char *)&msg) + 1, sz_rfbPointerEventMsg - 1)) <= 0) { if (n != 0) @@ -1376,6 +2058,8 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) return; } + rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbPointerEventMsg, sz_rfbPointerEventMsg); + if (cl->screen->pointerClient && cl->screen->pointerClient != cl) return; @@ -1401,6 +2085,115 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) return; + case rfbFileTransfer: + if ((n = rfbReadExact(cl, ((char *)&msg) + 1, + sz_rfbFileTransferMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseClient(cl); + return; + } + msg.ft.size = Swap32IfLE(msg.ft.size); + msg.ft.length = Swap32IfLE(msg.ft.length); + /* record statistics in rfbProcessFileTransfer as length is filled with garbage when it is not valid */ + rfbProcessFileTransfer(cl, msg.ft.contentType, msg.ft.contentParam, msg.ft.size, msg.ft.length); + return; + + case rfbSetSW: + if ((n = rfbReadExact(cl, ((char *)&msg) + 1, + sz_rfbSetSWMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseClient(cl); + return; + } + msg.sw.x = Swap16IfLE(msg.sw.x); + msg.sw.y = Swap16IfLE(msg.sw.y); + rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbSetSWMsg, sz_rfbSetSWMsg); + /* msg.sw.status is not initialized in the ultraVNC viewer and contains random numbers (why???) */ + + rfbLog("Received a rfbSetSingleWindow(%d x, %d y)\n", msg.sw.x, msg.sw.y); + if (cl->screen->setSingleWindow!=NULL) + cl->screen->setSingleWindow(cl, msg.sw.x, msg.sw.y); + return; + + case rfbSetServerInput: + if ((n = rfbReadExact(cl, ((char *)&msg) + 1, + sz_rfbSetServerInputMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseClient(cl); + return; + } + rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbSetServerInputMsg, sz_rfbSetServerInputMsg); + + /* msg.sim.pad is not initialized in the ultraVNC viewer and contains random numbers (why???) */ + /* msg.sim.pad = Swap16IfLE(msg.sim.pad); */ + + rfbLog("Received a rfbSetServerInput(%d status)\n", msg.sim.status); + if (cl->screen->setServerInput!=NULL) + cl->screen->setServerInput(cl, msg.sim.status); + return; + + case rfbTextChat: + if ((n = rfbReadExact(cl, ((char *)&msg) + 1, + sz_rfbTextChatMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseClient(cl); + return; + } + + msg.tc.pad2 = Swap16IfLE(msg.tc.pad2); + msg.tc.length = Swap32IfLE(msg.tc.length); + + switch (msg.tc.length) { + case rfbTextChatOpen: + case rfbTextChatClose: + case rfbTextChatFinished: + /* commands do not have text following */ + /* Why couldn't they have used the pad byte??? */ + str=NULL; + rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbTextChatMsg, sz_rfbTextChatMsg); + break; + default: + if ((msg.tc.length>0) && (msg.tc.length<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, @@ -1422,7 +2215,7 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) rfbCloseClient(cl); return; } - + rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbClientCutTextMsg+msg.cct.length, sz_rfbClientCutTextMsg+msg.cct.length); if(!cl->viewOnly) { cl->screen->setXCutText(str, msg.cct.length, cl); } @@ -1432,6 +2225,20 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) case rfbPalmVNCSetScaleFactor: cl->PalmVNC = TRUE; + if ((n = rfbReadExact(cl, ((char *)&msg) + 1, + sz_rfbSetScaleMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseClient(cl); + return; + } + rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbSetScaleMsg, sz_rfbSetScaleMsg); + rfbLog("rfbSetScale(%d)\n", msg.ssc.scale); + rfbScalingSetup(cl,cl->screen->width/msg.ssc.scale, cl->screen->height/msg.ssc.scale); + + rfbSendNewScaleSize(cl); + return; + case rfbSetScale: if ((n = rfbReadExact(cl, ((char *)&msg) + 1, @@ -1441,11 +2248,11 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) rfbCloseClient(cl); return; } + rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbSetScaleMsg, sz_rfbSetScaleMsg); rfbLog("rfbSetScale(%d)\n", msg.ssc.scale); rfbScalingSetup(cl,cl->screen->width/msg.ssc.scale, cl->screen->height/msg.ssc.scale); rfbSendNewScaleSize(cl); - return; default: @@ -1456,7 +2263,10 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) next = e->next; if(e->extension->handleMessage && e->extension->handleMessage(cl, e->data, &msg)) + { + rfbStatRecordMessageRcvd(cl, msg.type, 0, 0); /* Extension should handle this */ return; + } e = next; } @@ -1508,7 +2318,6 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, LOCK(cl->updateMutex); cl->newFBSizePending = FALSE; UNLOCK(cl->updateMutex); - cl->framebufferUpdateMessagesSent++; fu->type = rfbFramebufferUpdate; fu->nRects = Swap16IfLE(1); cl->ublen = sz_rfbFramebufferUpdateMsg; @@ -1690,11 +2499,11 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, rfbShowCursor(cl); } - /* + /* * Now send the update. */ - cl->framebufferUpdateMessagesSent++; - + + rfbStatRecordMessageSent(cl, rfbFramebufferUpdate, 0, 0); if (cl->preferredEncoding == rfbEncodingCoRRE) { nUpdateRegionRects = 0; @@ -1843,9 +2652,6 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, if (cl->screen!=cl->scaledScreen) rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h, "rfbSendFramebufferUpdate"); - cl->rawBytesEquivalent += (sz_rfbFramebufferUpdateRectHeader - + w * (cl->format.bitsPerPixel / 8) * h); - switch (cl->preferredEncoding) { case -1: case rfbEncodingRaw: @@ -1961,10 +2767,8 @@ rfbSendCopyRegion(rfbClientPtr cl, memcpy(&cl->updateBuf[cl->ublen], (char *)&cr, sz_rfbCopyRect); cl->ublen += sz_rfbCopyRect; - cl->rectanglesSent[rfbEncodingCopyRect]++; - cl->bytesSent[rfbEncodingCopyRect] - += sz_rfbFramebufferUpdateRectHeader + sz_rfbCopyRect; - + rfbStatRecordEncodingSent(cl, rfbEncodingCopyRect, sz_rfbFramebufferUpdateRectHeader + sz_rfbCopyRect, + w * h * (cl->scaledScreen->bitsPerPixel / 8)); } sraRgnReleaseIterator(i); @@ -2003,9 +2807,9 @@ rfbSendRectEncodingRaw(rfbClientPtr cl, memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,sz_rfbFramebufferUpdateRectHeader); cl->ublen += sz_rfbFramebufferUpdateRectHeader; - cl->rectanglesSent[rfbEncodingRaw]++; - cl->bytesSent[rfbEncodingRaw] - += sz_rfbFramebufferUpdateRectHeader + bytesPerLine * h; + + rfbStatRecordEncodingSent(cl, rfbEncodingRaw, sz_rfbFramebufferUpdateRectHeader + bytesPerLine * h, + sz_rfbFramebufferUpdateRectHeader + bytesPerLine * h); nlines = (UPDATE_BUF_SIZE - cl->ublen) / bytesPerLine; @@ -2069,8 +2873,8 @@ rfbSendLastRectMarker(rfbClientPtr cl) memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,sz_rfbFramebufferUpdateRectHeader); cl->ublen += sz_rfbFramebufferUpdateRectHeader; - cl->lastRectMarkersSent++; - cl->lastRectBytesSent += sz_rfbFramebufferUpdateRectHeader; + + rfbStatRecordEncodingSent(cl, rfbEncodingLastRect, sz_rfbFramebufferUpdateRectHeader, sz_rfbFramebufferUpdateRectHeader); return TRUE; } @@ -2108,8 +2912,7 @@ rfbSendNewFBSize(rfbClientPtr cl, sz_rfbFramebufferUpdateRectHeader); cl->ublen += sz_rfbFramebufferUpdateRectHeader; - cl->lastRectMarkersSent++; - cl->lastRectBytesSent += sz_rfbFramebufferUpdateRectHeader; + rfbStatRecordEncodingSent(cl, rfbEncodingNewFBSize, sz_rfbFramebufferUpdateRectHeader, sz_rfbFramebufferUpdateRectHeader); return TRUE; } @@ -2181,6 +2984,8 @@ rfbSendSetColourMapEntries(rfbClientPtr cl, rfbCloseClient(cl); return FALSE; } + + rfbStatRecordMessageSent(cl, rfbSetColourMapEntries, len, len); return TRUE; } @@ -2203,6 +3008,7 @@ rfbSendBell(rfbScreenInfoPtr rfbScreen) rfbCloseClient(cl); } } + rfbStatRecordMessageSent(cl, rfbBell, sz_rfbBellMsg, sz_rfbBellMsg); rfbReleaseClientIterator(i); } @@ -2232,6 +3038,7 @@ rfbSendServerCutText(rfbScreenInfoPtr rfbScreen,char *str, int len) rfbLogPerror("rfbSendServerCutText: write"); rfbCloseClient(cl); } + rfbStatRecordMessageSent(cl, rfbServerCutText, sz_rfbServerCutTextMsg+len, sz_rfbServerCutTextMsg+len); } rfbReleaseClientIterator(iterator); } diff --git a/libvncserver/rre.c b/libvncserver/rre.c index 8ef91fd..c0759aa 100755 --- a/libvncserver/rre.c +++ b/libvncserver/rre.c @@ -40,7 +40,7 @@ static char *rreBeforeBuf = NULL; static int rreAfterBufSize = 0; static char *rreAfterBuf = NULL; -static int rreAfterBufLen; +static int rreAfterBufLen=0; static int subrectEncode8(uint8_t *data, int w, int h); static int subrectEncode16(uint16_t *data, int w, int h); @@ -112,9 +112,9 @@ rfbSendRectEncodingRRE(rfbClientPtr cl, return rfbSendRectEncodingRaw(cl, x, y, w, h); } - cl->rectanglesSent[rfbEncodingRRE]++; - cl->bytesSent[rfbEncodingRRE] += (sz_rfbFramebufferUpdateRectHeader - + sz_rfbRREHeader + rreAfterBufLen); + rfbStatRecordEncodingSent(cl, rfbEncodingRRE, + sz_rfbFramebufferUpdateRectHeader + sz_rfbRREHeader + rreAfterBufLen, + sz_rfbFramebufferUpdateRectHeader + w * h * (cl->format.bitsPerPixel / 8)); if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbRREHeader > UPDATE_BUF_SIZE) diff --git a/libvncserver/scale.c b/libvncserver/scale.c index 63d232e..67de35f 100644 --- a/libvncserver/scale.c +++ b/libvncserver/scale.c @@ -128,8 +128,8 @@ void rfbScaledCorrection(rfbScreenInfoPtr from, rfbScreenInfoPtr to, int *x, int *h = (int)h2; /* Small changes for a thumbnail may be scaled to zero */ - if (*w==0) *w++; - if (*h==0) *h++; + if (*w==0) (*w)++; + if (*h==0) (*h)++; /* scaling from small to big may overstep the size a bit */ if (*x+*w > to->width) *w=to->width - *x; if (*y+*h > to->height) *h=to->height - *y; @@ -212,7 +212,9 @@ void rfbScaledScreenUpdateRect(rfbScreenInfoPtr screen, rfbScreenInfoPtr ptr, in pixel_value += (srcptr2[z] << (8 * z)); break; } - //srcptr2 += bytesPerPixel; + /* + srcptr2 += bytesPerPixel; + */ red += ((pixel_value >> redShift) & redMax); green += ((pixel_value >> greenShift) & greenMax); diff --git a/libvncserver/sockets.c b/libvncserver/sockets.c index 181862d..0d04f0a 100755 --- a/libvncserver/sockets.c +++ b/libvncserver/sockets.c @@ -239,8 +239,18 @@ rfbCheckFds(rfbScreenInfoPtr rfbScreen,long usec) tv.tv_usec = usec; nfds = select(rfbScreen->maxFd + 1, &fds, NULL, NULL /* &fds */, &tv); if (nfds == 0) { + /* timed out, check for async events */ + i = rfbGetClientIterator(rfbScreen); + while((cl = rfbClientIteratorNext(i))) { + if (cl->onHold) + continue; + if (FD_ISSET(cl->sock, &(rfbScreen->allFds))) + rfbSendFileTransferChunk(cl); + } + rfbReleaseClientIterator(i); return result; } + if (nfds < 0) { #ifdef WIN32 errno = WSAGetLastError(); @@ -332,11 +342,17 @@ rfbCheckFds(rfbScreenInfoPtr rfbScreen,long usec) i = rfbGetClientIterator(rfbScreen); while((cl = rfbClientIteratorNext(i))) { + if (cl->onHold) continue; - if (FD_ISSET(cl->sock, &fds) && - FD_ISSET(cl->sock, &(rfbScreen->allFds))) - rfbProcessClientMessage(cl); + + if (FD_ISSET(cl->sock, &(rfbScreen->allFds))) + { + if (FD_ISSET(cl->sock, &fds)) + rfbProcessClientMessage(cl); + else + rfbSendFileTransferChunk(cl); + } } rfbReleaseClientIterator(i); } while(rfbScreen->handleEventsEagerly); diff --git a/libvncserver/stats.c b/libvncserver/stats.c index ed9a3d0..0f8758f 100755 --- a/libvncserver/stats.c +++ b/libvncserver/stats.c @@ -26,90 +26,434 @@ #include <rfb/rfb.h> -static const char* encNames[] = { - "raw", "copyRect", "RRE", "[encoding 3]", "CoRRE", "hextile", - "zlib", "tight", "[encoding 8]", "ultra", "[encoding 10]", - "[encoding 11]", "[encoding 12]", "[encoding 13]", "[encoding 14]", - "[encoding 15]", - "ZRLE", "[encoding 17]", "[encoding 18]", "[encoding 19]", "[encoding 20]" -}; +char *messageNameServer2Client(uint32_t type, char *buf, int len); +char *messageNameClient2Server(uint32_t type, char *buf, int len); +char *encodingName(uint32_t enc, char *buf, int len); +rfbStatList *rfbStatLookupEncoding(rfbClientPtr cl, uint32_t type); +rfbStatList *rfbStatLookupMessage(rfbClientPtr cl, uint32_t type); -void -rfbResetStats(rfbClientPtr cl) +void rfbStatRecordEncodingSent(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw); +void rfbStatRecordEncodingRcvd(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw); +void rfbStatRecordMessageSent(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw); +void rfbStatRecordMessageRcvd(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw); +void rfbResetStats(rfbClientPtr cl); +void rfbPrintStats(rfbClientPtr cl); + + + + +char *messageNameServer2Client(uint32_t type, char *buf, int len) { + if (buf==NULL) return "error"; + switch (type) { + case rfbFramebufferUpdate: snprintf(buf, len, "FramebufferUpdate"); break; + case rfbSetColourMapEntries: snprintf(buf, len, "SetColourMapEntries"); break; + case rfbBell: snprintf(buf, len, "Bell"); break; + case rfbServerCutText: snprintf(buf, len, "ServerCutText"); break; + case rfbResizeFrameBuffer: snprintf(buf, len, "ResizeFrameBuffer"); break; + case rfbKeyFrameUpdate: snprintf(buf, len, "KeyFrameUpdate"); break; + case rfbFileTransfer: snprintf(buf, len, "FileTransfer"); break; + case rfbTextChat: snprintf(buf, len, "TextChat"); break; + case rfbPalmVNCReSizeFrameBuffer: snprintf(buf, len, "PalmVNCReSize"); break; + default: + snprintf(buf, len, "server2client(0x%04X)", type); + } + return buf; +} + +char *messageNameClient2Server(uint32_t type, char *buf, int len) { + if (buf==NULL) return "error"; + switch (type) { + case rfbSetPixelFormat: snprintf(buf, len, "SetPixelFormat"); break; + case rfbFixColourMapEntries: snprintf(buf, len, "FixColourMapEntries"); break; + case rfbSetEncodings: snprintf(buf, len, "SetEncodings"); break; + case rfbFramebufferUpdateRequest: snprintf(buf, len, "FramebufferUpdate"); break; + case rfbKeyEvent: snprintf(buf, len, "KeyEvent"); break; + case rfbPointerEvent: snprintf(buf, len, "PointerEvent"); break; + case rfbClientCutText: snprintf(buf, len, "ClientCutText"); break; + case rfbFileTransfer: snprintf(buf, len, "FileTransfer"); break; + case rfbSetScale: snprintf(buf, len, "SetScale"); break; + case rfbSetServerInput: snprintf(buf, len, "SetServerInput"); break; + case rfbSetSW: snprintf(buf, len, "SetSingleWindow"); break; + case rfbTextChat: snprintf(buf, len, "TextChat"); break; + case rfbKeyFrameRequest: snprintf(buf, len, "KeyFrameRequest"); break; + case rfbPalmVNCSetScaleFactor: snprintf(buf, len, "PalmVNCSetScale"); break; + default: + snprintf(buf, len, "client2server(0x%04X)", type); + + + } + return buf; +} + +char *encodingName(uint32_t type, char *buf, int len) { + if (buf==NULL) return "error"; + + switch (type) { + case rfbEncodingRaw: snprintf(buf, len, "raw"); break; + case rfbEncodingCopyRect: snprintf(buf, len, "copyRect"); break; + case rfbEncodingRRE: snprintf(buf, len, "RRE"); break; + case rfbEncodingCoRRE: snprintf(buf, len, "CoRRE"); break; + case rfbEncodingHextile: snprintf(buf, len, "hextile"); break; + case rfbEncodingZlib: snprintf(buf, len, "zlib"); break; + case rfbEncodingTight: snprintf(buf, len, "tight"); break; + case rfbEncodingZlibHex: snprintf(buf, len, "zlibhex"); break; + case rfbEncodingUltra: snprintf(buf, len, "ultra"); break; + case rfbEncodingZRLE: snprintf(buf, len, "ZRLE"); break; + case rfbEncodingCache: snprintf(buf, len, "cache"); break; + case rfbEncodingCacheEnable: snprintf(buf, len, "cacheEnable"); break; + case rfbEncodingXOR_Zlib: snprintf(buf, len, "xorZlib"); break; + case rfbEncodingXORMonoColor_Zlib: snprintf(buf, len, "xorMonoZlib"); break; + case rfbEncodingXORMultiColor_Zlib: snprintf(buf, len, "xorColorZlib"); break; + case rfbEncodingSolidColor: snprintf(buf, len, "solidColor"); break; + case rfbEncodingXOREnable: snprintf(buf, len, "xorEnable"); break; + case rfbEncodingCacheZip: snprintf(buf, len, "cacheZip"); break; + case rfbEncodingSolMonoZip: snprintf(buf, len, "monoZip"); break; + case rfbEncodingUltraZip: snprintf(buf, len, "ultraZip"); break; + + case rfbEncodingXCursor: snprintf(buf, len, "Xcursor"); break; + case rfbEncodingRichCursor: snprintf(buf, len, "RichCursor"); break; + case rfbEncodingPointerPos: snprintf(buf, len, "PointerPos"); break; + + case rfbEncodingLastRect: snprintf(buf, len, "LastRect"); break; + case rfbEncodingNewFBSize: snprintf(buf, len, "NewFBSize"); break; + case rfbEncodingKeyboardLedState: snprintf(buf, len, "LedState"); break; + case rfbEncodingSupportedMessages: snprintf(buf, len, "SupportedMessages"); break; + case rfbEncodingSupportedEncodings: snprintf(buf, len, "SupportedEncodings"); break; + case rfbEncodingServerIdentity: snprintf(buf, len, "ServerIdentity"); break; + + case rfbEncodingCompressLevel0: snprintf(buf, len, "CompressLevel0"); break; + case rfbEncodingCompressLevel1: snprintf(buf, len, "CompressLevel1"); break; + case rfbEncodingCompressLevel2: snprintf(buf, len, "CompressLevel2"); break; + case rfbEncodingCompressLevel3: snprintf(buf, len, "CompressLevel3"); break; + case rfbEncodingCompressLevel4: snprintf(buf, len, "CompressLevel4"); break; + case rfbEncodingCompressLevel5: snprintf(buf, len, "CompressLevel5"); break; + case rfbEncodingCompressLevel6: snprintf(buf, len, "CompressLevel6"); break; + case rfbEncodingCompressLevel7: snprintf(buf, len, "CompressLevel7"); break; + case rfbEncodingCompressLevel8: snprintf(buf, len, "CompressLevel8"); break; + case rfbEncodingCompressLevel9: snprintf(buf, len, "CompressLevel9"); break; + + case rfbEncodingQualityLevel0: snprintf(buf, len, "QualityLevel0"); break; + case rfbEncodingQualityLevel1: snprintf(buf, len, "QualityLevel1"); break; + case rfbEncodingQualityLevel2: snprintf(buf, len, "QualityLevel2"); break; + case rfbEncodingQualityLevel3: snprintf(buf, len, "QualityLevel3"); break; + case rfbEncodingQualityLevel4: snprintf(buf, len, "QualityLevel4"); break; + case rfbEncodingQualityLevel5: snprintf(buf, len, "QualityLevel5"); break; + case rfbEncodingQualityLevel6: snprintf(buf, len, "QualityLevel6"); break; + case rfbEncodingQualityLevel7: snprintf(buf, len, "QualityLevel7"); break; + case rfbEncodingQualityLevel8: snprintf(buf, len, "QualityLevel8"); break; + case rfbEncodingQualityLevel9: snprintf(buf, len, "QualityLevel9"); break; + + + default: + snprintf(buf, len, "encoding(0x%04X)", type); + } + + return buf; +} + + + + + +rfbStatList *rfbStatLookupEncoding(rfbClientPtr cl, uint32_t type) { - int i; - for (i = 0; i < MAX_ENCODINGS; i++) { - cl->bytesSent[i] = 0; - cl->rectanglesSent[i] = 0; + rfbStatList *ptr; + if (cl==NULL) return NULL; + for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next) + { + if (ptr->type==type) return ptr; + } + /* Well, we are here... need to *CREATE* an entry */ + ptr = (rfbStatList *)malloc(sizeof(rfbStatList)); + if (ptr!=NULL) + { + memset((char *)ptr, 0, sizeof(rfbStatList)); + ptr->type = type; + /* add to the top of the list */ + ptr->Next = cl->statEncList; + cl->statEncList = ptr; } - cl->lastRectMarkersSent = 0; - cl->lastRectBytesSent = 0; - cl->cursorShapeBytesSent = 0; - cl->cursorShapeUpdatesSent = 0; - cl->cursorPosBytesSent = 0; - cl->cursorPosUpdatesSent = 0; - cl->framebufferUpdateMessagesSent = 0; - cl->rawBytesEquivalent = 0; - cl->keyEventsRcvd = 0; - cl->pointerEventsRcvd = 0; + return ptr; } -void -rfbPrintStats(rfbClientPtr cl) + +rfbStatList *rfbStatLookupMessage(rfbClientPtr cl, uint32_t type) { - int i; - int totalRectanglesSent = 0; - int totalBytesSent = 0; + rfbStatList *ptr; + if (cl==NULL) return NULL; + for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next) + { + if (ptr->type==type) return ptr; + } + /* Well, we are here... need to *CREATE* an entry */ + ptr = (rfbStatList *)malloc(sizeof(rfbStatList)); + if (ptr!=NULL) + { + memset((char *)ptr, 0, sizeof(rfbStatList)); + ptr->type = type; + /* add to the top of the list */ + ptr->Next = cl->statMsgList; + cl->statMsgList = ptr; + } + return ptr; +} - rfbLog("Statistics:\n"); +void rfbStatRecordEncodingSentAdd(rfbClientPtr cl, uint32_t type, int byteCount) /* Specifically for tight encoding */ +{ + rfbStatList *ptr; - if ((cl->keyEventsRcvd != 0) || (cl->pointerEventsRcvd != 0)) - rfbLog(" key events received %d, pointer events %d\n", - cl->keyEventsRcvd, cl->pointerEventsRcvd); + ptr = rfbStatLookupEncoding(cl, type); + if (ptr!=NULL) + ptr->bytesSent += byteCount; +} - for (i = 0; i < MAX_ENCODINGS; i++) { - totalRectanglesSent += cl->rectanglesSent[i]; - totalBytesSent += cl->bytesSent[i]; + +void rfbStatRecordEncodingSent(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw) +{ + rfbStatList *ptr; + + ptr = rfbStatLookupEncoding(cl, type); + if (ptr!=NULL) + { + ptr->sentCount++; + ptr->bytesSent += byteCount; + ptr->bytesSentIfRaw += byteIfRaw; } +} - totalRectanglesSent += (cl->cursorShapeUpdatesSent + - cl->cursorPosUpdatesSent + - cl->lastRectMarkersSent); - totalBytesSent += (cl->cursorShapeBytesSent + - cl->cursorPosBytesSent + - cl->lastRectBytesSent); +void rfbStatRecordEncodingRcvd(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw) +{ + rfbStatList *ptr; - rfbLog(" framebuffer updates %d, rectangles %d, bytes %d\n", - cl->framebufferUpdateMessagesSent, totalRectanglesSent, - totalBytesSent); + ptr = rfbStatLookupEncoding(cl, type); + if (ptr!=NULL) + { + ptr->rcvdCount++; + ptr->bytesRcvd += byteCount; + ptr->bytesRcvdIfRaw += byteIfRaw; + } +} - if (cl->lastRectMarkersSent != 0) - rfbLog(" LastRect and NewFBSize markers %d, bytes %d\n", - cl->lastRectMarkersSent, cl->lastRectBytesSent); +void rfbStatRecordMessageSent(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw) +{ + rfbStatList *ptr; - if (cl->cursorShapeUpdatesSent != 0) - rfbLog(" cursor shape updates %d, bytes %d\n", - cl->cursorShapeUpdatesSent, cl->cursorShapeBytesSent); + ptr = rfbStatLookupMessage(cl, type); + if (ptr!=NULL) + { + ptr->sentCount++; + ptr->bytesSent += byteCount; + ptr->bytesSentIfRaw += byteIfRaw; + } +} - if (cl->cursorPosUpdatesSent != 0) - rfbLog(" cursor position updates %d, bytes %d\n", - cl->cursorPosUpdatesSent, cl->cursorPosBytesSent); +void rfbStatRecordMessageRcvd(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw) +{ + rfbStatList *ptr; - for (i = 0; i < MAX_ENCODINGS; i++) { - if (cl->rectanglesSent[i] != 0) - rfbLog(" %s rectangles %d, bytes %d\n", - encNames[i], cl->rectanglesSent[i], cl->bytesSent[i]); + ptr = rfbStatLookupMessage(cl, type); + if (ptr!=NULL) + { + ptr->rcvdCount++; + ptr->bytesRcvd += byteCount; + ptr->bytesRcvdIfRaw += byteIfRaw; } +} + + +int rfbStatGetSentBytes(rfbClientPtr cl) +{ + rfbStatList *ptr=NULL; + int bytes=0; + if (cl==NULL) return 0; + for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next) + bytes += ptr->bytesSent; + for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next) + bytes += ptr->bytesSent; + return bytes; +} + +int rfbStatGetSentBytesIfRaw(rfbClientPtr cl) +{ + rfbStatList *ptr=NULL; + int bytes=0; + if (cl==NULL) return 0; + for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next) + bytes += ptr->bytesSentIfRaw; + for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next) + bytes += ptr->bytesSentIfRaw; + return bytes; +} + +int rfbStatGetRcvdBytes(rfbClientPtr cl) +{ + rfbStatList *ptr=NULL; + int bytes=0; + if (cl==NULL) return 0; + for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next) + bytes += ptr->bytesRcvd; + for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next) + bytes += ptr->bytesRcvd; + return bytes; +} + +int rfbStatGetRcvdBytesIfRaw(rfbClientPtr cl) +{ + rfbStatList *ptr=NULL; + int bytes=0; + if (cl==NULL) return 0; + for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next) + bytes += ptr->bytesRcvdIfRaw; + for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next) + bytes += ptr->bytesRcvdIfRaw; + return bytes; +} + +int rfbStatGetMessageCountSent(rfbClientPtr cl, uint32_t type) +{ + rfbStatList *ptr=NULL; + if (cl==NULL) return 0; + for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next) + if (ptr->type==type) return ptr->sentCount; + return 0; +} +int rfbStatGetMessageCountRcvd(rfbClientPtr cl, uint32_t type) +{ + rfbStatList *ptr=NULL; + if (cl==NULL) return 0; + for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next) + if (ptr->type==type) return ptr->rcvdCount; + return 0; +} + +int rfbStatGetEncodingCountSent(rfbClientPtr cl, uint32_t type) +{ + rfbStatList *ptr=NULL; + if (cl==NULL) return 0; + for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next) + if (ptr->type==type) return ptr->sentCount; + return 0; +} +int rfbStatGetEncodingCountRcvd(rfbClientPtr cl, uint32_t type) +{ + rfbStatList *ptr=NULL; + if (cl==NULL) return 0; + for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next) + if (ptr->type==type) return ptr->rcvdCount; + return 0; +} + + - if ((totalBytesSent - cl->bytesSent[rfbEncodingCopyRect]) != 0) { - rfbLog(" raw bytes equivalent %d, compression ratio %f\n", - cl->rawBytesEquivalent, - (double)cl->rawBytesEquivalent - / (double)(totalBytesSent - - cl->bytesSent[rfbEncodingCopyRect]- - cl->cursorShapeBytesSent - - cl->cursorPosBytesSent - - cl->lastRectBytesSent)); + +void rfbResetStats(rfbClientPtr cl) +{ + rfbStatList *ptr; + if (cl==NULL) return; + while (cl->statEncList!=NULL) + { + ptr = cl->statEncList; + cl->statEncList = ptr->Next; + free(ptr); + } + while (cl->statMsgList!=NULL) + { + ptr = cl->statMsgList; + cl->statMsgList = ptr->Next; + free(ptr); } } + + +void rfbPrintStats(rfbClientPtr cl) +{ + rfbStatList *ptr=NULL; + char encBuf[64]; + double savings=0.0; + int totalRectsSent=0; + double totalBytesSent=0.0; + double totalBytesIfRawSent=0.0; + int totalRectsRcvd=0; + double totalBytesRcvd=0.0; + double totalBytesIfRawRcvd=0.0; + + char *name=NULL; + int bytes=0; + int count=0; + + if (cl==NULL) return; + + rfbLog("Statistics: Transmit\n"); + for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next) + { + name = messageNameServer2Client(ptr->type, encBuf, sizeof(encBuf)); + count = ptr->sentCount; + bytes = ptr->bytesSent; + savings = 0.0; + if (ptr->bytesSentIfRaw>0.0) + savings = 100.0 - (((double)ptr->bytesSent / (double)ptr->bytesSentIfRaw) * 100.0); + if ((bytes>0) || (count>0)) + rfbLog(" %-24.24s: %6d events %9d/%9d bytes (%5.2f%% saved)\n", + name, count, bytes, ptr->bytesSentIfRaw, savings); + totalRectsSent += count; + totalBytesSent += bytes; + totalBytesIfRawSent += ptr->bytesSentIfRaw; + } + for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next) + { + name = encodingName(ptr->type, encBuf, sizeof(encBuf)); + count = ptr->sentCount; + bytes = ptr->bytesSent; + savings = 0.0; + if (ptr->bytesSentIfRaw>0.0) + savings = 100.0 - (((double)ptr->bytesSent / (double)ptr->bytesSentIfRaw) * 100.0); + if ((bytes>0) || (count>0)) + rfbLog(" %-24.24s: %6d events %9d/%9d bytes (%5.2f%% saved)\n", + name, count, bytes, ptr->bytesSentIfRaw, savings); + totalRectsSent += count; + totalBytesSent += bytes; + totalBytesIfRawSent += ptr->bytesSentIfRaw; + } + savings = 100.0 - ((totalBytesSent/totalBytesIfRawSent)*100.0); + rfbLog(" %-24.24s: %6d events %9.0f/%9.0f bytes (%5.2f%% savings)\n", + "TOTALS", totalRectsSent, totalBytesSent, totalBytesIfRawSent, savings); + + + rfbLog("Statistics: Receive\n"); + for (ptr = cl->statMsgList; ptr!=NULL; ptr=ptr->Next) + { + name = messageNameClient2Server(ptr->type, encBuf, sizeof(encBuf)); + count = ptr->rcvdCount; + bytes = ptr->bytesRcvd; + savings = 0.0; + if (ptr->bytesSentIfRaw>0.0) + savings = 100.0 - (((double)ptr->bytesRcvd / (double)ptr->bytesRcvdIfRaw) * 100.0); + if ((bytes>0) || (count>0)) + rfbLog(" %-24.24s: %6d events %9d/%9d bytes (%5.2f%% saved)\n", + name, count, bytes, ptr->bytesRcvdIfRaw, savings); + totalRectsRcvd += count; + totalBytesRcvd += bytes; + totalBytesIfRawRcvd += ptr->bytesRcvdIfRaw; + } + for (ptr = cl->statEncList; ptr!=NULL; ptr=ptr->Next) + { + name = encodingName(ptr->type, encBuf, sizeof(encBuf)); + count = ptr->rcvdCount; + bytes = ptr->bytesRcvd; + savings = 0.0; + if (ptr->bytesSentIfRaw>0.0) + savings = 100.0 - (((double)ptr->bytesRcvd / (double)ptr->bytesRcvdIfRaw) * 100.0); + if ((bytes>0) || (count>0)) + rfbLog(" %-24.24s: %6d events %9d/%9d bytes (%5.2f%% saved)\n", + name, count, bytes, ptr->bytesRcvdIfRaw, savings); + totalRectsRcvd += count; + totalBytesRcvd += bytes; + totalBytesIfRawRcvd += ptr->bytesRcvdIfRaw; + } + savings = 100.0 - ((totalBytesRcvd/totalBytesIfRawRcvd)*100.0); + rfbLog(" %-24.24s: %6d events %9.0f/%9.0f bytes (%5.2f%% savings)\n", + "TOTALS", totalRectsRcvd, totalBytesRcvd,totalBytesIfRawRcvd, savings); + +} + diff --git a/libvncserver/tight.c b/libvncserver/tight.c index 71ce0d9..8a594c2 100644 --- a/libvncserver/tight.c +++ b/libvncserver/tight.c @@ -663,8 +663,8 @@ SendTightHeader(rfbClientPtr cl, sz_rfbFramebufferUpdateRectHeader); cl->ublen += sz_rfbFramebufferUpdateRectHeader; - cl->rectanglesSent[rfbEncodingTight]++; - cl->bytesSent[rfbEncodingTight] += sz_rfbFramebufferUpdateRectHeader; + rfbStatRecordEncodingSent(cl, rfbEncodingTight, sz_rfbFramebufferUpdateRectHeader, + sz_rfbFramebufferUpdateRectHeader + w * (cl->format.bitsPerPixel / 8) * h); return TRUE; } @@ -693,7 +693,7 @@ SendSolidRect(rfbClientPtr cl) memcpy (&cl->updateBuf[cl->ublen], tightBeforeBuf, len); cl->ublen += len; - cl->bytesSent[rfbEncodingTight] += len + 1; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, len+1); return TRUE; } @@ -736,7 +736,7 @@ SendMonoRect(rfbClientPtr cl, memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteLen); cl->ublen += paletteLen; - cl->bytesSent[rfbEncodingTight] += 3 + paletteLen; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 3 + paletteLen); break; case 16: @@ -747,7 +747,7 @@ SendMonoRect(rfbClientPtr cl, memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, 4); cl->ublen += 4; - cl->bytesSent[rfbEncodingTight] += 7; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 7); break; default: @@ -755,7 +755,7 @@ SendMonoRect(rfbClientPtr cl, cl->updateBuf[cl->ublen++] = (char)monoBackground; cl->updateBuf[cl->ublen++] = (char)monoForeground; - cl->bytesSent[rfbEncodingTight] += 5; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 5); } return CompressData(cl, streamId, dataLen, @@ -801,7 +801,7 @@ SendIndexedRect(rfbClientPtr cl, memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteNumColors * entryLen); cl->ublen += paletteNumColors * entryLen; - cl->bytesSent[rfbEncodingTight] += 3 + paletteNumColors * entryLen; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 3 + paletteNumColors * entryLen); break; case 16: @@ -814,7 +814,7 @@ SendIndexedRect(rfbClientPtr cl, memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteNumColors * 2); cl->ublen += paletteNumColors * 2; - cl->bytesSent[rfbEncodingTight] += 3 + paletteNumColors * 2; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 3 + paletteNumColors * 2); break; default: @@ -840,7 +840,7 @@ SendFullColorRect(rfbClientPtr cl, } cl->updateBuf[cl->ublen++] = 0x00; /* stream id = 0, no flushing, no filter */ - cl->bytesSent[rfbEncodingTight]++; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1); if (usePixelFormat24) { Pack24(cl, tightBeforeBuf, &cl->format, w * h); @@ -874,7 +874,7 @@ SendGradientRect(rfbClientPtr cl, cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4; cl->updateBuf[cl->ublen++] = rfbTightFilterGradient; - cl->bytesSent[rfbEncodingTight] += 2; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 2); if (usePixelFormat24) { FilterGradient24(cl, tightBeforeBuf, &cl->format, w, h); @@ -905,7 +905,7 @@ CompressData(rfbClientPtr cl, if (dataLen < TIGHT_MIN_TO_COMPRESS) { memcpy(&cl->updateBuf[cl->ublen], tightBeforeBuf, dataLen); cl->ublen += dataLen; - cl->bytesSent[rfbEncodingTight] += dataLen; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, dataLen); return TRUE; } @@ -955,15 +955,15 @@ static rfbBool SendCompressedData(rfbClientPtr cl, int i, portionLen; cl->updateBuf[cl->ublen++] = compressedLen & 0x7F; - cl->bytesSent[rfbEncodingTight]++; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1); if (compressedLen > 0x7F) { cl->updateBuf[cl->ublen-1] |= 0x80; cl->updateBuf[cl->ublen++] = compressedLen >> 7 & 0x7F; - cl->bytesSent[rfbEncodingTight]++; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1); if (compressedLen > 0x3FFF) { cl->updateBuf[cl->ublen-1] |= 0x80; cl->updateBuf[cl->ublen++] = compressedLen >> 14 & 0xFF; - cl->bytesSent[rfbEncodingTight]++; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1); } } @@ -979,7 +979,7 @@ static rfbBool SendCompressedData(rfbClientPtr cl, memcpy(&cl->updateBuf[cl->ublen], &tightAfterBuf[i], portionLen); cl->ublen += portionLen; } - cl->bytesSent[rfbEncodingTight] += compressedLen; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, compressedLen); return TRUE; } @@ -1686,7 +1686,7 @@ SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h, int quality) } cl->updateBuf[cl->ublen++] = (char)(rfbTightJpeg << 4); - cl->bytesSent[rfbEncodingTight]++; + rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1); return SendCompressedData(cl, jpegDstDataLen); } diff --git a/libvncserver/tightvnc-filetransfer/rfbtightproto.h b/libvncserver/tightvnc-filetransfer/rfbtightproto.h index 397daba..ef683ae 100644 --- a/libvncserver/tightvnc-filetransfer/rfbtightproto.h +++ b/libvncserver/tightvnc-filetransfer/rfbtightproto.h @@ -28,6 +28,12 @@ #include <rfb/rfb.h> #include <limits.h> +/* PATH_MAX is not defined in limits.h on some platforms */ +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + + #define rfbSecTypeTight 16 void rfbTightUsage(void); diff --git a/libvncserver/ultra.c b/libvncserver/ultra.c index e1821bb..1e51446 100644 --- a/libvncserver/ultra.c +++ b/libvncserver/ultra.c @@ -101,9 +101,7 @@ rfbSendOneRectEncodingUltra(rfbClientPtr cl, } /* Update statics */ - cl->rectanglesSent[rfbEncodingUltra]++; - cl->bytesSent[rfbEncodingUltra] += (sz_rfbFramebufferUpdateRectHeader - + sz_rfbZlibHeader + lzoAfterBufLen); + rfbStatRecordEncodingSent(cl, rfbEncodingUltra, sz_rfbFramebufferUpdateRectHeader + sz_rfbZlibHeader + lzoAfterBufLen, maxRawSize); if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbZlibHeader > UPDATE_BUF_SIZE) diff --git a/libvncserver/zlib.c b/libvncserver/zlib.c index 321d86f..56bcc6e 100644 --- a/libvncserver/zlib.c +++ b/libvncserver/zlib.c @@ -121,6 +121,7 @@ rfbSendOneRectEncodingZlib(rfbClientPtr cl, zlibAfterBuf = (char *)realloc(zlibAfterBuf, zlibAfterBufSize); } + /* * Convert pixel data to client format. */ @@ -176,9 +177,8 @@ rfbSendOneRectEncodingZlib(rfbClientPtr cl, */ /* Update statics */ - cl->rectanglesSent[rfbEncodingZlib]++; - cl->bytesSent[rfbEncodingZlib] += (sz_rfbFramebufferUpdateRectHeader - + sz_rfbZlibHeader + zlibAfterBufLen); + rfbStatRecordEncodingSent(cl, rfbEncodingZlib, sz_rfbFramebufferUpdateRectHeader + sz_rfbZlibHeader + zlibAfterBufLen, + + w * (cl->format.bitsPerPixel / 8) * h); if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbZlibHeader > UPDATE_BUF_SIZE) diff --git a/libvncserver/zrle.c b/libvncserver/zrle.c index 76123a0..d72993e 100644 --- a/libvncserver/zrle.c +++ b/libvncserver/zrle.c @@ -124,9 +124,8 @@ rfbBool rfbSendRectEncodingZRLE(rfbClientPtr cl, int x, int y, int w, int h) break; } - cl->rectanglesSent[rfbEncodingZRLE]++; - cl->bytesSent[rfbEncodingZRLE] += (sz_rfbFramebufferUpdateRectHeader - + sz_rfbZRLEHeader + ZRLE_BUFFER_LENGTH(&zos->out)); + rfbStatRecordEncodingSent(cl, rfbEncodingZRLE, sz_rfbFramebufferUpdateRectHeader + sz_rfbZRLEHeader + ZRLE_BUFFER_LENGTH(&zos->out), + + w * (cl->format.bitsPerPixel / 8) * h); if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbZRLEHeader > UPDATE_BUF_SIZE) @@ -136,8 +136,22 @@ typedef rfbBool (*rfbPasswordCheckProcPtr)(struct _rfbClientRec* cl,const char* typedef enum rfbNewClientAction (*rfbNewClientHookPtr)(struct _rfbClientRec* cl); typedef void (*rfbDisplayHookPtr)(struct _rfbClientRec* cl); /* support the capability to view the caps/num/scroll states of the X server */ -typedef int (*rfbGetKeyboardLedStateHookPtr)(struct _rfbScreenInfo* screen); - +typedef int (*rfbGetKeyboardLedStateHookPtr)(struct _rfbScreenInfo* screen); +/* If x==1 and y==1 then set the whole display + * else find the window underneath x and y and set the framebuffer to the dimensions + * of that window + */ +typedef void (*rfbSetSingleWindowProcPtr) (struct _rfbClientRec* cl, int x, int y); +/* Status determines if the X11 server permits input from the local user + * status==0 or 1 + */ +typedef void (*rfbSetServerInputProcPtr) (struct _rfbClientRec* cl, int status); +/* Permit the server to allow or deny filetransfers. This is defaulted to deny + * It is called when a client initiates a connection to determine if it is permitted. + */ +typedef int (*rfbFileTransferPermitted) (struct _rfbClientRec* cl); +/* Handle the textchat messages */ +typedef void (*rfbSetTextChat) (struct _rfbClientRec* cl, int length, char *string); typedef struct { uint32_t count; @@ -296,7 +310,11 @@ typedef struct _rfbScreenInfo rfbSetXCutTextProcPtr setXCutText; rfbGetCursorProcPtr getCursorPtr; rfbSetTranslateFunctionProcPtr setTranslateFunction; - + rfbSetSingleWindowProcPtr setSingleWindow; + rfbSetServerInputProcPtr setServerInput; + rfbFileTransferPermitted getFileTransferPermission; + rfbSetTextChat setTextChat; + /* newClientHook is called just after a new client is created */ rfbNewClientHookPtr newClientHook; /* displayHook is called just before a frame buffer update */ @@ -326,6 +344,10 @@ typedef struct _rfbScreenInfo /* rfbEncodingServerIdentity */ char *versionString; + + /* What does the server tell the new clients which version it supports */ + int protocolMajorVersion; + int protocolMinorVersion; } rfbScreenInfo, *rfbScreenInfoPtr; @@ -351,6 +373,27 @@ typedef struct sraRegion* sraRegionPtr; typedef void (*ClientGoneHookPtr)(struct _rfbClientRec* cl); +typedef struct _rfbFileTransferData { + int fd; + int compressionEnabled; + int fileSize; + int numPackets; + int receiving; + int sending; +} rfbFileTransferData; + + +typedef struct _rfbStatList { + uint32_t type; + uint32_t sentCount; + uint32_t bytesSent; + uint32_t bytesSentIfRaw; + uint32_t rcvdCount; + uint32_t bytesRcvd; + uint32_t bytesRcvdIfRaw; + struct _rfbStatList *Next; +} rfbStatList; + typedef struct _rfbClientRec { /* back pointer to the screen */ @@ -467,20 +510,11 @@ typedef struct _rfbClientRec { int ublen; /* statistics */ - - int bytesSent[MAX_ENCODINGS]; - int rectanglesSent[MAX_ENCODINGS]; - int lastRectMarkersSent; - int lastRectBytesSent; - int cursorShapeBytesSent; - int cursorShapeUpdatesSent; - int cursorPosBytesSent; - int cursorPosUpdatesSent; - int framebufferUpdateMessagesSent; + struct _rfbStatList *statEncList; + struct _rfbStatList *statMsgList; int rawBytesEquivalent; - int keyEventsRcvd; - int pointerEventsRcvd; - + int bytesSent; + #ifdef LIBVNCSERVER_HAVE_LIBZ /* zlib encoding -- necessary compression state info per client */ @@ -502,6 +536,7 @@ typedef struct _rfbClientRec { rfbBool compStreamInitedLZO; char *lzoWrkMem; + rfbFileTransferData fileTransfer; int lastKeyboardLedState; /* keep track of last value so we can send *change* events */ rfbBool enableSupportedMessages; /* client supports SupportedMessages encoding */ @@ -584,6 +619,11 @@ extern char rfbEndianTest; #define Swap24IfLE(l) (rfbEndianTest ? Swap24(l) : (l)) #define Swap32IfLE(l) (rfbEndianTest ? Swap32(l) : (l)) +/* UltraVNC uses some windows structures unmodified, so the viewer expects LittleEndian Data */ +#define Swap16IfBE(s) (rfbEndianTest ? (s) : Swap16(s)) +#define Swap24IfBE(l) (rfbEndianTest ? (l) : Swap24(l)) +#define Swap32IfBE(l) (rfbEndianTest ? (l) : Swap32(l)) + /* sockets.c */ extern int rfbMaxClientWait; @@ -634,6 +674,13 @@ extern rfbBool rfbSendNewFBSize(rfbClientPtr cl, int w, int h); extern rfbBool rfbSendSetColourMapEntries(rfbClientPtr cl, int firstColour, int nColours); extern void rfbSendBell(rfbScreenInfoPtr rfbScreen); +extern char *rfbProcessFileTransferReadBuffer(rfbClientPtr cl, uint32_t length); +extern rfbBool rfbSendFileTransferChunk(rfbClientPtr cl); +extern rfbBool rfbSendDirContent(rfbClientPtr cl, int length, char *buffer); +extern rfbBool rfbSendFileTransferMessage(rfbClientPtr cl, uint8_t contentType, uint8_t contentParam, uint32_t size, uint32_t length, char *buffer); +extern char *rfbProcessFileTransferReadBuffer(rfbClientPtr cl, uint32_t length); +extern rfbBool rfbProcessFileTransfer(rfbClientPtr cl, uint8_t contentType, uint8_t contentParam, uint32_t size, uint32_t length); + void rfbGotXCutText(rfbScreenInfoPtr rfbScreen, char *str, int len); /* translate.c */ @@ -880,6 +927,38 @@ extern rfbBool rfbIsActive(rfbScreenInfoPtr screenInfo); void rfbRegisterTightVNCFileTransferExtension(); void rfbUnregisterTightVNCFileTransferExtension(); +/* Statistics */ +extern char *messageNameServer2Client(uint32_t type, char *buf, int len); +extern char *messageNameClient2Server(uint32_t type, char *buf, int len); +extern char *encodingName(uint32_t enc, char *buf, int len); + +extern rfbStatList *rfbStatLookupEncoding(rfbClientPtr cl, uint32_t type); +extern rfbStatList *rfbStatLookupMessage(rfbClientPtr cl, uint32_t type); + +/* Each call to rfbStatRecord* adds one to the rect count for that type */ +extern void rfbStatRecordEncodingSent(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw); +extern void rfbStatRecordEncodingSentAdd(rfbClientPtr cl, uint32_t type, int byteCount); /* Specifically for tight encoding */ +extern void rfbStatRecordEncodingRcvd(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw); +extern void rfbStatRecordMessageSent(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw); +extern void rfbStatRecordMessageRcvd(rfbClientPtr cl, uint32_t type, int byteCount, int byteIfRaw); +extern void rfbResetStats(rfbClientPtr cl); +extern void rfbPrintStats(rfbClientPtr cl); + +extern int rfbStatGetSentBytes(rfbClientPtr cl); +extern int rfbStatGetSentBytesIfRaw(rfbClientPtr cl); +extern int rfbStatGetRcvdBytes(rfbClientPtr cl); +extern int rfbStatGetRcvdBytesIfRaw(rfbClientPtr cl); +extern int rfbStatGetMessageCountSent(rfbClientPtr cl, uint32_t type); +extern int rfbStatGetMessageCountRcvd(rfbClientPtr cl, uint32_t type); +extern int rfbStatGetEncodingCountSent(rfbClientPtr cl, uint32_t type); +extern int rfbStatGetEncodingCountRcvd(rfbClientPtr cl, uint32_t type); + +/* Set which version you want to advertise 3.3, 3.6, 3.7 and 3.8 are currently supported*/ +extern void rfbSetProtocolVersion(rfbScreenInfoPtr rfbScreen, int major_, int minor_); + + + + #endif #if(defined __cplusplus) diff --git a/rfb/rfbclient.h b/rfb/rfbclient.h index 3454af4..d8e3fdc 100644 --- a/rfb/rfbclient.h +++ b/rfb/rfbclient.h @@ -96,6 +96,7 @@ typedef struct { struct _rfbClient; +typedef void (*HandleTextChatProc)(struct _rfbClient* client, int value, char *text); typedef void (*HandleKeyboardLedStateProc)(struct _rfbClient* client, int value, int pad); typedef rfbBool (*HandleCursorPosProc)(struct _rfbClient* client, int x, int y); typedef void (*SoftCursorLockAreaProc)(struct _rfbClient* client, int x, int y, int w, int h); @@ -210,6 +211,7 @@ typedef struct _rfbClient { int canHandleNewFBSize; /* hooks */ + HandleTextChatProc HandleTextChat; HandleKeyboardLedStateProc HandleKeyboardLedState; HandleCursorPosProc HandleCursorPos; SoftCursorLockAreaProc SoftCursorLockArea; @@ -219,6 +221,19 @@ typedef struct _rfbClient { GetPasswordProc GetPassword; MallocFrameBufferProc MallocFrameBuffer; BellProc Bell; + + /* Which messages are supported by the server + * This is a *guess* for most servers. + * (If we can even detect the type of server) + * + * If the server supports the "rfbEncodingSupportedMessages" + * then this will be updated when the encoding is received to + * accurately reflect the servers capabilities. + */ + rfbSupportedMessages supportedMessages; + + /* negotiated protocol version */ + int major, minor; } rfbClient; /* cursor.c */ @@ -247,6 +262,12 @@ extern rfbBool SendKeyEvent(rfbClient* client,uint32_t key, rfbBool down); extern rfbBool SendClientCutText(rfbClient* client,char *str, int len); extern rfbBool HandleRFBServerMessage(rfbClient* client); +extern rfbBool TextChatSend(rfbClient* client, char *text); +extern rfbBool TextChatOpen(rfbClient* client); +extern rfbBool TextChatClose(rfbClient* client); +extern rfbBool TextChatFinish(rfbClient* client); +extern rfbBool PermitServerInput(rfbClient* client, int enabled); + extern void PrintPixelFormat(rfbPixelFormat *format); /* client data */ diff --git a/rfb/rfbproto.h b/rfb/rfbproto.h index c642f61..672d0d7 100644 --- a/rfb/rfbproto.h +++ b/rfb/rfbproto.h @@ -218,8 +218,10 @@ typedef struct { #define rfbProtocolVersionFormat "RFB %03d.%03d\n" #define rfbProtocolMajorVersion 3 -#define rfbProtocolMinorVersion 7 -#define rfbProtocolFallbackMinorVersion 3 +#define rfbProtocolMinorVersion 6 +/* UltraVNC Viewer examines rfbProtocolMinorVersion number (4, and 6) + * to identify if the server supports File Transfer + */ typedef char rfbProtocolVersionMsg[13]; /* allow extra byte for null */ @@ -397,24 +399,18 @@ typedef struct { #define rfbEncodingRRE 2 #define rfbEncodingCoRRE 4 #define rfbEncodingHextile 5 -#ifdef LIBVNCSERVER_HAVE_LIBZ #define rfbEncodingZlib 6 #define rfbEncodingTight 7 #define rfbEncodingZlibHex 8 -#endif #define rfbEncodingUltra 9 -#ifdef LIBVNCSERVER_HAVE_LIBZ #define rfbEncodingZRLE 16 -#endif /* Cache & XOR-Zlib - rdv@2002 */ #define rfbEncodingCache 0xFFFF0000 #define rfbEncodingCacheEnable 0xFFFF0001 -#ifdef LIBVNCSERVER_HAVE_LIBZ #define rfbEncodingXOR_Zlib 0xFFFF0002 #define rfbEncodingXORMonoColor_Zlib 0xFFFF0003 #define rfbEncodingXORMultiColor_Zlib 0xFFFF0004 -#endif #define rfbEncodingSolidColor 0xFFFF0005 #define rfbEncodingXOREnable 0xFFFF0006 #define rfbEncodingCacheZip 0xFFFF0007 @@ -649,11 +645,11 @@ typedef struct { #define rfbHextileExtractW(byte) (((byte) >> 4) + 1) #define rfbHextileExtractH(byte) (((byte) & 0xf) + 1) -#ifdef LIBVNCSERVER_HAVE_LIBZ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * zlib - zlib compressed Encoding. We have an rfbZlibHeader structure * giving the number of bytes following. Finally the data follows is * zlib compressed version of the raw pixel data as negotiated. + * (NOTE: also used by Ultra Encoding) */ typedef struct { @@ -662,6 +658,7 @@ typedef struct { #define sz_rfbZlibHeader 4 +#ifdef LIBVNCSERVER_HAVE_LIBZ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * Tight Encoding. @@ -935,9 +932,10 @@ typedef struct { typedef struct _rfbFileTransferMsg { uint8_t type; /* always rfbFileTransfer */ uint8_t contentType; /* See defines below */ - uint16_t contentParam;/* Other possible content classification (Dir or File name, etc..) */ - uint32_t size; /* FileSize or packet index or error or other */ - /* uint32_t sizeH; Additional 32Bits params to handle big values. Only for V2 (we want backward compatibility between all V1 versions) */ + uint8_t contentParam;/* Other possible content classification (Dir or File name, etc..) */ + uint8_t pad; /* It appears that UltraVNC *forgot* to Swap16IfLE(contentParam) */ + uint32_t size; /* FileSize or packet index or error or other */ +/* uint32_t sizeH; Additional 32Bits params to handle big values. Only for V2 (we want backward compatibility between all V1 versions) */ uint32_t length; /* followed by data char text[length] */ } rfbFileTransferMsg; diff --git a/x11vnc/rates.c b/x11vnc/rates.c index 811a7ae..d6f1c5c 100644 --- a/x11vnc/rates.c +++ b/x11vnc/rates.c @@ -350,11 +350,8 @@ db = 0; } nclients++; - cbs = 0; - for (i=0; i<MAX_ENCODINGS; i++) { - cbs += cl->bytesSent[i]; - } - rbs = cl->rawBytesEquivalent; + cbs = rfbStatGetSentBytes(cl); + rbs = rfbStatGetSentBytesIfRaw(cl); if (init) { @@ -435,7 +432,7 @@ if (db) fprintf(stderr, "%d client num rects req: %d mod: %d cbs: %d " if (db) fprintf(stderr, "dt2 calc: num rects req: %d/%d mod: %d/%d " "fbu-sent: %d dt: %.4f dt2: %.4f tm: %.4f\n", - req0, req1, mod0, mod1, cl->framebufferUpdateMessagesSent, dt, dt2, tm); + req0, req1, mod0, mod1, rfbStatGetMessageCountSent(cl, rfbFramebufferUpdate), dt, dt2, tm); if (req1 != 0 && mod1 == 0) { got_t2 = 1; break; @@ -502,7 +499,7 @@ if (db) fprintf(stderr, "dt2 calc: num rects req: %d/%d mod: %d/%d " if (db) fprintf(stderr, "dt3 calc: num rects req: %d/%d mod: %d/%d " "fbu-sent: %d dt: %.4f dt3: %.4f tm: %.4f\n", - req0, req1, mod0, mod1, cl->framebufferUpdateMessagesSent, dt, dt3, tm); + req0, req1, mod0, mod1, rfbStatGetMessageCountSent(cl, rfbFramebufferUpdate), dt, dt3, tm); if (req1 != 0 && mod1 == 0) { dts[got_t3++] = dt3; diff --git a/x11vnc/userinput.c b/x11vnc/userinput.c index ef1563f..7646d08 100644 --- a/x11vnc/userinput.c +++ b/x11vnc/userinput.c @@ -4082,7 +4082,7 @@ int fb_update_sent(int *count) { i = rfbGetClientIterator(screen); while( (cl = rfbClientIteratorNext(i)) ) { - sent += cl->framebufferUpdateMessagesSent; + sent += rfbStatGetMessageCountSent(cl, rfbFramebufferUpdate); } rfbReleaseClientIterator(i); if (sent != last_count) { |