diff options
author | steven_carr <steven_carr> | 2006-05-15 05:37:39 +0000 |
---|---|---|
committer | steven_carr <steven_carr> | 2006-05-15 05:37:39 +0000 |
commit | ccdbe8f3256c3c776a1cc1a0517a38437b9e2c65 (patch) | |
tree | 9853559d1c965946b196cccd03ce0f466d524950 /libvncclient/rfbproto.c | |
parent | 347c4a98475d1dba8030efe33aa0b35856c4d17f (diff) | |
download | libtdevnc-ccdbe8f3256c3c776a1cc1a0517a38437b9e2c65.tar.gz libtdevnc-ccdbe8f3256c3c776a1cc1a0517a38437b9e2c65.zip |
The great UltraVNC Compatibility Commit
Diffstat (limited to 'libvncclient/rfbproto.c')
-rw-r--r-- | libvncclient/rfbproto.c | 366 |
1 files changed, 336 insertions, 30 deletions
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, |