diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c index 098ae38..7d881c4 100644 --- a/libvncclient/rfbproto.c +++ b/libvncclient/rfbproto.c @@ -1230,6 +1230,9 @@ SetFormatAndEncodings(rfbClient* client) if (se->nEncodings < MAX_ENCODINGS) encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingServerIdentity); + /* xvp */ + if (se->nEncodings < MAX_ENCODINGS) + encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingXvp); /* client extensions */ for(e = rfbClientExtensions; e; e = e->next) @@ -1394,6 +1397,37 @@ rfbBool PermitServerInput(rfbClient* client, int enabled) } +/* + * send xvp client message + * A client supporting the xvp extension sends this to request that the server initiate + * a clean shutdown, clean reboot or abrupt reset of the system whose framebuffer the + * client is displaying. + * + * only version 1 is defined in the protocol specs + * + * possible values for code are: + * rfbXvp_Shutdown + * rfbXvp_Reboot + * rfbXvp_Reset + */ + +rfbBool SendXvpMsg(rfbClient* client, uint8_t version, uint8_t code) +{ + rfbXvpMsg xvp; + + if (!SupportsClient2Server(client, rfbXvp)) return TRUE; + xvp.type = rfbXvp; + xvp.pad = 0; + xvp.version = version; + xvp.code = code; + + if (!WriteToRFBServer(client, (char *)&xvp, sz_rfbXvpMsg)) + return FALSE; + + return TRUE; +} + + /* * SendPointerEvent. */ @@ -1984,6 +2018,24 @@ HandleRFBServerMessage(rfbClient* client) break; } + case rfbXvp: + { + if (!ReadFromRFBServer(client, ((char *)&msg) + 1, + sz_rfbXvpMsg -1)) + return FALSE; + + SetClient2Server(client, rfbXvp); + /* technically, we only care what we can *send* to the server + * but, we set Server2Client Just in case it ever becomes useful + */ + SetServer2Client(client, rfbXvp); + + if(client->HandleXvpMsg) + client->HandleXvpMsg(client, msg.xvp.version, msg.xvp.code); + + break; + } + case rfbResizeFrameBuffer: { if (!ReadFromRFBServer(client, ((char *)&msg) + 1, diff --git a/libvncserver/main.c b/libvncserver/main.c index 28bbc42..5332e2d 100644 --- a/libvncserver/main.c +++ b/libvncserver/main.c @@ -901,6 +901,7 @@ rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv, screen->displayHook = NULL; screen->displayFinishedHook = NULL; screen->getKeyboardLedStateHook = NULL; + screen->xvpHook = NULL; /* initialize client list and iterator mutex */ rfbClientListInit(screen); diff --git a/libvncserver/rfbserver.c b/libvncserver/rfbserver.c index b540f04..b71daf8 100644 --- a/libvncserver/rfbserver.c +++ b/libvncserver/rfbserver.c @@ -874,6 +874,7 @@ rfbSendSupportedMessages(rfbClientPtr cl) /*rfbSetBit(msgs.client2server, rfbTextChat); */ /*rfbSetBit(msgs.client2server, rfbKeyFrameRequest); */ rfbSetBit(msgs.client2server, rfbPalmVNCSetScaleFactor); + rfbSetBit(msgs.client2server, rfbXvp); rfbSetBit(msgs.server2client, rfbFramebufferUpdate); rfbSetBit(msgs.server2client, rfbSetColourMapEntries); @@ -882,6 +883,7 @@ rfbSendSupportedMessages(rfbClientPtr cl) rfbSetBit(msgs.server2client, rfbResizeFrameBuffer); /*rfbSetBit(msgs.server2client, rfbKeyFrameUpdate); */ rfbSetBit(msgs.server2client, rfbPalmVNCReSizeFrameBuffer); + rfbSetBit(msgs.server2client, rfbXvp); memcpy(&cl->updateBuf[cl->ublen], (char *)&msgs, sz_rfbSupportedMessages); cl->ublen += sz_rfbSupportedMessages; @@ -1027,6 +1029,33 @@ rfbSendServerIdentity(rfbClientPtr cl) return TRUE; } +/* + * Send an xvp server message + */ + +rfbBool +rfbSendXvp(rfbClientPtr cl, uint8_t version, uint8_t code) +{ + rfbXvpMsg xvp; + + xvp.type = rfbXvp; + xvp.pad = 0; + xvp.version = version; + xvp.code = code; + + LOCK(cl->sendMutex); + if (rfbWriteExact(cl, (char *)&xvp, sz_rfbXvpMsg) < 0) { + rfbLogPerror("rfbSendXvp: write"); + rfbCloseClient(cl); + } + UNLOCK(cl->sendMutex); + + rfbStatRecordMessageSent(cl, rfbXvp, sz_rfbXvpMsg, sz_rfbXvpMsg); + + return TRUE; +} + + rfbBool rfbSendTextChatMessage(rfbClientPtr cl, uint32_t length, char *buffer) { rfbTextChatMsg tc; @@ -1984,7 +2013,15 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) "%s\n", cl->host); cl->enableServerIdentity = TRUE; } - break; + break; + case rfbEncodingXvp: + rfbLog("Enabling Xvp protocol extension for client " + "%s\n", cl->host); + if (!rfbSendXvp(cl, 1, rfbXvp_Init)) { + rfbCloseClient(cl); + return; + } + break; default: #ifdef LIBVNCSERVER_HAVE_LIBZ if ( enc >= (uint32_t)rfbEncodingCompressLevel0 && @@ -2368,6 +2405,28 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) rfbSendNewScaleSize(cl); return; + case rfbXvp: + + if ((n = rfbReadExact(cl, ((char *)&msg) + 1, + sz_rfbXvpMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseClient(cl); + return; + } + rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbXvpMsg, sz_rfbXvpMsg); + + /* only version when is defined, so echo back a fail */ + if(msg.xvp.version != 1) { + rfbSendXvp(cl, msg.xvp.version, rfbXvp_Fail); + } + else { + /* if the hook exists and fails, send a fail msg */ + if(cl->screen->xvpHook && !cl->screen->xvpHook(cl, msg.xvp.version, msg.xvp.code)) + rfbSendXvp(cl, 1, rfbXvp_Fail); + } + return; + default: { rfbExtensionData *e,*next; @@ -2756,7 +2815,7 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, if (!rfbSendServerIdentity(cl)) goto updateFailed; } - + if (!sraRgnEmpty(updateCopyRegion)) { if (!rfbSendCopyRegion(cl,updateCopyRegion,dx,dy)) goto updateFailed; diff --git a/libvncserver/stats.c b/libvncserver/stats.c index d5d6925..1e6c9b1 100755 --- a/libvncserver/stats.c +++ b/libvncserver/stats.c @@ -55,6 +55,7 @@ char *messageNameServer2Client(uint32_t type, char *buf, int len) { case rfbFileTransfer: snprintf(buf, len, "FileTransfer"); break; case rfbTextChat: snprintf(buf, len, "TextChat"); break; case rfbPalmVNCReSizeFrameBuffer: snprintf(buf, len, "PalmVNCReSize"); break; + case rfbXvp: snprintf(buf, len, "XvpServerMessage"); break; default: snprintf(buf, len, "svr2cli-0x%08X", 0xFF); } @@ -78,6 +79,7 @@ char *messageNameClient2Server(uint32_t type, char *buf, int len) { case rfbTextChat: snprintf(buf, len, "TextChat"); break; case rfbKeyFrameRequest: snprintf(buf, len, "KeyFrameRequest"); break; case rfbPalmVNCSetScaleFactor: snprintf(buf, len, "PalmVNCSetScale"); break; + case rfbXvp: snprintf(buf, len, "XvpClientMessage"); break; default: snprintf(buf, len, "cli2svr-0x%08X", type); diff --git a/rfb/rfb.h b/rfb/rfb.h index e58e59e..1ea9571 100644 --- a/rfb/rfb.h +++ b/rfb/rfb.h @@ -139,6 +139,7 @@ typedef void (*rfbDisplayHookPtr)(struct _rfbClientRec* cl); typedef void (*rfbDisplayFinishedHookPtr)(struct _rfbClientRec* cl, int result); /* support the capability to view the caps/num/scroll states of the X server */ typedef int (*rfbGetKeyboardLedStateHookPtr)(struct _rfbScreenInfo* screen); +typedef rfbBool (*rfbXvpHookPtr)(struct _rfbClientRec* cl, uint8_t, uint8_t); /* 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 @@ -356,6 +357,8 @@ typedef struct _rfbScreenInfo /* displayFinishedHook is called just after a frame buffer update */ rfbDisplayFinishedHookPtr displayFinishedHook; + /* xvpHook is called to handle an xvp client message */ + rfbXvpHookPtr xvpHook; } rfbScreenInfo, *rfbScreenInfoPtr; diff --git a/rfb/rfbclient.h b/rfb/rfbclient.h index 34c8737..1edd32e 100644 --- a/rfb/rfbclient.h +++ b/rfb/rfbclient.h @@ -136,6 +136,7 @@ typedef union _rfbCredential struct _rfbClient; typedef void (*HandleTextChatProc)(struct _rfbClient* client, int value, char *text); +typedef void (*HandleXvpMsgProc)(struct _rfbClient* client, uint8_t version, uint8_t opcode); 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); @@ -316,6 +317,9 @@ typedef struct _rfbClient { /* the QoS IP DSCP for this client */ int QoS_DSCP; + + /* hook to handle xvp server messages */ + HandleXvpMsgProc HandleXvpMsg; } rfbClient; /* cursor.c */ @@ -352,6 +356,7 @@ extern rfbBool TextChatOpen(rfbClient* client); extern rfbBool TextChatClose(rfbClient* client); extern rfbBool TextChatFinish(rfbClient* client); extern rfbBool PermitServerInput(rfbClient* client, int enabled); +extern rfbBool SendXvpMsg(rfbClient* client, uint8_t version, uint8_t code); extern void PrintPixelFormat(rfbPixelFormat *format); diff --git a/rfb/rfbproto.h b/rfb/rfbproto.h index c20f95c..cb62fc5 100644 --- a/rfb/rfbproto.h +++ b/rfb/rfbproto.h @@ -106,7 +106,7 @@ typedef uint32_t in_addr_t; #define INADDR_NONE ((in_addr_t) 0xffffffff) #endif -#define MAX_ENCODINGS 20 +#define MAX_ENCODINGS 21 /***************************************************************************** * @@ -405,6 +405,8 @@ typedef struct { #define rfbKeyFrameRequest 12 /* PalmVNC 1.4 & 2.0 SetScale Factor message */ #define rfbPalmVNCSetScaleFactor 0xF +/* Xvp message - bidirectional */ +#define rfbXvp 250 @@ -439,6 +441,9 @@ typedef struct { #define rfbEncodingSolMonoZip 0xFFFF0008 #define rfbEncodingUltraZip 0xFFFF0009 +/* Xvp pseudo-encoding */ +#define rfbEncodingXvp 0xFFFFFECB + /* * Special encoding numbers: * 0xFFFFFF00 .. 0xFFFFFF0F -- encoding-specific compression levels; @@ -1061,6 +1066,44 @@ typedef struct _rfbTextChatMsg { #define rfbTextChatFinished 0xFFFFFFFD +/*----------------------------------------------------------------------------- + * Xvp Message + * Bidirectional message + * A server which supports the xvp extension declares this by sending a message + * with an Xvp_INIT xvp-message-code when it receives a request from the client + * to use the xvp Pseudo-encoding. The server must specify in this message the + * highest xvp-extension-version it supports: the client may assume that the + * server supports all versions from 1 up to this value. The client is then + * free to use any supported version. Currently, only version 1 is defined. + * + * A server which subsequently receives an xvp Client Message requesting an + * operation which it is unable to perform, informs the client of this by + * sending a message with an Xvp_FAIL xvp-message-code, and the same + * xvp-extension-version as included in the client's operation request. + * + * A client supporting the xvp extension sends this to request that the server + * initiate a clean shutdown, clean reboot or abrupt reset of the system whose + * framebuffer the client is displaying. + */ + + +typedef struct { + uint8_t type; /* always rfbXvp */ + uint8_t pad; + uint8_t version; /* xvp extension version */ + uint8_t code; /* xvp message code */ +} rfbXvpMsg; + +#define sz_rfbXvpMsg (4) + +/* server message codes */ +#define rfbXvp_Fail 0 +#define rfbXvp_Init 1 +/* client message codes */ +#define rfbXvp_Shutdown 2 +#define rfbXvp_Reboot 3 +#define rfbXvp_Reset 4 + /*----------------------------------------------------------------------------- * Modif sf@2002 @@ -1115,6 +1158,7 @@ typedef union { rfbPalmVNCReSizeFrameBufferMsg prsfb; rfbFileTransferMsg ft; rfbTextChatMsg tc; + rfbXvpMsg xvp; } rfbServerToClientMsg; @@ -1350,6 +1394,7 @@ typedef struct _rfbSetSWMsg { #define sz_rfbSetSWMsg 6 + /*----------------------------------------------------------------------------- * Union of all client->server messages. */ @@ -1369,6 +1414,7 @@ typedef union { rfbFileTransferMsg ft; rfbSetSWMsg sw; rfbTextChatMsg tc; + rfbXvpMsg xvp; } rfbClientToServerMsg; /*