diff options
Diffstat (limited to 'nxcomp/src/ServerChannel.cpp')
-rw-r--r-- | nxcomp/src/ServerChannel.cpp | 7946 |
1 files changed, 7946 insertions, 0 deletions
diff --git a/nxcomp/src/ServerChannel.cpp b/nxcomp/src/ServerChannel.cpp new file mode 100644 index 000000000..a560eca6d --- /dev/null +++ b/nxcomp/src/ServerChannel.cpp @@ -0,0 +1,7946 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <sys/types.h> +#include <sys/ipc.h> +#ifndef ANDROID +#include <sys/shm.h> +#endif + +#include <X11/X.h> +#include <X11/Xatom.h> + +#include "NXproto.h" +#include "NXalert.h" +#include "NXpack.h" +#include "NXmitshm.h" + +#include "ServerChannel.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +#include "StaticCompressor.h" + +#include "Statistics.h" +#include "Proxy.h" + +#include "Auth.h" +#include "Unpack.h" + +// +// Available unpack methods. +// + +#include "Alpha.h" +#include "Colormap.h" +#include "Bitmap.h" +#include "Jpeg.h" +#include "Pgn.h" +#include "Rgb.h" +#include "Rle.h" + +extern Proxy *proxy; + +// +// Set the verbosity level. You also +// need to define OPCODES in Misc.cpp +// if you want literals instead of +// opcodes' numbers. +// + +#define PANIC +#define WARNING +#undef OPCODES +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Log the important tracepoints related +// to writing packets to the peer proxy. +// + +#undef FLUSH + +// +// Log the operations related to splits. +// + +#undef SPLIT + +// +// Define this to log when a channel +// is created or destroyed. +// + +#undef REFERENCES + +// +// Define this to exit and suspend the +// session after a given number of X +// messages decoded by the proxy. +// + +#undef SUSPEND + +// +// Define these to hide the server extensions. +// + +#define HIDE_MIT_SHM_EXTENSION +#define HIDE_BIG_REQUESTS_EXTENSION +#define HIDE_XFree86_Bigfont_EXTENSION +#undef HIDE_SHAPE_EXTENSION +#undef HIDE_XKEYBOARD_EXTENSION + +// +// Known reasons of connection failures. +// + +#define INVALID_COOKIE_DATA "Invalid MIT-MAGIC-COOKIE-1 key" +#define INVALID_COOKIE_SIZE ((int) sizeof(INVALID_COOKIE_DATA) - 1) + +#define NO_AUTH_PROTO_DATA "No protocol specified" +#define NO_AUTH_PROTO_SIZE ((int) sizeof(NO_AUTH_PROTO_DATA) - 1) + +// +// Here are the static members. +// + +#ifdef REFERENCES + +int ServerChannel::references_ = 0; + +#endif + +ServerChannel::ServerChannel(Transport *transport, StaticCompressor *compressor) + + : Channel(transport, compressor), readBuffer_(transport_, this) +{ + // + // Sequence number of the next message + // being encoded or decoded. + // + + clientSequence_ = 0; + serverSequence_ = 0; + + // + // Save the last motion event and flush + // it only when the timeout expires. + // + + lastMotion_[0] = '\0'; + + // + // Clear the queue of sequence numbers + // of split commits. Used to mask the + // errors. + // + + initCommitQueue(); + + // + // Do we enable or not sending of expose + // events to the X client. + // + + enableExpose_ = 1; + enableGraphicsExpose_ = 1; + enableNoExpose_ = 1; + + // + // Track data of image currently being + // decompressed. + // + + imageState_ = NULL; + + // + // Track MIT-SHM resources. + // + + shmemState_ = NULL; + + // + // Store the unpack state for each agent + // resource. + // + + for (int i = 0; i < CONNECTIONS_LIMIT; i++) + { + unpackState_[i] = NULL; + } + + // + // Data about the split parameters requested + // by the encoding side. + // + + splitState_.resource = nothing; + splitState_.current = 0; + splitState_.save = 1; + splitState_.load = 1; + splitState_.commit = 0; + + // + // It will be eventually set by + // the server proxy. + // + + fontPort_ = -1; + + #ifdef REFERENCES + *logofs << "ServerChannel: Created new object at " + << this << " for FD#" << fd_ << " out of " + << ++references_ << " allocated channels.\n" + << logofs_flush; + #endif +} + +ServerChannel::~ServerChannel() +{ + #ifdef TEST + *logofs << "ServerChannel: Freeing image state information.\n" + << logofs_flush; + #endif + + handleImageStateRemove(); + + #ifdef TEST + *logofs << "ServerChannel: Freeing shared memory information.\n" + << logofs_flush; + #endif + + handleShmemStateRemove(); + + #ifdef TEST + *logofs << "ServerChannel: Freeing unpack state information.\n" + << logofs_flush; + #endif + + for (int i = 0; i < CONNECTIONS_LIMIT; i++) + { + handleUnpackStateRemove(i); + } + + #ifdef TEST + *logofs << "ServerChannel: Freeing channel caches.\n" + << logofs_flush; + #endif + + #ifdef REFERENCES + *logofs << "ServerChannel: Deleted object at " + << this << " for FD#" << fd_ << " out of " + << --references_ << " allocated channels.\n" + << logofs_flush; + #endif +} + +// +// Beginning of handleRead(). +// + +int ServerChannel::handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message, + unsigned int length) +{ + #ifdef DEBUG + *logofs << "handleRead: Called for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + // + // Pointer to located message and + // its size in bytes. + // + + const unsigned char *inputMessage; + unsigned int inputLength; + + // + // Set when message is found in + // cache. + // + + int hit; + + #if defined(TEST) || defined(INFO) + *logofs << "handleRead: Trying to read from FD#" + << fd_ << " at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + int result = readBuffer_.readMessage(); + + #if defined(DEBUG) || defined(INFO) + *logofs << "handleRead: Read result on FD#" << fd_ + << " is " << result << ".\n" + << logofs_flush; + #endif + + if (result < 0) + { + // + // Let the proxy close the channel. + // + + return -1; + } + else if (result == 0) + { + #if defined(TEST) || defined(INFO) + + // + // This can happen because we have the descriptor + // selected in the read set but we already read + // the data asynchronously, while decoding data + // read from the proxy. + // + + *logofs << "handleRead: WARNING! No data read from FD#" + << fd_ << " while encoding messages.\n" + << logofs_flush; + + #endif + + return 0; + } + + #if defined(TEST) || defined(INFO) || defined(FLUSH) + *logofs << "handleRead: Encoding messages for FD#" << fd_ + << " with " << readBuffer_.getLength() << " bytes " + << "in the buffer.\n" << logofs_flush; + #endif + + // + // Extract any complete message which + // is available in the buffer. + // + + if (proxy -> handleAsyncSwitch(fd_) < 0) + { + return -1; + } + + while ((inputMessage = readBuffer_.getMessage(inputLength)) != NULL) + { + hit = 0; + + if (firstReply_) + { + // + // Handle the X server's authorization reply. + // + + if (handleAuthorization(inputMessage, inputLength) < 0) + { + return -1; + } + + imageByteOrder_ = inputMessage[30]; + bitmapBitOrder_ = inputMessage[31]; + scanlineUnit_ = inputMessage[32]; + scanlinePad_ = inputMessage[33]; + + encodeBuffer.encodeValue((unsigned int) inputMessage[0], 8); + encodeBuffer.encodeValue((unsigned int) inputMessage[1], 8); + encodeBuffer.encodeValue(GetUINT(inputMessage + 2, bigEndian_), 16); + encodeBuffer.encodeValue(GetUINT(inputMessage + 4, bigEndian_), 16); + encodeBuffer.encodeValue(GetUINT(inputMessage + 6, bigEndian_), 16); + + if (ServerCache::lastInitReply.compare(inputLength - 8, inputMessage + 8)) + { + encodeBuffer.encodeBoolValue(1); + } + else + { + encodeBuffer.encodeBoolValue(0); + + for (unsigned int i = 8; i < inputLength; i++) + { + encodeBuffer.encodeValue((unsigned int) inputMessage[i], 8); + } + } + + firstReply_ = 0; + + #if defined(TEST) || defined(OPCODES) + + int bits = encodeBuffer.diffBits(); + + *logofs << "handleRead: Handled first reply. " << inputLength + << " bytes in, " << bits << " bits (" << ((float) bits) / 8 + << " bytes) out.\n" << logofs_flush; + + #endif + + priority_++; + + } + else + { + // + // NX client needs this line to consider + // the initialization phase successfully + // completed. + // + + if (firstClient_ == -1) + { + cerr << "Info" << ": Established X server connection.\n" ; + + firstClient_ = fd_; + } + + // + // Check if this is a reply. + // + + if (*inputMessage == X_Reply) + { + int bits = 0; + + unsigned char inputOpcode = *inputMessage; + + unsigned short int requestSequenceNum; + unsigned char requestOpcode; + unsigned int requestData[3]; + + unsigned int sequenceNum = GetUINT(inputMessage + 2, bigEndian_); + + #ifdef SUSPEND + + if (sequenceNum >= 1000) + { + cerr << "Warning" << ": Exiting to test the resilience of the agent.\n"; + + sleep(2); + + HandleAbort(); + } + + #endif + + // + // We managed all the events and errors caused + // by the previous requests. We can now reset + // the queue of split commits. + // + + clearCommitQueue(); + + // + // Encode opcode and difference between + // current sequence and the last one. + // + + encodeBuffer.encodeOpcodeValue(inputOpcode, serverCache_ -> opcodeCache); + + unsigned int sequenceDiff = sequenceNum - serverSequence_; + + serverSequence_ = sequenceNum; + + #ifdef DEBUG + *logofs << "handleRead: Last server sequence number for FD#" + << fd_ << " is " << serverSequence_ << " with " + << "difference " << sequenceDiff << ".\n" + << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(sequenceDiff, 16, + serverCache_ -> replySequenceCache, 7); + + // + // Now handle the data part. + // + + if (sequenceQueue_.peek(requestSequenceNum, requestOpcode) && + requestSequenceNum == sequenceNum) + { + // + // We've found the request that generated this reply. + // It is possible to compress the reply based on the + // specific request type. + // + + sequenceQueue_.pop(requestSequenceNum, requestOpcode, + requestData[0], requestData[1], requestData[2]); + + // + // If differential compression is disabled + // then use the most simple encoding. + // + + if (control -> LocalDeltaCompression == 0) + { + int result = handleFastReadReply(encodeBuffer, requestOpcode, + inputMessage, inputLength); + if (result < 0) + { + return -1; + } + else if (result > 0) + { + continue; + } + } + + switch (requestOpcode) + { + case X_AllocColor: + { + const unsigned char *nextSrc = inputMessage + 8; + for (unsigned int i = 0; i < 3; i++) + { + unsigned int colorValue = GetUINT(nextSrc, bigEndian_); + nextSrc += 2; + if (colorValue == requestData[i]) + encodeBuffer.encodeBoolValue(1); + else + { + encodeBuffer.encodeBoolValue(0); + encodeBuffer.encodeValue(colorValue - colorValue, 16, 6); + } + } + unsigned int pixel = GetULONG(inputMessage + 16, bigEndian_); + encodeBuffer.encodeValue(pixel, 32, 9); + + priority_++; + } + break; + case X_GetAtomName: + { + unsigned int nameLength = GetUINT(inputMessage + 8, bigEndian_); + encodeBuffer.encodeValue(nameLength, 16, 6); + const unsigned char *nextSrc = inputMessage + 32; + + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeTextData(nextSrc, nameLength); + + priority_++; + } + break; + case X_GetGeometry: + { + // + // TODO: This obtains a satisfactory 10:1, but + // could be cached to leverage the big amount + // of such requests issued by QT clients. + // + + encodeBuffer.encodeCachedValue(inputMessage[1], 8, + serverCache_ -> depthCache); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> getGeometryRootCache, 9); + const unsigned char *nextSrc = inputMessage + 12; + for (unsigned int i = 0; i < 5; i++) + { + encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16, + *serverCache_ -> getGeometryGeomCache[i], 8); + nextSrc += 2; + } + + priority_++; + } + break; + case X_GetInputFocus: + { + // + // Is it a real X_GetInputFocus or a + // masqueraded reply? + // + + if (requestData[0] == X_GetInputFocus) + { + encodeBuffer.encodeValue((unsigned int) inputMessage[1], 2); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> getInputFocusWindowCache); + + priority_++; + } + else + { + // + // TODO: We are not setting priority in case + // of replies other than real X_GetInputFocus + // or X_NXGetUnpackParameters. We should check + // once again that this is OK. + // + + #ifdef TEST + *logofs << "handleRead: Received tainted X_GetInputFocus reply " + << "for request OPCODE#" << requestData[0] << " with " + << "sequence " << sequenceNum << ".\n" + << logofs_flush; + #endif + + // + // Don't encode any data in case of sync + // messages or any other reply for which + // opcode is enough. + // + + if (requestData[0] == opcodeStore_ -> getUnpackParameters) + { + for (int i = 0; i < PACK_METHOD_LIMIT; i++) + { + encodeBuffer.encodeBoolValue(control -> LocalUnpackMethods[i]); + } + + priority_++; + } + else if (requestData[0] == opcodeStore_ -> getShmemParameters) + { + if (handleShmemReply(encodeBuffer, requestOpcode, requestData[1], + inputMessage, inputLength) < 0) + { + return -1; + } + + priority_++; + } + else if (requestData[0] == opcodeStore_ -> getFontParameters) + { + if (handleFontReply(encodeBuffer, requestOpcode, + inputMessage, inputLength) < 0) + { + return -1; + } + } + + // + // Account this data to the original opcode. + // + + requestOpcode = requestData[0]; + } + } + break; + case X_GetKeyboardMapping: + { + unsigned int keysymsPerKeycode = (unsigned int) inputMessage[1]; + if (ServerCache::getKeyboardMappingLastMap.compare(inputLength - 32, + inputMessage + 32) && (keysymsPerKeycode == + ServerCache::getKeyboardMappingLastKeysymsPerKeycode)) + { + encodeBuffer.encodeBoolValue(1); + + priority_++; + + break; + } + ServerCache::getKeyboardMappingLastKeysymsPerKeycode = keysymsPerKeycode; + encodeBuffer.encodeBoolValue(0); + unsigned int numKeycodes = + (((inputLength - 32) / keysymsPerKeycode) >> 2); + encodeBuffer.encodeValue(numKeycodes, 8); + encodeBuffer.encodeValue(keysymsPerKeycode, 8, 4); + const unsigned char *nextSrc = inputMessage + 32; + unsigned char previous = 0; + for (unsigned int count = numKeycodes * keysymsPerKeycode; + count; --count) + { + unsigned int keysym = GetULONG(nextSrc, bigEndian_); + nextSrc += 4; + if (keysym == NoSymbol) + encodeBuffer.encodeBoolValue(1); + else + { + encodeBuffer.encodeBoolValue(0); + unsigned int first3Bytes = (keysym >> 8); + encodeBuffer.encodeCachedValue(first3Bytes, 24, + serverCache_ -> getKeyboardMappingKeysymCache, 9); + unsigned char lastByte = (unsigned char) (keysym & 0xff); + encodeBuffer.encodeCachedValue(lastByte - previous, 8, + serverCache_ -> getKeyboardMappingLastByteCache, 5); + previous = lastByte; + } + } + + priority_++; + } + break; + case X_GetModifierMapping: + { + encodeBuffer.encodeValue((unsigned int) inputMessage[1], 8); + const unsigned char *nextDest = inputMessage + 32; + if (ServerCache::getModifierMappingLastMap.compare(inputLength - 32, + nextDest)) + { + encodeBuffer.encodeBoolValue(1); + + priority_++; + + break; + } + encodeBuffer.encodeBoolValue(0); + for (unsigned int count = inputLength - 32; count; count--) + { + unsigned char next = *nextDest++; + if (next == 0) + encodeBuffer.encodeBoolValue(1); + else + { + encodeBuffer.encodeBoolValue(0); + encodeBuffer.encodeValue(next, 8); + } + } + + priority_++; + } + break; + case X_GetProperty: + { + MessageStore *messageStore = serverStore_ -> + getReplyStore(X_GetProperty); + + hit = handleEncode(encodeBuffer, serverCache_, messageStore, + requestOpcode, inputMessage, inputLength); + + priority_++; + } + break; + case X_GetSelectionOwner: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> getSelectionOwnerCache, 9); + priority_++; + } + break; + case X_GetWindowAttributes: + { + encodeBuffer.encodeValue((unsigned int) inputMessage[1], 2); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> visualCache); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 12, bigEndian_), + 16, serverCache_ -> getWindowAttributesClassCache, 3); + encodeBuffer.encodeCachedValue(inputMessage[14], 8, + serverCache_ -> getWindowAttributesBitGravityCache); + encodeBuffer.encodeCachedValue(inputMessage[15], 8, + serverCache_ -> getWindowAttributesWinGravityCache); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, bigEndian_), + 32, serverCache_ -> getWindowAttributesPlanesCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 20, bigEndian_), + 32, serverCache_ -> getWindowAttributesPixelCache, 9); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[24]); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[25]); + encodeBuffer.encodeValue((unsigned int) inputMessage[26], 2); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[27]); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 28, bigEndian_), + 29, serverCache_ -> colormapCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 32, bigEndian_), + 32, serverCache_ -> getWindowAttributesAllEventsCache); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 36, bigEndian_), + 32, serverCache_ -> getWindowAttributesYourEventsCache); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 40, bigEndian_), + 16, serverCache_ -> getWindowAttributesDontPropagateCache); + + priority_++; + } + break; + case X_GrabKeyboard: + case X_GrabPointer: + { + encodeBuffer.encodeValue((unsigned int) inputMessage[1], 3); + + priority_++; + } + break; + case X_InternAtom: + { + encodeBuffer.encodeValue(GetULONG(inputMessage + 8, bigEndian_), 29, 9); + + priority_++; + } + break; + case X_ListExtensions: + { + encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 8); + unsigned int numExtensions = (unsigned int) inputMessage[1]; + encodeBuffer.encodeValue(numExtensions, 8); + const unsigned char *nextSrc = inputMessage + 32; + + for (; numExtensions; numExtensions--) + { + unsigned int length = (unsigned int) (*nextSrc++); + + encodeBuffer.encodeValue(length, 8); + + #ifdef HIDE_MIT_SHM_EXTENSION + + if (!strncmp((char *) nextSrc, "MIT-SHM", 7)) + { + #ifdef TEST + *logofs << "handleRead: Hiding MIT-SHM extension in reply.\n" + << logofs_flush; + #endif + + memcpy((unsigned char *) nextSrc, "NO-MIT-", 7); + } + + #endif + + #ifdef HIDE_BIG_REQUESTS_EXTENSION + + if (!strncmp((char *) nextSrc, "BIG-REQUESTS", 12)) + { + #ifdef TEST + *logofs << "handleRead: Hiding BIG-REQUESTS extension in reply.\n" + << logofs_flush; + #endif + + memcpy((unsigned char *) nextSrc, "NO-BIG-REQUE", 12); + } + + #endif + + #ifdef HIDE_XKEYBOARD_EXTENSION + + if (!strncmp((char *) nextSrc, "XKEYBOARD", 9)) + { + #ifdef TEST + *logofs << "handleRead: Hiding XKEYBOARD extension in reply.\n" + << logofs_flush; + #endif + + memcpy((unsigned char *) nextSrc, "NO-XKEYBO", 9); + } + + #endif + + #ifdef HIDE_XFree86_Bigfont_EXTENSION + + if (!strncmp((char *) nextSrc, "XFree86-Bigfont", 15)) + { + #ifdef TEST + *logofs << "handleRead: Hiding XFree86-Bigfont extension in reply.\n" + << logofs_flush; + #endif + + memcpy((unsigned char *) nextSrc, "NO-XFree86-Bigf", 15); + } + + #endif + + #ifdef HIDE_SHAPE_EXTENSION + + if (!strncmp((char *) nextSrc, "SHAPE", 5)) + { + #ifdef TEST + *logofs << "handleRead: Hiding SHAPE extension in reply.\n" + << logofs_flush; + #endif + + memcpy((unsigned char *) nextSrc, "NO-SH", 5); + } + + #endif + + // + // Check if user disabled RENDER extension. + // + + if (control -> HideRender == 1 && + !strncmp((char *) nextSrc, "RENDER", 6)) + { + #ifdef TEST + *logofs << "handleRead: Hiding RENDER extension in reply.\n" + << logofs_flush; + #endif + + memcpy((unsigned char *) nextSrc, "NO-REN", 6); + } + + for (; length; length--) + { + encodeBuffer.encodeValue((unsigned int) (*nextSrc++), 8); + } + } + + priority_++; + } + break; + case X_ListFonts: + { + MessageStore *messageStore = serverStore_ -> + getReplyStore(X_ListFonts); + + if (handleEncodeCached(encodeBuffer, serverCache_, messageStore, + inputMessage, inputLength)) + { + priority_++; + + hit = 1; + + break; + } + + encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 8); + unsigned int numFonts = GetUINT(inputMessage + 8, bigEndian_); + encodeBuffer.encodeValue(numFonts, 16, 6); + + // Differential encoding. + encodeBuffer.encodeBoolValue(1); + + const unsigned char* nextSrc = inputMessage + 32; + for (; numFonts; numFonts--) + { + unsigned int length = (unsigned int) (*nextSrc++); + encodeBuffer.encodeValue(length, 8); + + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeTextData(nextSrc, length); + + nextSrc += length; + } + + priority_++; + } + break; + case X_LookupColor: + case X_AllocNamedColor: + { + const unsigned char *nextSrc = inputMessage + 8; + if (requestOpcode == X_AllocNamedColor) + { + encodeBuffer.encodeValue(GetULONG(nextSrc, bigEndian_), 32, 9); + nextSrc += 4; + } + unsigned int count = 3; + do + { + unsigned int exactColor = GetUINT(nextSrc, bigEndian_); + encodeBuffer.encodeValue(exactColor, 16, 9); + unsigned int visualColor = GetUINT(nextSrc + 6, bigEndian_) - + exactColor; + encodeBuffer.encodeValue(visualColor, 16, 5); + nextSrc += 2; + } + while (--count); + + priority_++; + } + break; + case X_QueryBestSize: + { + encodeBuffer.encodeValue(GetUINT(inputMessage + 8, bigEndian_), 16, 8); + encodeBuffer.encodeValue(GetUINT(inputMessage + 10, bigEndian_), 16, 8); + + priority_++; + } + break; + case X_QueryColors: + { + // Differential encoding. + encodeBuffer.encodeBoolValue(1); + + unsigned int numColors = ((inputLength - 32) >> 3); + const unsigned char *nextSrc = inputMessage + 40; + unsigned char *nextDest = (unsigned char *) inputMessage + 38; + for (unsigned int c = 1; c < numColors; c++) + { + for (unsigned int i = 0; i < 6; i++) + *nextDest++ = *nextSrc++; + nextSrc += 2; + } + unsigned int colorsLength = numColors * 6; + if (serverCache_ -> queryColorsLastReply.compare(colorsLength, + inputMessage + 32)) + encodeBuffer.encodeBoolValue(1); + else + { + const unsigned char *nextSrc = inputMessage + 32; + encodeBuffer.encodeBoolValue(0); + encodeBuffer.encodeValue(numColors, 16, 5); + for (numColors *= 3; numColors; numColors--) + { + encodeBuffer.encodeValue(GetUINT(nextSrc, bigEndian_), 16); + nextSrc += 2; + } + } + + priority_++; + } + break; + case X_QueryExtension: + { + if (requestData[0] == X_QueryExtension) + { + // + // Value in requestData[0] will be nonzero + // if the request is for an extension that + // we should hide to the X client. + // + + if (requestData[1]) + { + encodeBuffer.encodeBoolValue(0); + encodeBuffer.encodeValue(0, 8); + } + else + { + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[8]); + encodeBuffer.encodeValue((unsigned int) inputMessage[9], 8); + } + + encodeBuffer.encodeValue((unsigned int) inputMessage[10], 8); + encodeBuffer.encodeValue((unsigned int) inputMessage[11], 8); + + if (requestData[2] == X_NXInternalShapeExtension) + { + opcodeStore_ -> shapeExtension = inputMessage[9]; + + #ifdef TEST + *logofs << "handleRead: Shape extension opcode for FD#" << fd_ + << " is " << (unsigned int) opcodeStore_ -> shapeExtension + << ".\n" << logofs_flush; + #endif + } + else if (requestData[2] == X_NXInternalRenderExtension) + { + opcodeStore_ -> renderExtension = inputMessage[9]; + + #ifdef TEST + *logofs << "handleRead: Render extension opcode for FD#" << fd_ + << " is " << (unsigned int) opcodeStore_ -> renderExtension + << ".\n" << logofs_flush; + #endif + } + + priority_++; + } + else + { + #ifdef TEST + *logofs << "handleRead: Received tainted X_QueryExtension reply " + << "for request OPCODE#" << requestData[0] << " with " + << "sequence " << sequenceNum << ".\n" + << logofs_flush; + #endif + + if (requestData[0] == opcodeStore_ -> getShmemParameters) + { + if (handleShmemReply(encodeBuffer, requestOpcode, requestData[1], + inputMessage, inputLength) < 0) + { + return -1; + } + + priority_++; + } + // + // Account this data to the original opcode. + // + + requestOpcode = requestData[0]; + } + } + break; + case X_QueryFont: + { + MessageStore *messageStore = serverStore_ -> + getReplyStore(X_QueryFont); + + if (handleEncodeCached(encodeBuffer, serverCache_, messageStore, + inputMessage, inputLength)) + { + priority_++; + + hit = 1; + + break; + } + + // Differential encoding. + encodeBuffer.encodeBoolValue(1); + + unsigned int numProperties = GetUINT(inputMessage + 46, bigEndian_); + unsigned int numCharInfos = GetULONG(inputMessage + 56, bigEndian_); + encodeBuffer.encodeValue(numProperties, 16, 8); + encodeBuffer.encodeValue(numCharInfos, 32, 10); + handleEncodeCharInfo(inputMessage + 8, encodeBuffer); + handleEncodeCharInfo(inputMessage + 24, encodeBuffer); + encodeBuffer.encodeValue(GetUINT(inputMessage + 40, bigEndian_), 16, 9); + encodeBuffer.encodeValue(GetUINT(inputMessage + 42, bigEndian_), 16, 9); + encodeBuffer.encodeValue(GetUINT(inputMessage + 44, bigEndian_), 16, 9); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[48]); + encodeBuffer.encodeValue((unsigned int) inputMessage[49], 8); + encodeBuffer.encodeValue((unsigned int) inputMessage[50], 8); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[51]); + encodeBuffer.encodeValue(GetUINT(inputMessage + 52, bigEndian_), 16, 9); + encodeBuffer.encodeValue(GetUINT(inputMessage + 54, bigEndian_), 16, 9); + const unsigned char *nextSrc = inputMessage + 60; + unsigned int index; + + int end = 0; + + if (ServerCache::queryFontFontCache.lookup(numProperties * 8 + + numCharInfos * 12, nextSrc, index)) + { + encodeBuffer.encodeBoolValue(1); + encodeBuffer.encodeValue(index, 4); + + end = 1; + } + + if (end == 0) + { + encodeBuffer.encodeBoolValue(0); + for (; numProperties; numProperties--) + { + encodeBuffer.encodeValue(GetULONG(nextSrc, bigEndian_), 32, 9); + encodeBuffer.encodeValue(GetULONG(nextSrc + 4, bigEndian_), 32, 9); + nextSrc += 8; + } + for (; numCharInfos; numCharInfos--) + { + handleEncodeCharInfo(nextSrc, encodeBuffer); + + nextSrc += 12; + } + } + + priority_++; + } + break; + case X_QueryPointer: + { + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> queryPointerRootCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), + 29, serverCache_ -> queryPointerChildCache, 9); + unsigned int rootX = GetUINT(inputMessage + 16, bigEndian_); + unsigned int rootY = GetUINT(inputMessage + 18, bigEndian_); + unsigned int eventX = GetUINT(inputMessage + 20, bigEndian_); + unsigned int eventY = GetUINT(inputMessage + 22, bigEndian_); + eventX -= rootX; + eventY -= rootY; + encodeBuffer.encodeCachedValue( + rootX - serverCache_ -> motionNotifyLastRootX, 16, + serverCache_ -> motionNotifyRootXCache, 8); + serverCache_ -> motionNotifyLastRootX = rootX; + encodeBuffer.encodeCachedValue( + rootY - serverCache_ -> motionNotifyLastRootY, 16, + serverCache_ -> motionNotifyRootYCache, 8); + serverCache_ -> motionNotifyLastRootY = rootY; + encodeBuffer.encodeCachedValue(eventX, 16, + serverCache_ -> motionNotifyEventXCache, 8); + encodeBuffer.encodeCachedValue(eventY, 16, + serverCache_ -> motionNotifyEventYCache, 8); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 24, bigEndian_), + 16, serverCache_ -> motionNotifyStateCache); + priority_++; + } + break; + case X_QueryTree: + { + // + // This was very inefficient. In practice + // it just copied data on the output. Now + // it obtains an average 7:1 compression + // and could optionally be cached. + // + + unsigned int children = GetUINT(inputMessage + 16, bigEndian_); + + encodeBuffer.encodeValue(children, 16, 8); + + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 29, + serverCache_ -> queryTreeWindowCache); + + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), 29, + serverCache_ -> queryTreeWindowCache); + + const unsigned char *next = inputMessage + 32; + + for (unsigned int i = 0; i < children; i++) + { + encodeBuffer.encodeCachedValue(GetULONG(next + (i * 4), bigEndian_), 29, + serverCache_ -> queryTreeWindowCache); + } + + priority_++; + } + break; + case X_TranslateCoords: + { + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> translateCoordsChildCache, 9); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 12, bigEndian_), + 16, serverCache_ -> translateCoordsXCache, 8); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 14, bigEndian_), + 16, serverCache_ -> translateCoordsYCache, 8); + priority_++; + } + break; + case X_GetImage: + { + MessageStore *messageStore = serverStore_ -> + getReplyStore(X_GetImage); + + if (handleEncodeCached(encodeBuffer, serverCache_, messageStore, + inputMessage, inputLength)) + { + priority_++; + + hit = 1; + + break; + } + + // Depth. + encodeBuffer.encodeCachedValue(inputMessage[1], 8, + serverCache_ -> depthCache); + // Reply length. + encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 9); + + // Visual. + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 29, + serverCache_ -> visualCache); + + // Since ProtoStep8 (#issue 108) + handleCopy(encodeBuffer, requestOpcode, messageStore -> + dataOffset, inputMessage, inputLength); + + priority_++; + } + break; + case X_GetPointerMapping: + { + encodeBuffer.encodeValue(inputMessage[1], 8, 4); + encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 4); + for (unsigned int i = 32; i < inputLength; i++) + encodeBuffer.encodeValue((unsigned int) inputMessage[i], 8, 4); + + priority_++; + } + break; + case X_GetKeyboardControl: + { + encodeBuffer.encodeValue(inputMessage[1], 8, 2); + encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 8); + for (unsigned int i = 8; i < inputLength; i++) + encodeBuffer.encodeValue((unsigned int) inputMessage[i], 8, 4); + + priority_++; + } + break; + default: + { + #ifdef PANIC + *logofs << "ServerChannel: PANIC! No matching request with " + << "OPCODE#" << (unsigned int) requestOpcode + << " for reply with sequence number " + << requestSequenceNum << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": No matching request with OPCODE#" + << (unsigned int) requestOpcode << " for reply with " + << "sequence number " << requestSequenceNum << ".\n"; + + return -1; + } + } + + bits = encodeBuffer.diffBits(); + + #if defined(TEST) || defined(OPCODES) + + const char *cacheString = (hit ? "cached " : ""); + + *logofs << "handleRead: Handled " << cacheString << "reply to OPCODE#" + << (unsigned int) requestOpcode << " (" << DumpOpcode(requestOpcode) + << ") for FD#" << fd_ << " sequence " << serverSequence_ + << ". " << inputLength << " bytes in, " << bits << " bits (" + << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush; + + #endif + + } // End of if (sequenceQueue_.peek(requestSequenceNum, requestOpcode) && ... + else + { + // + // We didn't push the request opcode. + // Check if fast encoding is required. + // + + requestOpcode = X_Reply; + + if (control -> LocalDeltaCompression == 0) + { + int result = handleFastReadReply(encodeBuffer, requestOpcode, + inputMessage, inputLength); + if (result < 0) + { + return -1; + } + else if (result > 0) + { + continue; + } + } + + // + // Group all replies whose opcode was not + // pushed in sequence number queue under + // the category 'generic reply'. + // + + #ifdef DEBUG + *logofs << "handleRead: Identified generic reply.\n" + << logofs_flush; + #endif + + MessageStore *messageStore = serverStore_ -> + getReplyStore(X_NXInternalGenericReply); + + hit = handleEncode(encodeBuffer, serverCache_, messageStore, + requestOpcode, inputMessage, inputLength); + + priority_++; + + bits = encodeBuffer.diffBits(); + + #if defined(TEST) || defined(OPCODES) + + const char *cacheString = (hit ? "cached " : ""); + + *logofs << "handleRead: Handled " << cacheString << "generic reply " + << "OPCODE#" << X_NXInternalGenericReply << " for FD#" << fd_ + << " sequence " << serverSequence_ << ". " << inputLength + << " bytes in, " << bits << " bits (" << ((float) bits) / 8 + << " bytes) out.\n" << logofs_flush; + + #endif + } + + if (hit) + { + statistics -> addCachedReply(requestOpcode); + } + + statistics -> addReplyBits(requestOpcode, inputLength << 3, bits); + + } // End of if (inputMessage[0] == 1) ... + else + { + // + // Event or error. + // + + unsigned char inputOpcode = *inputMessage; + + unsigned int inputSequence = GetUINT(inputMessage + 2, bigEndian_); + + // + // Check if this is an event which we can discard. + // + + if ((inputOpcode == Expose && enableExpose_ == 0) || + (inputOpcode == GraphicsExpose && enableGraphicsExpose_ == 0) || + (inputOpcode == NoExpose && enableNoExpose_ == 0)) + { + continue; + } + else if (shmemState_ != NULL && shmemState_ -> enabled == 1 && + inputOpcode == shmemState_ -> event && + checkShmemEvent(inputOpcode, inputSequence, + inputMessage) > 0) + { + continue; + } + else if (inputOpcode == MotionNotify) + { + // + // Save the motion event and send when another + // event or error is received or the motion ti- + // meout is elapsed. If a previous motion event + // was already saved, we replace it with the + // new one and don't reset the timeout, so we + // still have a motion event every given ms. + // + + memcpy(lastMotion_, inputMessage, 32); + + #ifdef TEST + *logofs << "handleRead: Saved suppressed motion event for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + continue; + } + else if (inputOpcode == X_Error) + { + // + // Check if this is an error that matches a + // sequence number for which we are expecting + // a reply. + // + + unsigned short int errorSequenceNum; + unsigned char errorOpcode; + + if (sequenceQueue_.peek(errorSequenceNum, errorOpcode) && + ((unsigned int) errorSequenceNum == inputSequence)) + { + sequenceQueue_.pop(errorSequenceNum, errorOpcode); + } + + // + // Check if error is due to an image commit + // generated at the end of a split. + // + + if (checkCommitError(*(inputMessage + 1), inputSequence, inputMessage) > 0) + { + #ifdef TEST + *logofs << "handleRead: Skipping error on image commit for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + continue; + } + + // + // Check if it's an error generated by a request + // concerning shared memory support. + // + + else if (shmemState_ != NULL && (shmemState_ -> sequence == + inputSequence || (shmemState_ -> enabled == 1 && + (shmemState_ -> opcode == *(inputMessage + 10) || + shmemState_ -> error == *(inputMessage + 1)))) && + checkShmemError(*(inputMessage + 1), inputSequence, + inputMessage) > 0) + { + #ifdef TEST + *logofs << "handleRead: Skipping error on shmem operation for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + continue; + } + } + // + // Check if user pressed the CTRL+ALT+SHIFT+ESC key + // sequence because was unable to kill the session + // through the normal procedure. + // + + if (inputOpcode == KeyPress) + { + if (checkKeyboardEvent(inputOpcode, inputSequence, inputMessage) == 1) + { + #ifdef TEST + *logofs << "handleRead: Removing the key sequence from the " + << "event stream for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + continue; + } + } + + // + // We are going to handle an event or error + // that's not a mouse motion. Prepend any + // saved motion to it. + // + + if (lastMotion_[0] != '\0') + { + if (handleMotion(encodeBuffer) < 0) + { + #ifdef PANIC + *logofs << "handleRead: PANIC! Can't encode motion event for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't encode motion event for FD#" + << fd_ << ".\n"; + + return -1; + } + } + + // + // Encode opcode and difference between + // current sequence and the last one. + // + + encodeBuffer.encodeOpcodeValue(inputOpcode, serverCache_ -> opcodeCache); + + unsigned int sequenceDiff = inputSequence - serverSequence_; + + serverSequence_ = inputSequence; + + #ifdef DEBUG + *logofs << "handleRead: Last server sequence number for FD#" + << fd_ << " is " << serverSequence_ << " with " + << "difference " << sequenceDiff << ".\n" + << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(sequenceDiff, 16, + serverCache_ -> eventSequenceCache, 7); + + // + // If differential compression is disabled + // then use the most simple encoding. + // + + if (control -> LocalDeltaCompression == 0) + { + int result = handleFastReadEvent(encodeBuffer, inputOpcode, + inputMessage, inputLength); + if (result < 0) + { + return -1; + } + else if (result > 0) + { + continue; + } + } + + switch (inputOpcode) + { + case X_Error: + { + // + // Set the priority flag in the case of + // a X protocol error. This may restart + // the client if it was waiting for the + // reply. + // + + priority_++; + + unsigned char errorCode = *(inputMessage + 1); + + encodeBuffer.encodeCachedValue(errorCode, 8, + serverCache_ -> errorCodeCache); + + if (errorCode != 11 && errorCode != 8 && + errorCode != 15 && errorCode != 1) + { + encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 16); + } + + if (errorCode >= 18) + { + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16, + serverCache_ -> errorMinorCache); + } + + encodeBuffer.encodeCachedValue(inputMessage[10], 8, + serverCache_ -> errorMajorCache); + + if (errorCode >= 18) + { + const unsigned char *nextSrc = inputMessage + 11; + for (unsigned int i = 11; i < 32; i++) + encodeBuffer.encodeValue(*nextSrc++, 8); + } + } + break; + case ButtonPress: + case ButtonRelease: + case KeyPress: + case KeyRelease: + case MotionNotify: + case EnterNotify: + case LeaveNotify: + { + // + // Set the priority in the case this is + // an event that the remote side may + // care to receive as soon as possible. + // + + switch (inputOpcode) + { + case ButtonPress: + case ButtonRelease: + case KeyPress: + case KeyRelease: + { + priority_++; + } + } + + unsigned char detail = inputMessage[1]; + if (*inputMessage == MotionNotify) + encodeBuffer.encodeBoolValue((unsigned int) detail); + else if ((*inputMessage == EnterNotify) || (*inputMessage == LeaveNotify)) + encodeBuffer.encodeValue((unsigned int) detail, 3); + else if (*inputMessage == KeyRelease) + { + if (detail == serverCache_ -> keyPressLastKey) + encodeBuffer.encodeBoolValue(1); + else + { + encodeBuffer.encodeBoolValue(0); + encodeBuffer.encodeValue((unsigned int) detail, 8); + } + } + else if ((*inputMessage == ButtonPress) || (*inputMessage == ButtonRelease)) + encodeBuffer.encodeCachedValue(detail, 8, + serverCache_ -> buttonCache); + else + encodeBuffer.encodeValue((unsigned int) detail, 8); + unsigned int timestamp = GetULONG(inputMessage + 4, bigEndian_); + unsigned int timestampDiff = + timestamp - serverCache_ -> lastTimestamp; + serverCache_ -> lastTimestamp = timestamp; + encodeBuffer.encodeCachedValue(timestampDiff, 32, + serverCache_ -> motionNotifyTimestampCache, 9); + int skipRest = 0; + if (*inputMessage == KeyRelease) + { + skipRest = 1; + for (unsigned int i = 8; i < 31; i++) + { + if (inputMessage[i] != serverCache_ -> keyPressCache[i - 8]) + { + skipRest = 0; + break; + } + } + encodeBuffer.encodeBoolValue(skipRest); + } + + if (!skipRest) + { + const unsigned char *nextSrc = inputMessage + 8; + for (unsigned int i = 0; i < 3; i++) + { + encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 29, + *serverCache_ -> motionNotifyWindowCache[i], 6); + nextSrc += 4; + } + unsigned int rootX = GetUINT(inputMessage + 20, bigEndian_); + unsigned int rootY = GetUINT(inputMessage + 22, bigEndian_); + unsigned int eventX = GetUINT(inputMessage + 24, bigEndian_); + unsigned int eventY = GetUINT(inputMessage + 26, bigEndian_); + eventX -= rootX; + eventY -= rootY; + encodeBuffer.encodeCachedValue(rootX - + serverCache_ -> motionNotifyLastRootX, 16, + serverCache_ -> motionNotifyRootXCache, 6); + serverCache_ -> motionNotifyLastRootX = rootX; + encodeBuffer.encodeCachedValue(rootY - + serverCache_ -> motionNotifyLastRootY, 16, + serverCache_ -> motionNotifyRootYCache, 6); + serverCache_ -> motionNotifyLastRootY = rootY; + encodeBuffer.encodeCachedValue(eventX, 16, + serverCache_ -> motionNotifyEventXCache, 6); + encodeBuffer.encodeCachedValue(eventY, 16, + serverCache_ -> motionNotifyEventYCache, 6); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 28, bigEndian_), + 16, serverCache_ -> motionNotifyStateCache); + if ((*inputMessage == EnterNotify) || (*inputMessage == LeaveNotify)) + encodeBuffer.encodeValue((unsigned int) inputMessage[30], 2); + else + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[30]); + if ((*inputMessage == EnterNotify) || (*inputMessage == LeaveNotify)) + encodeBuffer.encodeValue((unsigned int) inputMessage[31], 2); + else if (*inputMessage == KeyPress) + { + serverCache_ -> keyPressLastKey = detail; + for (unsigned int i = 8; i < 31; i++) + { + serverCache_ -> keyPressCache[i - 8] = inputMessage[i]; + } + } + } + } + break; + case ColormapNotify: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), + 29, serverCache_ -> colormapNotifyWindowCache, 8); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> colormapNotifyColormapCache, 8); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[12]); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[13]); + } + break; + case ConfigureNotify: + { + const unsigned char *nextSrc = inputMessage + 4; + for (unsigned int i = 0; i < 3; i++) + { + encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 29, + *serverCache_ -> configureNotifyWindowCache[i], 9); + nextSrc += 4; + } + for (unsigned int j = 0; j < 5; j++) + { + encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16, + *serverCache_ -> configureNotifyGeomCache[j], 8); + nextSrc += 2; + } + encodeBuffer.encodeBoolValue(*nextSrc); + } + break; + case CreateNotify: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), + 29, serverCache_ -> createNotifyWindowCache, 9); + unsigned int window = GetULONG(inputMessage + 8, bigEndian_); + encodeBuffer.encodeValue(window - + serverCache_ -> createNotifyLastWindow, 29, 5); + serverCache_ -> createNotifyLastWindow = window; + const unsigned char* nextSrc = inputMessage + 12; + for (unsigned int i = 0; i < 5; i++) + { + encodeBuffer.encodeValue(GetUINT(nextSrc, bigEndian_), 16, 9); + nextSrc += 2; + } + encodeBuffer.encodeBoolValue(*nextSrc); + } + break; + case Expose: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29, + serverCache_ -> exposeWindowCache, 9); + const unsigned char *nextSrc = inputMessage + 8; + for (unsigned int i = 0; i < 5; i++) + { + encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16, + *serverCache_ -> exposeGeomCache[i], 6); + nextSrc += 2; + } + } + break; + case FocusIn: + case FocusOut: + { + encodeBuffer.encodeValue((unsigned int) inputMessage[1], 3); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), + 29, serverCache_ -> focusInWindowCache, 9); + encodeBuffer.encodeValue((unsigned int) inputMessage[8], 2); + } + break; + case KeymapNotify: + { + if (ServerCache::lastKeymap.compare(31, inputMessage + 1)) + encodeBuffer.encodeBoolValue(1); + else + { + encodeBuffer.encodeBoolValue(0); + const unsigned char *nextSrc = inputMessage + 1; + for (unsigned int i = 1; i < 32; i++) + encodeBuffer.encodeValue((unsigned int) *nextSrc++, 8); + } + } + break; + case MapNotify: + case UnmapNotify: + case DestroyNotify: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), + 29, serverCache_ -> mapNotifyEventCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> mapNotifyWindowCache, 9); + if ((*inputMessage == MapNotify) || (*inputMessage == UnmapNotify)) + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[12]); + } + break; + case NoExpose: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), + 29, serverCache_ -> noExposeDrawableCache, 9); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16, + serverCache_ -> noExposeMinorCache); + encodeBuffer.encodeCachedValue(inputMessage[10], 8, + serverCache_ -> noExposeMajorCache); + } + break; + case PropertyNotify: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), + 29, serverCache_ -> propertyNotifyWindowCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> propertyNotifyAtomCache, 9); + unsigned int timestamp = GetULONG(inputMessage + 12, bigEndian_); + unsigned int timestampDiff = + timestamp - serverCache_ -> lastTimestamp; + serverCache_ -> lastTimestamp = timestamp; + encodeBuffer.encodeValue(timestampDiff, 32, 9); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[16]); + } + break; + case ReparentNotify: + { + const unsigned char* nextSrc = inputMessage + 4; + for (unsigned int i = 0; i < 3; i++) + { + encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), + 29, serverCache_ -> reparentNotifyWindowCache, 9); + nextSrc += 4; + } + encodeBuffer.encodeValue(GetUINT(nextSrc, bigEndian_), 16, 6); + encodeBuffer.encodeValue(GetUINT(nextSrc + 2, bigEndian_), 16, 6); + encodeBuffer.encodeBoolValue((unsigned int)inputMessage[20]); + } + break; + case SelectionClear: + { + unsigned int timestamp = GetULONG(inputMessage + 4, bigEndian_); + unsigned int timestampDiff = timestamp - serverCache_ -> lastTimestamp; + serverCache_ -> lastTimestamp = timestamp; + encodeBuffer.encodeValue(timestampDiff, 32, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> selectionClearWindowCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), + 29, serverCache_ -> selectionClearAtomCache, 9); + } + break; + case SelectionRequest: + { + unsigned int timestamp = GetULONG(inputMessage + 4, bigEndian_); + unsigned int timestampDiff = timestamp - serverCache_ -> lastTimestamp; + serverCache_ -> lastTimestamp = timestamp; + encodeBuffer.encodeValue(timestampDiff, 32, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> selectionClearWindowCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), + 29, serverCache_ -> selectionClearWindowCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, bigEndian_), + 29, serverCache_ -> selectionClearAtomCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 20, bigEndian_), + 29, serverCache_ -> selectionClearAtomCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 24, bigEndian_), + 29, serverCache_ -> selectionClearAtomCache, 9); + } + break; + case VisibilityNotify: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), + 29, serverCache_ -> visibilityNotifyWindowCache, 9); + encodeBuffer.encodeValue((unsigned int) inputMessage[8], 2); + } + break; + default: + { + #ifdef TEST + *logofs << "handleRead: Using generic event compression " + << "for OPCODE#" << (unsigned int) inputOpcode + << ".\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(*(inputMessage + 1), 8, + serverCache_ -> genericEventCharCache); + + for (unsigned int i = 0; i < 14; i++) + { + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + i * 2 + 4, bigEndian_), + 16, *serverCache_ -> genericEventIntCache[i]); + } + } + + } // switch (inputOpcode)... + + int bits = encodeBuffer.diffBits(); + + #if defined(TEST) || defined(OPCODES) + + if (*inputMessage == X_Error) + { + unsigned char code = *(inputMessage + 1); + + *logofs << "handleRead: Handled error ERR_CODE#" + << (unsigned int) code << " for FD#" << fd_; + + *logofs << " RES_ID#" << GetULONG(inputMessage + 4, bigEndian_); + + *logofs << " MIN_OP#" << GetUINT(inputMessage + 8, bigEndian_); + + *logofs << " MAJ_OP#" << (unsigned int) *(inputMessage + 10); + + *logofs << " sequence " << inputSequence << ". " << inputLength + << " bytes in, " << bits << " bits (" << ((float) bits) / 8 + << " bytes) out.\n" << logofs_flush; + } + else + { + *logofs << "handleRead: Handled event OPCODE#" + << (unsigned int) *inputMessage << " for FD#" << fd_ + << " sequence " << inputSequence << ". " << inputLength + << " bytes in, " << bits << " bits (" << ((float) bits) / 8 + << " bytes) out.\n" << logofs_flush; + } + + #endif + + statistics -> addEventBits(*inputMessage, inputLength << 3, bits); + + } // End of if (inputMessage[0] == X_Reply) ... else ... + + } // End of if (firstReply_) ... else ... + + } // End of while ((inputMessage = readBuffer_.getMessage(inputLength)) != 0) ... + + // + // Check if we need to flush because of + // prioritized data. + // + + if (priority_ > 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleRead: WARNING! Requesting flush " + << "because of " << priority_ << " prioritized " + << "messages for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + if (proxy -> handleAsyncPriority() < 0) + { + return -1; + } + + // + // Reset the priority flag. + // + + priority_ = 0; + } + + // + // Flush if we produced enough data. + // + + if (proxy -> canAsyncFlush() == 1) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleRead: WARNING! Requesting flush " + << "because of token length exceeded.\n" + << logofs_flush; + #endif + + if (proxy -> handleAsyncFlush() < 0) + { + return -1; + } + } + + #if defined(TEST) || defined(INFO) + + if (transport_ -> pending() != 0 || + readBuffer_.checkMessage() != 0) + { + *logofs << "handleRead: PANIC! Buffer for X descriptor FD#" + << fd_ << " has " << transport_ -> pending() + << " bytes to read.\n" << logofs_flush; + + HandleCleanup(); + } + + #endif + + // + // Reset the read buffer. + // + + readBuffer_.fullReset(); + + return 1; +} + +// +// End of handleRead(). +// + +// +// Beginning of handleWrite(). +// + +int ServerChannel::handleWrite(const unsigned char *message, unsigned int length) +{ + #ifdef TEST + *logofs << "handleWrite: Called for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + // + // Create the buffer from which to + // decode messages. + // + + DecodeBuffer decodeBuffer(message, length); + + #if defined(TEST) || defined(INFO) || defined(FLUSH) + *logofs << "handleWrite: Decoding messages for FD#" << fd_ + << " with " << length << " bytes in the buffer.\n" + << logofs_flush; + #endif + + if (firstRequest_) + { + // + // Need to add the length of the first request + // because it was not present in the previous + // versions. Length of the first request was + // assumed to be the same as the encode buffer + // but this may be not the case if a different + // encoding is used. + // + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeValue(length, 8); + + unsigned int nextByte; + unsigned char *outputMessage = writeBuffer_.addMessage(length); + unsigned char *nextDest = outputMessage; + + for (unsigned int i = 0; i < length; i++) + { + decodeBuffer.decodeValue(nextByte, 8); + + *nextDest++ = (unsigned char) nextByte; + } + + if (*outputMessage == 0x42) + { + setBigEndian(1); + } + else + { + setBigEndian(0); + } + + #ifdef TEST + *logofs << "handleWrite: First request detected.\n" << logofs_flush; + #endif + + // + // Handle the fake authorization cookie. + // + + if (handleAuthorization(outputMessage) < 0) + { + return -1; + } + + firstRequest_ = 0; + + } // End of if (firstRequest_) + + // + // This was previously in a 'else' block. + // Due to the way the first request was + // handled, we could not decode multiple + // messages in the first frame. + // + + { // Start of the decoding block. + + unsigned char outputOpcode; + + unsigned char *outputMessage; + unsigned int outputLength; + + // + // Set when message is found in cache. + // + + int hit; + + while (decodeBuffer.decodeOpcodeValue(outputOpcode, clientCache_ -> opcodeCache, 1)) + { + hit = 0; + + // + // Splits are sent by client proxy outside the + // normal read loop. As we 'insert' splits in + // the real client-server X protocol, we must + // avoid to increment the sequence number or + // our clients would get confused. + // + + if (outputOpcode != opcodeStore_ -> splitData) + { + clientSequence_++; + clientSequence_ &= 0xffff; + + #ifdef DEBUG + *logofs << "handleWrite: Last client sequence number for FD#" + << fd_ << " is " << clientSequence_ << ".\n" + << logofs_flush; + #endif + } + else + { + // + // It's a split, not a normal + // burst of proxy data. + // + + handleSplit(decodeBuffer); + + continue; + } + + #ifdef SUSPEND + + if (clientSequence_ == 1000) + { + cerr << "Warning" << ": Exiting to test the resilience of the agent.\n"; + + sleep(2); + + HandleAbort(); + } + + #endif + + // + // Is differential encoding disabled? + // + + if (control -> RemoteDeltaCompression == 0) + { + int result = handleFastWriteRequest(decodeBuffer, outputOpcode, + outputMessage, outputLength); + if (result < 0) + { + return -1; + } + else if (result > 0) + { + continue; + } + } + + // + // General-purpose temp variables for + // decoding ints and chars. + // + + unsigned int value; + unsigned char cValue; + + #ifdef DEBUG + *logofs << "handleWrite: Going to handle request OPCODE#" + << (unsigned int) outputOpcode << " (" << DumpOpcode(outputOpcode) + << ") for FD#" << fd_ << " sequence " << clientSequence_ + << ".\n" << logofs_flush; + #endif + + switch (outputOpcode) + { + case X_AllocColor: + { + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> colormapCache); + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned char *nextDest = outputMessage + 8; + unsigned int colorData[3]; + + for (unsigned int i = 0; i < 3; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *(clientCache_ -> allocColorRGBCache[i]), 4); + PutUINT(value, nextDest, bigEndian_); + colorData[i] = value; + nextDest += 2; + } + + sequenceQueue_.push(clientSequence_, outputOpcode, + colorData[0], colorData[1], colorData[2]); + } + break; + case X_ReparentWindow: + { + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeValue(value, 16, 11); + PutUINT(value, outputMessage + 12, bigEndian_); + decodeBuffer.decodeValue(value, 16, 11); + PutUINT(value, outputMessage + 14, bigEndian_); + } + break; + case X_ChangeProperty: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_ChangeProperty); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + unsigned char format; + decodeBuffer.decodeCachedValue(format, 8, + clientCache_ -> changePropertyFormatCache); + unsigned int dataLength; + decodeBuffer.decodeValue(dataLength, 32, 6); + outputLength = 24 + RoundUp4(dataLength * (format >> 3)); + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeValue(value, 2); + outputMessage[1] = (unsigned char) value; + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> changePropertyPropertyCache, 9); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> changePropertyTypeCache, 9); + PutULONG(value, outputMessage + 12, bigEndian_); + outputMessage[16] = format; + PutULONG(dataLength, outputMessage + 20, bigEndian_); + unsigned char *nextDest = outputMessage + 24; + + if (format == 8) + { + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeTextData(nextDest, dataLength); + } + else if (format == 32) + { + for (unsigned int i = 0; i < dataLength; i++) + { + decodeBuffer.decodeCachedValue(value, 32, + clientCache_ -> changePropertyData32Cache); + + PutULONG(value, nextDest, bigEndian_); + + nextDest += 4; + } + } + else + { + for (unsigned int i = 0; i < dataLength; i++) + { + decodeBuffer.decodeValue(value, 16); + + PutUINT(value, nextDest, bigEndian_); + + nextDest += 2; + } + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_SendEvent: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_SendEvent); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 44; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeBoolValue(value); + *(outputMessage + 1) = value; + decodeBuffer.decodeBoolValue(value); + if (value) + { + decodeBuffer.decodeBoolValue(value); + } + else + { + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + } + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeCachedValue(value, 32, + clientCache_ -> sendEventMaskCache, 9); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(*(outputMessage + 12), 8, + clientCache_ -> sendEventCodeCache); + decodeBuffer.decodeCachedValue(*(outputMessage + 13), 8, + clientCache_ -> sendEventByteDataCache); + decodeBuffer.decodeValue(value, 16, 4); + clientCache_ -> sendEventLastSequence += value; + clientCache_ -> sendEventLastSequence &= 0xffff; + PutUINT(clientCache_ -> sendEventLastSequence, outputMessage + 14, bigEndian_); + decodeBuffer.decodeCachedValue(value, 32, + clientCache_ -> sendEventIntDataCache); + PutULONG(value, outputMessage + 16, bigEndian_); + + for (unsigned int i = 20; i < 44; i++) + { + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache_ -> sendEventEventCache); + *(outputMessage + i) = cValue; + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_ChangeWindowAttributes: + { + unsigned int numAttrs; + decodeBuffer.decodeValue(numAttrs, 4); + outputLength = 12 + (numAttrs << 2); + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned int bitmask; + decodeBuffer.decodeCachedValue(bitmask, 15, + clientCache_ -> createWindowBitmaskCache); + PutULONG(bitmask, outputMessage + 8, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + unsigned int mask = 0x1; + for (unsigned int i = 0; i < 15; i++) + { + if (bitmask & mask) + { + decodeBuffer.decodeCachedValue(value, 32, + *clientCache_ -> createWindowAttrCache[i]); + PutULONG(value, nextDest, bigEndian_); + nextDest += 4; + } + mask <<= 1; + } + } + break; + case X_ClearArea: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_ClearArea); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeBoolValue(value); + outputMessage[1] = (unsigned char) value; + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned char *nextDest = outputMessage + 8; + for (unsigned int i = 0; i < 4; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> clearAreaGeomCache[i], 8); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_CloseFont: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeValue(value, 29, 5); + clientCache_ -> lastFont += value; + clientCache_ -> lastFont &= 0x1fffffff; + PutULONG(clientCache_ -> lastFont, outputMessage + 4, bigEndian_); + } + break; + case X_ConfigureWindow: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_ConfigureWindow); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 12; + outputMessage = writeBuffer_.addMessage(outputLength); + writeBuffer_.registerPointer(&outputMessage); + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned int bitmask; + decodeBuffer.decodeCachedValue(bitmask, 7, + clientCache_ -> configureWindowBitmaskCache); + PutUINT(bitmask, outputMessage + 8, bigEndian_); + unsigned int mask = 0x1; + for (unsigned int i = 0; i < 7; i++) + { + if (bitmask & mask) + { + unsigned char* nextDest = writeBuffer_.addMessage(4); + outputLength += 4; + decodeBuffer.decodeCachedValue(value, CONFIGUREWINDOW_FIELD_WIDTH[i], + *clientCache_ -> configureWindowAttrCache[i], 8); + PutULONG(value, nextDest, bigEndian_); + nextDest += 4; + } + mask <<= 1; + } + writeBuffer_.unregisterPointer(); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_ConvertSelection: + { + outputLength = 24; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> convertSelectionRequestorCache, 9); + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned char* nextDest = outputMessage + 8; + for (unsigned int i = 0; i < 3; i++) + { + decodeBuffer.decodeCachedValue(value, 29, + *(clientCache_ -> convertSelectionAtomCache[i]), 9); + PutULONG(value, nextDest, bigEndian_); + nextDest += 4; + } + decodeBuffer.decodeValue(value, 32, 4); + clientCache_ -> convertSelectionLastTimestamp += value; + PutULONG(clientCache_ -> convertSelectionLastTimestamp, + nextDest, bigEndian_); + } + break; + case X_CopyArea: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_CopyArea); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 28; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 12, bigEndian_); + unsigned char *nextDest = outputMessage + 16; + for (unsigned int i = 0; i < 6; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> copyAreaGeomCache[i], 8); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_CopyGC: + { + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 23, + clientCache_ -> createGCBitmaskCache); + PutULONG(value, outputMessage + 12, bigEndian_); + } + break; + case X_CopyPlane: + { + outputLength = 32; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 12, bigEndian_); + unsigned char *nextDest = outputMessage + 16; + for (unsigned int i = 0; i < 6; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> copyPlaneGeomCache[i], 8); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + } + decodeBuffer.decodeCachedValue(value, 32, + clientCache_ -> copyPlaneBitPlaneCache, 10); + PutULONG(value, outputMessage + 28, bigEndian_); + } + break; + case X_CreateGC: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_CreateGC); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + writeBuffer_.registerPointer(&outputMessage); + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeNewXidValue(value, clientCache_ -> lastId, + clientCache_ -> lastIdCache, clientCache_ -> gcCache, + clientCache_ -> freeGCCache); + + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned int offset = 8; + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + offset, bigEndian_); + offset += 4; + unsigned int bitmask; + decodeBuffer.decodeCachedValue(bitmask, 23, + clientCache_ -> createGCBitmaskCache); + PutULONG(bitmask, outputMessage + offset, bigEndian_); + unsigned int mask = 0x1; + for (unsigned int i = 0; i < 23; i++) + { + if (bitmask & mask) + { + unsigned char* nextDest = writeBuffer_.addMessage(4); + outputLength += 4; + unsigned int fieldWidth = CREATEGC_FIELD_WIDTH[i]; + if (fieldWidth <= 4) + decodeBuffer.decodeValue(value, fieldWidth); + else + decodeBuffer.decodeCachedValue(value, fieldWidth, + *clientCache_ -> createGCAttrCache[i]); + PutULONG(value, nextDest, bigEndian_); + } + mask <<= 1; + } + writeBuffer_.unregisterPointer(); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_ChangeGC: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_ChangeGC); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 12; + outputMessage = writeBuffer_.addMessage(outputLength); + writeBuffer_.registerPointer(&outputMessage); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned int offset = 8; + unsigned int bitmask; + decodeBuffer.decodeCachedValue(bitmask, 23, + clientCache_ -> createGCBitmaskCache); + PutULONG(bitmask, outputMessage + offset, bigEndian_); + unsigned int mask = 0x1; + for (unsigned int i = 0; i < 23; i++) + { + if (bitmask & mask) + { + unsigned char* nextDest = writeBuffer_.addMessage(4); + outputLength += 4; + unsigned int fieldWidth = CREATEGC_FIELD_WIDTH[i]; + if (fieldWidth <= 4) + decodeBuffer.decodeValue(value, fieldWidth); + else + decodeBuffer.decodeCachedValue(value, fieldWidth, + *clientCache_ -> createGCAttrCache[i]); + PutULONG(value, nextDest, bigEndian_); + } + mask <<= 1; + } + writeBuffer_.unregisterPointer(); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_CreatePixmap: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_CreatePixmap); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + } + break; + case X_CreateWindow: + { + outputLength = 32; + outputMessage = writeBuffer_.addMessage(outputLength); + writeBuffer_.registerPointer(&outputMessage); + decodeBuffer.decodeCachedValue(cValue, 8, clientCache_ -> depthCache); + outputMessage[1] = cValue; + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 8, bigEndian_); + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeNewXidValue(value, clientCache_ -> lastId, + clientCache_ -> lastIdCache, clientCache_ -> windowCache, + clientCache_ -> freeWindowCache); + + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + unsigned int i; + for (i = 0; i < 6; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> createWindowGeomCache[i], 8); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + } + decodeBuffer.decodeCachedValue(value, 29, clientCache_ -> visualCache); + PutULONG(value, outputMessage + 24, bigEndian_); + unsigned int bitmask; + decodeBuffer.decodeCachedValue(bitmask, 15, + clientCache_ -> createWindowBitmaskCache); + PutULONG(bitmask, outputMessage + 28, bigEndian_); + unsigned int mask = 0x1; + for (i = 0; i < 15; i++) + { + if (bitmask & mask) + { + nextDest = writeBuffer_.addMessage(4); + outputLength += 4; + decodeBuffer.decodeCachedValue(value, 32, + *clientCache_ -> createWindowAttrCache[i]); + PutULONG(value, nextDest, bigEndian_); + } + mask <<= 1; + } + writeBuffer_.unregisterPointer(); + } + break; + case X_DeleteProperty: + { + outputLength = 12; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeValue(value, 29, 9); + PutULONG(value, outputMessage + 8, bigEndian_); + } + break; + case X_FillPoly: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_FillPoly); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + unsigned int numPoints; + + // Since ProtoStep10 (#issue 108) + decodeBuffer.decodeCachedValue(numPoints, 16, + clientCache_ -> fillPolyNumPointsCache, 4); + + outputLength = 16 + (numPoints << 2); + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeValue(value, 2); + outputMessage[12] = (unsigned char) value; + unsigned int relativeCoordMode; + decodeBuffer.decodeBoolValue(relativeCoordMode); + outputMessage[13] = (unsigned char) relativeCoordMode; + unsigned char *nextDest = outputMessage + 16; + unsigned int pointIndex = 0; + for (unsigned int i = 0; i < numPoints; i++) + { + if (relativeCoordMode) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> fillPolyXRelCache[pointIndex], 8); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> fillPolyYRelCache[pointIndex], 8); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + } + else + { + unsigned int x, y; + decodeBuffer.decodeBoolValue(value); + if (value) + { + decodeBuffer.decodeValue(value, 3); + x = clientCache_ -> fillPolyRecentX[value]; + y = clientCache_ -> fillPolyRecentY[value]; + } + else + { + decodeBuffer.decodeCachedValue(x, 16, + *clientCache_ -> fillPolyXAbsCache[pointIndex], 8); + decodeBuffer.decodeCachedValue(y, 16, + *clientCache_ -> fillPolyYAbsCache[pointIndex], 8); + clientCache_ -> fillPolyRecentX[clientCache_ -> fillPolyIndex] = x; + clientCache_ -> fillPolyRecentY[clientCache_ -> fillPolyIndex] = y; + clientCache_ -> fillPolyIndex++; + if (clientCache_ -> fillPolyIndex == 8) + clientCache_ -> fillPolyIndex = 0; + } + PutUINT(x, nextDest, bigEndian_); + nextDest += 2; + PutUINT(y, nextDest, bigEndian_); + nextDest += 2; + } + + if (++pointIndex == 10) pointIndex = 0; + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_FreeColors: + { + unsigned int numPixels; + decodeBuffer.decodeValue(numPixels, 16, 4); + outputLength = 12 + (numPixels << 2); + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> colormapCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeValue(value, 32, 4); + PutULONG(value, outputMessage + 8, bigEndian_); + unsigned char* nextDest = outputMessage + 12; + while (numPixels) + { + decodeBuffer.decodeValue(value, 32, 8); + PutULONG(value, nextDest, bigEndian_); + nextDest += 4; + numPixels--; + } + } + break; + case X_FreeCursor: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, clientCache_ -> cursorCache, 9); + PutULONG(value, outputMessage + 4, bigEndian_); + } + break; + case X_FreeGC: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeFreeXidValue(value, clientCache_ -> freeGCCache); + + PutULONG(value, outputMessage + 4, bigEndian_); + } + break; + case X_FreePixmap: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeFreeXidValue(value, clientCache_ -> freeDrawableCache); + + PutULONG(value, outputMessage + 4, bigEndian_); + } + break; + case X_GetAtomName: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeValue(value, 29, 9); + PutULONG(value, outputMessage + 4, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_GetGeometry: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_GetInputFocus: + { + outputLength = 4; + outputMessage = writeBuffer_.addMessage(outputLength); + + sequenceQueue_.push(clientSequence_, outputOpcode, outputOpcode); + } + break; + case X_GetModifierMapping: + { + outputLength = 4; + outputMessage = writeBuffer_.addMessage(outputLength); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_GetKeyboardMapping: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeValue(value, 8); + outputMessage[4] = value; + decodeBuffer.decodeValue(value, 8); + outputMessage[5] = value; + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_GetProperty: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_GetProperty); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + // + // Save a reference to identify the reply. + // + + unsigned int property = GetULONG(outputMessage + 8, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode, property); + + break; + } + + outputLength = 24; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeBoolValue(value); + outputMessage[1] = (unsigned char) value; + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned int property; + decodeBuffer.decodeValue(property, 29, 9); + PutULONG(property, outputMessage + 8, bigEndian_); + decodeBuffer.decodeValue(value, 29, 9); + PutULONG(value, outputMessage + 12, bigEndian_); + decodeBuffer.decodeValue(value, 32, 2); + PutULONG(value, outputMessage + 16, bigEndian_); + decodeBuffer.decodeValue(value, 32, 8); + PutULONG(value, outputMessage + 20, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode, property); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_GetSelectionOwner: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> getSelectionOwnerSelectionCache, 9); + PutULONG(value, outputMessage + 4, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_GrabButton: + case X_GrabPointer: + { + outputLength = 24; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeBoolValue(value); + outputMessage[1] = (unsigned char) value; + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> grabButtonEventMaskCache); + PutUINT(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeBoolValue(value); + outputMessage[10] = (unsigned char) value; + decodeBuffer.decodeBoolValue(value); + outputMessage[11] = (unsigned char) value; + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> grabButtonConfineCache, 9); + PutULONG(value, outputMessage + 12, bigEndian_); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> cursorCache, 9); + PutULONG(value, outputMessage + 16, bigEndian_); + if (outputOpcode == X_GrabButton) + { + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache_ -> grabButtonButtonCache); + outputMessage[20] = cValue; + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> grabButtonModifierCache); + PutUINT(value, outputMessage + 22, bigEndian_); + } + else + { + decodeBuffer.decodeValue(value, 32, 4); + clientCache_ -> grabKeyboardLastTimestamp += value; + PutULONG(clientCache_ -> grabKeyboardLastTimestamp, + outputMessage + 20, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + } + break; + case X_GrabKeyboard: + { + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeBoolValue(value); + outputMessage[1] = (unsigned char) value; + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeValue(value, 32, 4); + clientCache_ -> grabKeyboardLastTimestamp += value; + PutULONG(clientCache_ -> grabKeyboardLastTimestamp, outputMessage + 8, + bigEndian_); + decodeBuffer.decodeBoolValue(value); + outputMessage[12] = (unsigned char) value; + decodeBuffer.decodeBoolValue(value); + outputMessage[13] = (unsigned char) value; + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_GrabServer: + case X_UngrabServer: + case X_NoOperation: + { + #ifdef DEBUG + *logofs << "handleWrite: Managing (probably tainted) X_NoOperation request for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + outputLength = 4; + outputMessage = writeBuffer_.addMessage(outputLength); + } + break; + case X_PolyText8: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyText8); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> polyTextCacheX); + clientCache_ -> polyTextLastX += value; + clientCache_ -> polyTextLastX &= 0xffff; + PutUINT(clientCache_ -> polyTextLastX, outputMessage + 12, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> polyTextCacheY); + clientCache_ -> polyTextLastY += value; + clientCache_ -> polyTextLastY &= 0xffff; + PutUINT(clientCache_ -> polyTextLastY, outputMessage + 14, bigEndian_); + unsigned int addedLength = 0; + writeBuffer_.registerPointer(&outputMessage); + for (;;) + { + decodeBuffer.decodeBoolValue(value); + if (!value) + break; + unsigned int textLength; + decodeBuffer.decodeValue(textLength, 8); + if (textLength == 255) + { + addedLength += 5; + unsigned char *nextSegment = writeBuffer_.addMessage(5); + *nextSegment = (unsigned char) textLength; + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> polyTextFontCache); + PutULONG(value, nextSegment + 1, 1); + } + else + { + addedLength += (textLength + 2); + unsigned char *nextSegment = + writeBuffer_.addMessage(textLength + 2); + *nextSegment = (unsigned char) textLength; + unsigned char *nextDest = nextSegment + 1; + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache_ -> polyTextDeltaCache); + *nextDest++ = cValue; + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeTextData(nextDest, textLength); + + nextDest += textLength; + } + } + outputLength += addedLength; + unsigned int mod4 = (addedLength & 0x3); + if (mod4) + { + unsigned int extra = 4 - mod4; + unsigned char *nextDest = writeBuffer_.addMessage(extra); + for (unsigned int i = 0; i < extra; i++) + *nextDest++ = 0; + outputLength += extra; + } + writeBuffer_.unregisterPointer(); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_PolyText16: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyText16); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> polyTextCacheX); + clientCache_ -> polyTextLastX += value; + clientCache_ -> polyTextLastX &= 0xffff; + PutUINT(clientCache_ -> polyTextLastX, outputMessage + 12, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> polyTextCacheY); + clientCache_ -> polyTextLastY += value; + clientCache_ -> polyTextLastY &= 0xffff; + PutUINT(clientCache_ -> polyTextLastY, outputMessage + 14, bigEndian_); + unsigned int addedLength = 0; + writeBuffer_.registerPointer(&outputMessage); + for (;;) + { + decodeBuffer.decodeBoolValue(value); + if (!value) + break; + unsigned int textLength; + decodeBuffer.decodeValue(textLength, 8); + if (textLength == 255) + { + addedLength += 5; + unsigned char *nextSegment = writeBuffer_.addMessage(5); + *nextSegment = (unsigned char) textLength; + decodeBuffer.decodeCachedValue(value, 29, clientCache_ -> polyTextFontCache); + PutULONG(value, nextSegment + 1, 1); + } + else + { + addedLength += (textLength * 2 + 2); + unsigned char *nextSegment = + writeBuffer_.addMessage(textLength * 2 + 2); + *nextSegment = (unsigned char) textLength; + unsigned char *nextDest = nextSegment + 1; + decodeBuffer.decodeCachedValue(cValue, 8, clientCache_ -> polyTextDeltaCache); + *nextDest++ = cValue; + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeTextData(nextDest, textLength * 2); + + nextDest += textLength * 2; + } + } + outputLength += addedLength; + + unsigned int mod4 = (addedLength & 0x3); + if (mod4) + { + unsigned int extra = 4 - mod4; + unsigned char *nextDest = writeBuffer_.addMessage(extra); + for (unsigned int i = 0; i < extra; i++) + *nextDest++ = 0; + outputLength += extra; + } + writeBuffer_.unregisterPointer(); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_ImageText8: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_ImageText8); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + unsigned int textLength; + decodeBuffer.decodeCachedValue(textLength, 8, + clientCache_ -> imageTextLengthCache, 4); + outputLength = 16 + RoundUp4(textLength); + outputMessage = writeBuffer_.addMessage(outputLength); + outputMessage[1] = (unsigned char) textLength; + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> imageTextCacheX); + clientCache_ -> imageTextLastX += value; + clientCache_ -> imageTextLastX &= 0xffff; + PutUINT(clientCache_ -> imageTextLastX, outputMessage + 12, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> imageTextCacheY); + clientCache_ -> imageTextLastY += value; + clientCache_ -> imageTextLastY &= 0xffff; + PutUINT(clientCache_ -> imageTextLastY, outputMessage + 14, bigEndian_); + unsigned char *nextDest = outputMessage + 16; + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeTextData(nextDest, textLength); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_ImageText16: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_ImageText16); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + unsigned int textLength; + decodeBuffer.decodeCachedValue(textLength, 8, + clientCache_ -> imageTextLengthCache, 4); + outputLength = 16 + RoundUp4(textLength * 2); + outputMessage = writeBuffer_.addMessage(outputLength); + outputMessage[1] = (unsigned char) textLength; + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> imageTextCacheX); + clientCache_ -> imageTextLastX += value; + clientCache_ -> imageTextLastX &= 0xffff; + PutUINT(clientCache_ -> imageTextLastX, outputMessage + 12, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> imageTextCacheY); + clientCache_ -> imageTextLastY += value; + clientCache_ -> imageTextLastY &= 0xffff; + PutUINT(clientCache_ -> imageTextLastY, outputMessage + 14, bigEndian_); + unsigned char *nextDest = outputMessage + 16; + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeTextData(nextDest, textLength * 2); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_InternAtom: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_InternAtom); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + sequenceQueue_.push(clientSequence_, outputOpcode); + + break; + } + + unsigned int nameLength; + decodeBuffer.decodeValue(nameLength, 16, 6); + outputLength = RoundUp4(nameLength) + 8; + outputMessage = writeBuffer_.addMessage(outputLength); + PutUINT(nameLength, outputMessage + 4, bigEndian_); + decodeBuffer.decodeBoolValue(value); + outputMessage[1] = (unsigned char) value; + unsigned char *nextDest = outputMessage + 8; + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeTextData(nextDest, nameLength); + + sequenceQueue_.push(clientSequence_, outputOpcode); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_ListExtensions: + { + outputLength = 4; + outputMessage = writeBuffer_.addMessage(outputLength); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_ListFonts: + { + unsigned int textLength; + decodeBuffer.decodeValue(textLength, 16, 6); + outputLength = 8 + RoundUp4(textLength); + outputMessage = writeBuffer_.addMessage(outputLength); + PutUINT(textLength, outputMessage + 6, bigEndian_); + decodeBuffer.decodeValue(value, 16, 6); + PutUINT(value, outputMessage + 4, bigEndian_); + unsigned char* nextDest = outputMessage + 8; + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeTextData(nextDest, textLength); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_LookupColor: + case X_AllocNamedColor: + { + unsigned int textLength; + decodeBuffer.decodeValue(textLength, 16, 6); + outputLength = 12 + RoundUp4(textLength); + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> colormapCache); + PutULONG(value, outputMessage + 4, bigEndian_); + PutUINT(textLength, outputMessage + 8, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeTextData(nextDest, textLength); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_MapWindow: + case X_UnmapWindow: + case X_MapSubwindows: + case X_GetWindowAttributes: + case X_DestroyWindow: + case X_DestroySubwindows: + case X_QueryPointer: + case X_QueryTree: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + + if (outputOpcode == X_DestroyWindow) // Since ProtoStep7 (#issue 108) + { + decodeBuffer.decodeFreeXidValue(value, clientCache_ -> freeWindowCache); + } + else + { + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + } + + PutULONG(value, outputMessage + 4, bigEndian_); + if (outputOpcode == X_QueryPointer || + outputOpcode == X_GetWindowAttributes || + outputOpcode == X_QueryTree) + { + sequenceQueue_.push(clientSequence_, outputOpcode); + } + } + break; + case X_OpenFont: + { + unsigned int nameLength; + decodeBuffer.decodeValue(nameLength, 16, 7); + outputLength = RoundUp4(12 + nameLength); + outputMessage = writeBuffer_.addMessage(outputLength); + PutUINT(nameLength, outputMessage + 8, bigEndian_); + decodeBuffer.decodeValue(value, 29, 5); + clientCache_ -> lastFont += value; + clientCache_ -> lastFont &= 0x1fffffff; + PutULONG(clientCache_ -> lastFont, outputMessage + 4, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeTextData(nextDest, nameLength); + } + break; + case X_PolyFillRectangle: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyFillRectangle); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 12; + outputMessage = writeBuffer_.addMessage(outputLength); + writeBuffer_.registerPointer(&outputMessage); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + + unsigned int index = 0; + unsigned int lastX = 0, lastY = 0, lastWidth = 0, lastHeight = 0; + unsigned int numRectangles = 0; + + for (;;) + { + outputLength += 8; + writeBuffer_.addMessage(8); + unsigned char *nextDest = outputMessage + 12 + + (numRectangles << 3); + numRectangles++; + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillRectangleCacheX[index], 8); + value += lastX; + PutUINT(value, nextDest, bigEndian_); + lastX = value; + nextDest += 2; + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillRectangleCacheY[index], 8); + value += lastY; + PutUINT(value, nextDest, bigEndian_); + lastY = value; + nextDest += 2; + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillRectangleCacheWidth[index], 8); + value += lastWidth; + PutUINT(value, nextDest, bigEndian_); + lastWidth = value; + nextDest += 2; + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillRectangleCacheHeight[index], 8); + value += lastHeight; + PutUINT(value, nextDest, bigEndian_); + lastHeight = value; + nextDest += 2; + + if (++index == 4) index = 0; + + decodeBuffer.decodeBoolValue(value); + + if (!value) break; + } + writeBuffer_.unregisterPointer(); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_PolyFillArc: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyFillArc); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 12; + outputMessage = writeBuffer_.addMessage(outputLength); + writeBuffer_.registerPointer(&outputMessage); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + + unsigned int index = 0; + unsigned int lastX = 0, lastY = 0, + lastWidth = 0, lastHeight = 0, + lastAngle1 = 0, lastAngle2 = 0; + + unsigned int numArcs = 0; + + for (;;) + { + outputLength += 12; + writeBuffer_.addMessage(12); + + unsigned char *nextDest = outputMessage + 12 + + (numArcs * 12); + numArcs++; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillArcCacheX[index], 8); + value += lastX; + PutUINT(value, nextDest, bigEndian_); + lastX = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillArcCacheY[index], 8); + value += lastY; + PutUINT(value, nextDest, bigEndian_); + lastY = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillArcCacheWidth[index], 8); + value += lastWidth; + PutUINT(value, nextDest, bigEndian_); + lastWidth = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillArcCacheHeight[index], 8); + value += lastHeight; + PutUINT(value, nextDest, bigEndian_); + lastHeight = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillArcCacheAngle1[index], 8); + value += lastAngle1; + PutUINT(value, nextDest, bigEndian_); + lastAngle1 = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillArcCacheAngle2[index], 8); + value += lastAngle2; + PutUINT(value, nextDest, bigEndian_); + lastAngle2 = value; + nextDest += 2; + + if (++index == 2) index = 0; + + decodeBuffer.decodeBoolValue(value); + + if (!value) break; + } + writeBuffer_.unregisterPointer(); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_PolyArc: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyArc); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 12; + outputMessage = writeBuffer_.addMessage(outputLength); + writeBuffer_.registerPointer(&outputMessage); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + + unsigned int index = 0; + unsigned int lastX = 0, lastY = 0, + lastWidth = 0, lastHeight = 0, + lastAngle1 = 0, lastAngle2 = 0; + + unsigned int numArcs = 0; + + for (;;) + { + outputLength += 12; + writeBuffer_.addMessage(12); + + unsigned char *nextDest = outputMessage + 12 + + (numArcs * 12); + numArcs++; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyArcCacheX[index], 8); + value += lastX; + PutUINT(value, nextDest, bigEndian_); + lastX = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyArcCacheY[index], 8); + value += lastY; + PutUINT(value, nextDest, bigEndian_); + lastY = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyArcCacheWidth[index], 8); + value += lastWidth; + PutUINT(value, nextDest, bigEndian_); + lastWidth = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyArcCacheHeight[index], 8); + value += lastHeight; + PutUINT(value, nextDest, bigEndian_); + lastHeight = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyArcCacheAngle1[index], 8); + value += lastAngle1; + PutUINT(value, nextDest, bigEndian_); + lastAngle1 = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyArcCacheAngle2[index], 8); + value += lastAngle2; + PutUINT(value, nextDest, bigEndian_); + lastAngle2 = value; + nextDest += 2; + + if (++index == 2) index = 0; + + decodeBuffer.decodeBoolValue(value); + + if (!value) break; + } + writeBuffer_.unregisterPointer(); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_PolyPoint: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyPoint); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + unsigned int numPoints; + decodeBuffer.decodeValue(numPoints, 16, 4); + outputLength = (numPoints << 2) + 12; + outputMessage = writeBuffer_.addMessage(outputLength); + unsigned int relativeCoordMode; + decodeBuffer.decodeBoolValue(relativeCoordMode); + outputMessage[1] = (unsigned char) relativeCoordMode; + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + + unsigned int index = 0; + unsigned int lastX = 0, lastY = 0; + + for (unsigned int i = 0; i < numPoints; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyPointCacheX[index], 8); + lastX += value; + PutUINT(lastX, nextDest, bigEndian_); + nextDest += 2; + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyPointCacheY[index], 8); + lastY += value; + PutUINT(lastY, nextDest, bigEndian_); + nextDest += 2; + + if (++index == 2) index = 0; + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_PolyLine: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyLine); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + unsigned int numPoints; + decodeBuffer.decodeValue(numPoints, 16, 4); + outputLength = (numPoints << 2) + 12; + outputMessage = writeBuffer_.addMessage(outputLength); + unsigned int relativeCoordMode; + decodeBuffer.decodeBoolValue(relativeCoordMode); + outputMessage[1] = (unsigned char) relativeCoordMode; + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + + unsigned int index = 0; + unsigned int lastX = 0, lastY = 0; + + for (unsigned int i = 0; i < numPoints; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyLineCacheX[index], 8); + lastX += value; + PutUINT(lastX, nextDest, bigEndian_); + nextDest += 2; + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyLineCacheY[index], 8); + lastY += value; + PutUINT(lastY, nextDest, bigEndian_); + nextDest += 2; + + if (++index == 2) index = 0; + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_PolyRectangle: + { + unsigned int numRectangles; + decodeBuffer.decodeValue(numRectangles, 16, 3); + outputLength = (numRectangles << 3) + 12; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + for (unsigned int i = 0; i < numRectangles; i++) + for (unsigned int k = 0; k < 4; k++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyRectangleGeomCache[k], 8); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + } + } + break; + case X_PolySegment: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolySegment); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + unsigned int numSegments; + decodeBuffer.decodeValue(numSegments, 16, 4); + outputLength = (numSegments << 3) + 12; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + + for (numSegments *= 2; numSegments; numSegments--) + { + unsigned int index; + decodeBuffer.decodeBoolValue(index); + unsigned int x; + decodeBuffer.decodeCachedValue(x, 16, + clientCache_ -> polySegmentCacheX, 6); + x += clientCache_ -> polySegmentLastX[index]; + PutUINT(x, nextDest, bigEndian_); + nextDest += 2; + + unsigned int y; + decodeBuffer.decodeCachedValue(y, 16, + clientCache_ -> polySegmentCacheY, 6); + y += clientCache_ -> polySegmentLastY[index]; + PutUINT(y, nextDest, bigEndian_); + nextDest += 2; + + clientCache_ -> polySegmentLastX[clientCache_ -> polySegmentCacheIndex] = x; + clientCache_ -> polySegmentLastY[clientCache_ -> polySegmentCacheIndex] = y; + + if (clientCache_ -> polySegmentCacheIndex == 1) + clientCache_ -> polySegmentCacheIndex = 0; + else + clientCache_ -> polySegmentCacheIndex = 1; + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_PutImage: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PutImage); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + + if (outputOpcode == X_PutImage) + { + handleImage(outputOpcode, outputMessage, outputLength); + } + } + break; + case X_QueryBestSize: + { + outputLength = 12; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeValue(value, 2); + outputMessage[1] = (unsigned char)value; + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeValue(value, 16, 8); + PutUINT(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeValue(value, 16, 8); + PutUINT(value, outputMessage + 10, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_QueryColors: + { + // Differential or plain data compression? + decodeBuffer.decodeBoolValue(value); + + if (value) + { + unsigned int numColors; + decodeBuffer.decodeValue(numColors, 16, 5); + outputLength = (numColors << 2) + 8; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> colormapCache); + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned char *nextDest = outputMessage + 8; + unsigned int predictedPixel = clientCache_ -> queryColorsLastPixel; + for (unsigned int i = 0; i < numColors; i++) + { + unsigned int pixel; + decodeBuffer.decodeBoolValue(value); + if (value) + pixel = predictedPixel; + else + decodeBuffer.decodeValue(pixel, 32, 9); + PutULONG(pixel, nextDest, bigEndian_); + if (i == 0) + clientCache_ -> queryColorsLastPixel = pixel; + predictedPixel = pixel + 1; + nextDest += 4; + } + } + else + { + // Request length. + unsigned int requestLength; + decodeBuffer.decodeValue(requestLength, 16, 10); + outputLength = (requestLength << 2); + outputMessage = writeBuffer_.addMessage(outputLength); + + const unsigned char *compressedData = NULL; + unsigned int compressedDataSize = 0; + + int decompressed = handleDecompress(decodeBuffer, outputOpcode, 4, + outputMessage, outputLength, compressedData, + compressedDataSize); + if (decompressed < 0) + { + return -1; + } + } + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_QueryExtension: + { + unsigned int nameLength; + decodeBuffer.decodeValue(nameLength, 16, 6); + outputLength = 8 + RoundUp4(nameLength); + outputMessage = writeBuffer_.addMessage(outputLength); + PutUINT(nameLength, outputMessage + 4, bigEndian_); + unsigned char *nextDest = outputMessage + 8; + for (unsigned int i = 0; i < nameLength; i++) + { + decodeBuffer.decodeValue(value, 8); + *nextDest++ = (unsigned char) value; + } + + unsigned int hide = 0; + + #ifdef HIDE_MIT_SHM_EXTENSION + + if (!strncmp((char *) outputMessage + 8, "MIT-SHM", 7)) + { + #ifdef TEST + *logofs << "handleWrite: Going to hide MIT-SHM extension in reply.\n" + << logofs_flush; + #endif + + hide = 1; + } + + #endif + + #ifdef HIDE_BIG_REQUESTS_EXTENSION + + if (!strncmp((char *) outputMessage + 8, "BIG-REQUESTS", 12)) + { + #ifdef TEST + *logofs << "handleWrite: Going to hide BIG-REQUESTS extension in reply.\n" + << logofs_flush; + #endif + + hide = 1; + } + + #endif + + #ifdef HIDE_XKEYBOARD_EXTENSION + + else if (!strncmp((char *) outputMessage + 8, "XKEYBOARD", 9)) + { + #ifdef TEST + *logofs << "handleWrite: Going to hide XKEYBOARD extension in reply.\n" + << logofs_flush; + #endif + + hide = 1; + } + + #endif + + #ifdef HIDE_XFree86_Bigfont_EXTENSION + + else if (!strncmp((char *) outputMessage + 8, "XFree86-Bigfont", 15)) + { + #ifdef TEST + *logofs << "handleWrite: Going to hide XFree86-Bigfont extension in reply.\n" + << logofs_flush; + #endif + + hide = 1; + } + + #endif + + // + // This is if you want to experiment disabling SHAPE extensions. + // + + #ifdef HIDE_SHAPE_EXTENSION + + if (!strncmp((char *) outputMessage + 8, "SHAPE", 5)) + { + #ifdef DEBUG + *logofs << "handleWrite: Going to hide SHAPE extension in reply.\n" + << logofs_flush; + #endif + + hide = 1; + } + + #endif + + // + // Check if user disabled RENDER extension. + // + + if (control -> HideRender == 1 && + strncmp((char *) outputMessage + 8, "RENDER", 6) == 0) + { + #ifdef TEST + *logofs << "handleWrite: Going to hide RENDER extension in reply.\n" + << logofs_flush; + #endif + + hide = 1; + } + + unsigned int extension = 0; + + if (strncmp((char *) outputMessage + 8, "SHAPE", 5) == 0) + { + extension = X_NXInternalShapeExtension; + } + else if (strncmp((char *) outputMessage + 8, "RENDER", 6) == 0) + { + extension = X_NXInternalRenderExtension; + } + + sequenceQueue_.push(clientSequence_, outputOpcode, + outputOpcode, hide, extension); + } + break; + case X_QueryFont: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeValue(value, 29, 5); + clientCache_ -> lastFont += value; + clientCache_ -> lastFont &= 0x1fffffff; + PutULONG(clientCache_ -> lastFont, outputMessage + 4, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_SetClipRectangles: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_SetClipRectangles); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + unsigned int numRectangles; + + // Since ProtoStep9 (#issue 108) + decodeBuffer.decodeValue(numRectangles, 15, 4); + + outputLength = (numRectangles << 3) + 12; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeValue(value, 2); + outputMessage[1] = (unsigned char) value; + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> setClipRectanglesXCache, 8); + PutUINT(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> setClipRectanglesYCache, 8); + PutUINT(value, outputMessage + 10, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + for (unsigned int i = 0; i < numRectangles; i++) + { + for (unsigned int k = 0; k < 4; k++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> setClipRectanglesGeomCache[k], 8); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + } + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_SetDashes: + { + unsigned int numDashes; + decodeBuffer.decodeCachedValue(numDashes, 16, + clientCache_ -> setDashesLengthCache, 5); + outputLength = 12 + RoundUp4(numDashes); + outputMessage = writeBuffer_.addMessage(outputLength); + PutUINT(numDashes, outputMessage + 10, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> setDashesOffsetCache, 5); + PutUINT(value, outputMessage + 8, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + for (unsigned int i = 0; i < numDashes; i++) + { + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache_ -> setDashesDashCache_[i & 1], 5); + *nextDest++ = cValue; + } + } + break; + case X_SetSelectionOwner: + { + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> setSelectionOwnerCache, 9); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> getSelectionOwnerSelectionCache, 9); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 32, + clientCache_ -> setSelectionOwnerTimestampCache, 9); + PutULONG(value, outputMessage + 12, bigEndian_); + } + break; + case X_TranslateCoords: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_TranslateCoords); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + sequenceQueue_.push(clientSequence_, outputOpcode); + + break; + } + + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> translateCoordsSrcCache, 9); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> translateCoordsDstCache, 9); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> translateCoordsXCache, 8); + PutUINT(value, outputMessage + 12, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> translateCoordsYCache, 8); + PutUINT(value, outputMessage + 14, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_GetImage: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_GetImage); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + sequenceQueue_.push(clientSequence_, outputOpcode); + + break; + } + + outputLength = 20; + outputMessage = writeBuffer_.addMessage(outputLength); + // Format. + unsigned int format; + decodeBuffer.decodeValue(format, 2); + outputMessage[1] = (unsigned char) format; + // Drawable. + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + // X. + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> putImageXCache, 8); + clientCache_ -> putImageLastX += value; + clientCache_ -> putImageLastX &= 0xffff; + PutUINT(clientCache_ -> putImageLastX, outputMessage + 8, bigEndian_); + // Y. + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> putImageYCache, 8); + clientCache_ -> putImageLastY += value; + clientCache_ -> putImageLastY &= 0xffff; + PutUINT(clientCache_ -> putImageLastY, outputMessage + 10, bigEndian_); + // Width. + unsigned int width; + decodeBuffer.decodeCachedValue(width, 16, + clientCache_ -> putImageWidthCache, 8); + PutUINT(width, outputMessage + 12, bigEndian_); + // Height. + unsigned int height; + decodeBuffer.decodeCachedValue(height, 16, + clientCache_ -> putImageHeightCache, 8); + PutUINT(height, outputMessage + 14, bigEndian_); + // Plane mask. + decodeBuffer.decodeCachedValue(value, 32, + clientCache_ -> getImagePlaneMaskCache, 5); + PutULONG(value, outputMessage + 16, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_GetPointerMapping: + { + outputLength = 4; + outputMessage = writeBuffer_.addMessage(outputLength); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_GetKeyboardControl: + { + outputLength = 4; + outputMessage = writeBuffer_.addMessage(outputLength); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + default: + { + if (outputOpcode == opcodeStore_ -> renderExtension) + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXInternalRenderExtension); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> shapeExtension) + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXInternalShapeExtension); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> putPackedImage) + { + #ifdef DEBUG + *logofs << "handleWrite: Decoding packed image request for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXPutPackedImage); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + + if (outputOpcode == opcodeStore_ -> putPackedImage) + { + handleImage(outputOpcode, outputMessage, outputLength); + } + } + else if (outputOpcode == opcodeStore_ -> setUnpackColormap) + { + #ifdef DEBUG + *logofs << "handleWrite: Decoding set unpack colormap request " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXSetUnpackColormap); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + // + // Message could have been split. + // + + if (outputOpcode == opcodeStore_ -> setUnpackColormap) + { + handleColormap(outputOpcode, outputMessage, outputLength); + } + } + else if (outputOpcode == opcodeStore_ -> setUnpackAlpha) + { + #ifdef DEBUG + *logofs << "handleWrite: Decoding unpack alpha request for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXSetUnpackAlpha); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + // + // Message could have been split. + // + + if (outputOpcode == opcodeStore_ -> setUnpackAlpha) + { + handleAlpha(outputOpcode, outputMessage, outputLength); + } + } + else if (outputOpcode == opcodeStore_ -> setUnpackGeometry) + { + #ifdef DEBUG + *logofs << "handleWrite: Decoding set unpack geometry request " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXSetUnpackGeometry); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + + handleGeometry(outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> startSplit) + { + handleStartSplitRequest(decodeBuffer, outputOpcode, + outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> endSplit) + { + handleEndSplitRequest(decodeBuffer, outputOpcode, + outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> commitSplit) + { + int result = handleCommitSplitRequest(decodeBuffer, outputOpcode, + outputMessage, outputLength); + + // + // Check if message has been successfully + // extracted from the split store. In this + // case post-process it in the usual way. + // + + if (result > 0) + { + if (outputOpcode == opcodeStore_ -> putPackedImage || + outputOpcode == X_PutImage) + { + handleImage(outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> setUnpackColormap) + { + handleColormap(outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> setUnpackAlpha) + { + handleAlpha(outputOpcode, outputMessage, outputLength); + } + } + else if (result < 0) + { + return -1; + } + } + else if (outputOpcode == opcodeStore_ -> abortSplit) + { + handleAbortSplitRequest(decodeBuffer, outputOpcode, + outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> finishSplit) + { + #ifdef DEBUG + *logofs << "handleWrite: Decoding finish split request " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache_ -> resourceCache); + + handleNullRequest(outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> freeSplit) + { + #ifdef DEBUG + *logofs << "handleWrite: Decoding free split request " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache_ -> resourceCache); + + handleNullRequest(outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> freeUnpack) + { + #ifdef DEBUG + *logofs << "handleWrite: Decoding free unpack request " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache_ -> resourceCache); + + #ifdef DEBUG + *logofs << "handleWrite: Freeing unpack state for resource " + << (unsigned int) cValue << ".\n" << logofs_flush; + #endif + + handleUnpackStateRemove(cValue); + + handleNullRequest(outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> setExposeParameters) + { + // + // Send expose events according to agent's wish. + // + + decodeBuffer.decodeBoolValue(enableExpose_); + decodeBuffer.decodeBoolValue(enableGraphicsExpose_); + decodeBuffer.decodeBoolValue(enableNoExpose_); + + handleNullRequest(outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> getUnpackParameters) + { + // + // Client proxy needs the list of supported + // unpack methods. We would need an encode + // buffer, but this is in proxy, not here in + // channel. + // + + #ifdef TEST + *logofs << "handleWrite: Sending X_GetInputFocus request for FD#" + << fd_ << " due to OPCODE#" << (unsigned int) outputOpcode + << ".\n" << logofs_flush; + #endif + + outputOpcode = X_GetInputFocus; + + outputLength = 4; + outputMessage = writeBuffer_.addMessage(outputLength); + + sequenceQueue_.push(clientSequence_, outputOpcode, + opcodeStore_ -> getUnpackParameters); + } + else if (outputOpcode == opcodeStore_ -> getControlParameters || + outputOpcode == opcodeStore_ -> getCleanupParameters || + outputOpcode == opcodeStore_ -> getImageParameters) + { + handleNullRequest(outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> getShmemParameters) + { + if (handleShmemRequest(decodeBuffer, outputOpcode, + outputMessage, outputLength) < 0) + { + return -1; + } + } + else if (outputOpcode == opcodeStore_ -> setCacheParameters) + { + if (handleCacheRequest(decodeBuffer, outputOpcode, + outputMessage, outputLength) < 0) + { + return -1; + } + } + else if (outputOpcode == opcodeStore_ -> getFontParameters) + { + if (handleFontRequest(decodeBuffer, outputOpcode, + outputMessage, outputLength) < 0) + { + return -1; + } + } + else + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXInternalGenericRequest); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + } + } + } // End of switch on opcode. + + // + // TODO: at the moment the variable hit was being set + // but not used, so to avoid the corresponding warning + // this logging block has been added. + // This code will probably be optimized away when none + // of the defines is set, but if there is no additional + // use for the hit variable in the future, then maybe + // it could be removed completely. + // + + if (hit) + { + #if defined(TEST) || defined(OPCODES) + *logofs << "handleWrite: Cached flag enabled in handled request.\n" + << logofs_flush; + #endif + } + + // + // A packed image request can generate more than just + // a single X_PutImage. Write buffer is handled inside + // handleUnpack(). Cannot simply assume that the final + // opcode and size must be put at the buffer offset as + // as buffer could have been grown or could have been + // replaced by a scratch buffer. The same is true in + // the case of a shared memory image. + // + + if (outputOpcode != 0) + { + // + // Commit opcode and size to the buffer. + // + + *outputMessage = (unsigned char) outputOpcode; + + PutUINT(outputLength >> 2, outputMessage + 2, bigEndian_); + + #if defined(TEST) || defined(OPCODES) + *logofs << "handleWrite: Handled request OPCODE#" + << (unsigned int) outputOpcode << " (" + << DumpOpcode(outputOpcode) << ") for FD#" + << fd_ << " sequence " << clientSequence_ + << ". " << outputLength << " bytes out.\n" + << logofs_flush; + #endif + } + #if defined(TEST) || defined(OPCODES) + else + { + // + // In case of shared memory images the log doesn't + // reflect the actual opcode of the request that is + // going to be written. It would be possible to find + // the opcode of the original request received from + // the remote proxy in member imageState_ -> opcode, + // but we have probably already deleted the struct. + // + + *logofs << "handleWrite: Handled image request for FD#" + << fd_ << " new sequence " << clientSequence_ + << ". " << outputLength << " bytes out.\n" + << logofs_flush; + } + #endif + + // + // Check if we produced enough data. We need to + // decode all the proxy messages or the decode + // buffer will be left in an inconsistent state, + // so we just update the finish flag in case of + // failure. + // + + handleFlush(flush_if_needed); + + } // End of while (decodeBuffer.decodeOpcodeValue(outputOpcode, 8, ... + + } // End of the decoding block. + + // + // Write any remaining data to the X connection. + // + + if (handleFlush(flush_if_any) < 0) + { + return -1; + } + + // + // Reset offset at which we read the + // last event looking for the shared + // memory completion. + // + + if (shmemState_ != NULL) + { + shmemState_ -> checked = 0; + } + + return 1; +} + +// +// End of handleWrite(). +// + +// +// Other members. +// + +int ServerChannel::handleSplit(DecodeBuffer &decodeBuffer, MessageStore *store, + T_store_action action, int position, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + // Since ProtoStep7 (#issue 108) + splitState_.current = splitState_.resource; + + handleSplitStoreAlloc(&splitResources_, splitState_.current); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Message OPCODE#" + << (unsigned int) store -> opcode() << " of size " << size + << " [split] with resource " << splitState_.current + << " position " << position << " and action [" + << DumpAction(action) << "] at " << strMsTimestamp() + << ".\n" << logofs_flush; + #endif + + // + // Get the MD5 of the message being + // split. + // + + T_checksum checksum = NULL; + + if (action != IS_HIT) + { + handleSplitChecksum(decodeBuffer, checksum); + } + + // + // The method must abort the connection + // if it can't allocate the split. + // + + Split *splitMessage = clientStore_ -> getSplitStore(splitState_.current) -> + add(store, splitState_.current, position, + action, checksum, buffer, size); + + // + // If the encoding side didn't provide + // a checksum, then don't send the split + // report. + // + + if (checksum == NULL) + { + if (action == IS_HIT) + { + splitMessage -> setState(split_loaded); + } + else + { + splitMessage -> setState(split_missed); + } + + #if defined(TEST) || defined(SPLIT) + + *logofs << "handleSplit: SPLIT! There are " << clientStore_ -> + getSplitTotalSize() << " messages and " << clientStore_ -> + getSplitTotalStorageSize() << " bytes to send in " + << "the split stores.\n" << logofs_flush; + + clientStore_ -> dumpSplitStore(splitState_.current); + + #endif + + return 1; + } + + delete [] checksum; + + // + // Tell the split store if it must use + // the disk cache to retrieve and save + // the message. + // + + splitMessage -> setPolicy(splitState_.load, splitState_.save); + + // + // Try to locate the message on disk. + // + + if (clientStore_ -> getSplitStore(splitState_.current) -> + load(splitMessage) == 1) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Loaded the message " + << "from the image cache.\n" << logofs_flush; + #endif + + splitMessage -> setState(split_loaded); + } + else + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: WARNING! SPLIT! Can't find the message " + << "in the image cache.\n" << logofs_flush; + #endif + + splitMessage -> setState(split_missed); + } + + #if defined(TEST) || defined(SPLIT) + + T_timestamp startTs = getTimestamp(); + + *logofs << "handleSplit: SPLIT! Encoding abort " + << "split events for FD#" << fd_ << " at " + << strMsTimestamp() << ".\n" << logofs_flush; + #endif + + if (proxy -> handleAsyncSplit(fd_, splitMessage) < 0) + { + return -1; + } + + // + // Send the encoded data immediately. We + // want the abort split message to reach + // the remote proxy as soon as possible. + // + + if (proxy -> handleAsyncFlush() < 0) + { + return -1; + } + + #if defined(TEST) || defined(SPLIT) + + *logofs << "handleSplit: SPLIT! Spent " + << diffTimestamp(startTs, getTimestamp()) << " Ms " + << "handling abort split events for FD#" << fd_ + << ".\n" << logofs_flush; + + *logofs << "handleSplit: SPLIT! There are " << clientStore_ -> + getSplitTotalSize() << " messages and " << clientStore_ -> + getSplitTotalStorageSize() << " bytes to send in " + << "the split stores.\n" << logofs_flush; + + clientStore_ -> dumpSplitStore(splitState_.current); + + #endif + + return 1; +} + +int ServerChannel::handleSplit(DecodeBuffer &decodeBuffer) +{ + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Going to handle splits " + << "for FD#" << fd_ << " at " << strMsTimestamp() + << ".\n" << logofs_flush; + #endif + + unsigned char resource; + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeCachedValue(resource, 8, + clientCache_ -> resourceCache); + + splitState_.current = resource; + + handleSplitStoreAlloc(&splitResources_, splitState_.current); + + SplitStore *splitStore = clientStore_ -> getSplitStore(splitState_.current); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Handling splits for " + << "resource [" << splitState_.current << "] with " + << splitStore -> getSize() << " elements " + << "in the split store.\n" << logofs_flush; + #endif + + int result = splitStore -> receive(decodeBuffer); + + if (result < 0) + { + #ifdef PANIC + *logofs << "handleSplit: PANIC! Receive of split for FD#" << fd_ + << " failed.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Receive of split for FD#" << fd_ + << " failed.\n"; + + return -1; + } + else if (result == 0) + { + // + // The split is still incomplete. It's time + // to check if we need to start the house- + // keeping process to take care of the image + // cache. + // + + if (proxy -> handleAsyncKeeperCallback() < 0) + { + return -1; + } + } + else + { + // + // Note that we don't need the resource id at the + // X server side and, thus, we don't provide it + // at the time we add split to the split store. + // + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Remote agent should " + << "now commit a new split for resource [" + << splitState_.current << "].\n" + << logofs_flush; + + clientStore_ -> dumpCommitStore(); + + #endif + + if (splitStore -> getSize() == 0) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Removing split store " + << "for resource [" << splitState_.current + << "] at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + handleSplitStoreRemove(&splitResources_, splitState_.current); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! There are [" << clientStore_ -> + getSplitTotalSize() << "] messages and " << clientStore_ -> + getSplitTotalStorageSize() << " bytes to send in " + << "the split stores.\n" << logofs_flush; + #endif + } + else + { + // + // If the next split is discarded, it can be + // that, since the beginning of the split, we + // have saved the message on the disk, due to + // a more recent split operation. This is also + // the case when we had to discard the message + // because it was locked but, since then, we + // completed the transferral of the split. + // + + Split *splitMessage = splitStore -> getFirstSplit(); + + if (splitMessage -> getAction() == is_discarded && + splitMessage -> getState() == split_missed && + splitStore -> load(splitMessage) == 1) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: WARNING! SPLIT! Asynchronously " + << "loaded the message from the image cache.\n" + << logofs_flush; + #endif + + splitMessage -> setState(split_loaded); + + #if defined(TEST) || defined(SPLIT) + + T_timestamp startTs = getTimestamp(); + + *logofs << "handleSplit: WARNING! SPLIT! Asynchronously " + << "encoding abort split events for FD#" << fd_ + << " at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + if (proxy -> handleAsyncSplit(fd_, splitMessage) < 0) + { + return -1; + } + + // + // Send the encoded data immediately. We + // want the abort split message to reach + // the remote proxy as soon as possible. + // + + if (proxy -> handleAsyncFlush() < 0) + { + return -1; + } + + #if defined(TEST) || defined(SPLIT) + + *logofs << "handleSplit: WARNING! SPLIT! Spent " + << diffTimestamp(startTs, getTimestamp()) << " Ms " + << "handling asynchronous abort split events for " + << "FD#" << fd_ << ".\n" << logofs_flush; + + *logofs << "handleSplit: SPLIT! There are " << clientStore_ -> + getSplitTotalSize() << " messages and " << clientStore_ -> + getSplitTotalStorageSize() << " bytes to send in " + << "the split stores.\n" << logofs_flush; + + clientStore_ -> dumpSplitStore(splitState_.current); + + #endif + } + } + } + + return 1; +} + +int ServerChannel::handleSplitEvent(EncodeBuffer &encodeBuffer, Split *splitMessage) +{ + int resource = splitMessage -> getResource(); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitEvent: SPLIT! Going to send a " + << "split report for resource " << resource + << ".\n" << logofs_flush; + #endif + + // + // This function is called only after the message + // has been searched in the disk cache. We need to + // inform the other side if the data transfer can + // start or it must be aborted to let the local + // side use the copy that was found on the disk. + // + + #if defined(TEST) || defined(INFO) + + if (splitMessage -> getState() != split_loaded && + splitMessage -> getState() != split_missed) + { + *logofs << "handleSplitEvent: PANIC! Can't find the split to be aborted.\n" + << logofs_flush; + + HandleCleanup(); + } + + #endif + + // + // We need to send a boolean telling if the split + // was found or not, followed by the checksum of + // message we are referencing. + // + + T_checksum checksum = splitMessage -> getChecksum(); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitEvent: SPLIT! Sending split report for " + << "checksum [" << DumpChecksum(checksum) << "].\n" + << logofs_flush; + #endif + + if (proxy -> handleAsyncSwitch(fd_) < 0) + { + return -1; + } + + encodeBuffer.encodeOpcodeValue(opcodeStore_ -> splitEvent, + serverCache_ -> opcodeCache); + + // + // The encoding in older protocol versions + // is different but we will never try to + // send a split report if the remote does + // not support our version. + // + + encodeBuffer.encodeCachedValue(resource, 8, + serverCache_ -> resourceCache); + + if (splitMessage -> getState() == split_loaded) + { + encodeBuffer.encodeBoolValue(1); + + encodeBuffer.encodeOpcodeValue(splitMessage -> getStore() -> opcode(), + serverCache_ -> abortOpcodeCache); + + encodeBuffer.encodeValue(splitMessage -> compressedSize(), 32, 14); + } + else + { + encodeBuffer.encodeBoolValue(0); + } + + for (unsigned int i = 0; i < MD5_LENGTH; i++) + { + encodeBuffer.encodeValue((unsigned int) checksum[i], 8); + } + + // + // Update statistics for this special opcode. + // + + int bits = encodeBuffer.diffBits(); + + #if defined(TEST) || defined(OPCODES) || defined(INFO) || defined(SPLIT) + *logofs << "handleSplitEvent: SPLIT! Handled event OPCODE#" + << (unsigned int) opcodeStore_ -> splitEvent << " (" + << DumpOpcode(opcodeStore_ -> splitEvent) << ")" << " for FD#" + << fd_ << " sequence none. 0 bytes in, " << bits << " bits (" + << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush; + #endif + + statistics -> addEventBits(opcodeStore_ -> splitEvent, 0, bits); + + return 1; +} + +int ServerChannel::handleAbortSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + unsigned char resource; + + decodeBuffer.decodeCachedValue(resource, 8, + clientCache_ -> resourceCache); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleAbortSplitRequest: SPLIT! Handling abort split " + << "request for FD#" << fd_ << " and resource " + << (unsigned) resource << ".\n" + << logofs_flush; + #endif + + int splits = 0; + + SplitStore *splitStore = clientStore_ -> getSplitStore(resource); + + if (splitStore != NULL) + { + // + // Discard from the memory cache the messages + // that are still incomplete and then get rid + // of the splits in the store. + // + + #if defined(TEST) || defined(SPLIT) + + clientStore_ -> dumpSplitStore(resource); + + #endif + + Split *splitMessage; + + for (;;) + { + splitMessage = splitStore -> getFirstSplit(); + + if (splitMessage == NULL) + { + // + // Check if we had created the store + // but no message was added yet. + // + + #ifdef WARNING + + if (splits == 0) + { + *logofs << "handleAbortSplitRequest: WARNING! SPLIT! The " + << "split store for resource [" << (unsigned int) + resource << "] is unexpectedly empty.\n" + << logofs_flush; + } + + #endif + + break; + } + + // + // Splits already aborted can't be in the + // split store. + // + + #if defined(TEST) || defined(SPLIT) + + if (splitMessage -> getState() == split_aborted) + { + *logofs << "handleAbortSplitRequest: PANIC! SPLIT! Found an " + << "aborted split in store [" << (unsigned int) resource + << "].\n" << logofs_flush; + + HandleCleanup(); + } + + #endif + + if (splitMessage -> getAction() == IS_HIT) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleAbortSplitRequest: SPLIT! Removing the " + << "split from the memory cache.\n" + << logofs_flush; + #endif + + splitMessage -> getStore() -> remove(splitMessage -> getPosition(), + discard_checksum, use_data); + } + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleAbortSplitRequest: SPLIT! Removing the " + << "split from the split store.\n" + << logofs_flush; + #endif + + splitMessage = splitStore -> pop(); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleAbortSplitRequest: SPLIT! Freeing up the " + << "aborted split.\n" << logofs_flush; + #endif + + delete splitMessage; + + splits++; + } + } + #ifdef WARNING + else + { + *logofs << "handleAbortSplitRequest: WARNING! SPLIT! The " + << "split store for resource [" << (unsigned int) + resource << "] is already empty.\n" + << logofs_flush; + } + #endif + + handleNullRequest(opcode, buffer, size); + + return (splits > 0); +} + +int ServerChannel::handleCommitSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + // + // Get request type and position of the image + // to commit. + // + + unsigned char request; + + decodeBuffer.decodeOpcodeValue(request, clientCache_ -> opcodeCache); + + unsigned int diffCommit; + + decodeBuffer.decodeValue(diffCommit, 32, 5); + + splitState_.commit += diffCommit; + + unsigned char resource = 0; + unsigned int commit = 1; + + // + // Send the resource id and the commit flag. + // The resource id is ignored at the moment. + // The message will be handled based on the + // resource id that was sent together with + // the original message. + // + + decodeBuffer.decodeCachedValue(resource, 8, + clientCache_ -> resourceCache); + + decodeBuffer.decodeBoolValue(commit); + + Split *split = handleSplitCommitRemove(request, resource, splitState_.commit); + + if (split == NULL) + { + return -1; + } + + clientStore_ -> getCommitStore() -> update(split); + + if (commit == 1) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleCommitSplitRequest: SPLIT! Handling split commit " + << "for FD#" << fd_ << " with commit " << commit + << " request " << (unsigned) request << " resource " + << (unsigned) resource << " and position " + << splitState_.commit << ".\n" + << logofs_flush; + #endif + + // + // Allocate as many bytes in the write + // buffer as the final length of the + // message in uncompressed form. + // + + size = split -> plainSize(); + + buffer = writeBuffer_.addMessage(size); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleCommitSplitRequest: SPLIT! Prepared an " + << "outgoing buffer of " << size << " bytes.\n" + << logofs_flush; + #endif + + if (clientStore_ -> getCommitStore() -> expand(split, buffer, size) < 0) + { + writeBuffer_.removeMessage(size); + + commit = 0; + } + } + + // + // Free the split. + // + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleCommitSplitRequest: SPLIT! Freeing up the " + << "committed split.\n" << logofs_flush; + #endif + + delete split; + + // + // Discard the operation and send a null + // message. + // + + if (commit == 0) + { + handleNullRequest(opcode, buffer, size); + } + else + { + // + // Save the sequence number to be able + // to mask any error generated by the + // request. + // + + updateCommitQueue(clientSequence_); + + // + // Now in the write buffer there is + // a copy of this request. + // + + opcode = request; + } + + return commit; +} + +int ServerChannel::handleGeometry(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size) +{ + // + // Replace the old geometry and taint + // the message into a X_NoOperation. + // + + int resource = *(buffer + 1); + + #ifdef TEST + *logofs << "handleGeometry: Setting new unpack geometry " + << "for resource " << resource << ".\n" + << logofs_flush; + #endif + + handleUnpackStateInit(resource); + + handleUnpackAllocGeometry(resource); + + unpackState_[resource] -> geometry -> depth1_bpp = *(buffer + 4); + unpackState_[resource] -> geometry -> depth4_bpp = *(buffer + 5); + unpackState_[resource] -> geometry -> depth8_bpp = *(buffer + 6); + unpackState_[resource] -> geometry -> depth16_bpp = *(buffer + 7); + unpackState_[resource] -> geometry -> depth24_bpp = *(buffer + 8); + unpackState_[resource] -> geometry -> depth32_bpp = *(buffer + 9); + + unpackState_[resource] -> geometry -> red_mask = GetULONG(buffer + 12, bigEndian_); + unpackState_[resource] -> geometry -> green_mask = GetULONG(buffer + 16, bigEndian_); + unpackState_[resource] -> geometry -> blue_mask = GetULONG(buffer + 20, bigEndian_); + + handleCleanAndNullRequest(opcode, buffer, size); + + return 1; +} + +int ServerChannel::handleColormap(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size) +{ + // + // Replace the old colormap and taint + // the message into a X_NoOperation. + // + + int resource = *(buffer + 1); + + #ifdef TEST + *logofs << "handleColormap: Setting new unpack colormap " + << "for resource " << resource << ".\n" + << logofs_flush; + #endif + + handleUnpackStateInit(resource); + + handleUnpackAllocColormap(resource); + + // + // New protocol versions send the alpha + // data in compressed form. + // + + // + // Since ProtoStep7 (#issue 108) + // + + { // An anonymous block is used here to limit the scope of local variables + unsigned int packed = GetULONG(buffer + 8, bigEndian_); + unsigned int unpacked = GetULONG(buffer + 12, bigEndian_); + + validateSize("colormap", packed, unpacked, 16, size); + + if (unpackState_[resource] -> colormap -> entries != unpacked >> 2 && + unpackState_[resource] -> colormap -> data != NULL) + { + #ifdef TEST + *logofs << "handleColormap: Freeing previously allocated " + << "unpack colormap data.\n" << logofs_flush; + #endif + + delete [] unpackState_[resource] -> colormap -> data; + + unpackState_[resource] -> colormap -> data = NULL; + unpackState_[resource] -> colormap -> entries = 0; + } + + #ifdef TEST + *logofs << "handleColormap: Setting " << unpacked + << " bytes of unpack colormap data for resource " + << resource << ".\n" << logofs_flush; + #endif + + if (unpackState_[resource] -> colormap -> data == NULL) + { + unpackState_[resource] -> colormap -> data = + (unsigned int *) new unsigned char[unpacked]; + + if (unpackState_[resource] -> colormap -> data == NULL) + { + #ifdef PANIC + *logofs << "handleColormap: PANIC! Can't allocate " + << unpacked << " entries for unpack colormap data " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + goto handleColormapEnd; + } + + #ifdef DEBUG + *logofs << "handleColormap: Size of new colormap data is " + << unpacked << ".\n" << logofs_flush; + #endif + } + + unsigned int method = *(buffer + 4); + + if (method == PACK_COLORMAP) + { + if (UnpackColormap(method, buffer + 16, packed, + (unsigned char *) unpackState_[resource] -> + colormap -> data, unpacked) < 0) + { + #ifdef PANIC + *logofs << "handleColormap: PANIC! Can't unpack " << packed + << " bytes to " << unpacked << " entries for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + delete [] unpackState_[resource] -> colormap -> data; + + unpackState_[resource] -> colormap -> data = NULL; + unpackState_[resource] -> colormap -> entries = 0; + + goto handleColormapEnd; + } + } + else + { + memcpy((unsigned char *) unpackState_[resource] -> + colormap -> data, buffer + 16, unpacked); + } + + unpackState_[resource] -> colormap -> entries = unpacked >> 2; + + #if defined(DEBUG) && defined(DUMP) + + *logofs << "handleColormap: Dumping colormap entries:\n" + << logofs_flush; + + const unsigned char *p = (const unsigned char *) unpackState_[resource] -> colormap -> data; + + for (unsigned int i = 0; i < unpackState_[resource] -> + colormap -> entries; i++) + { + *logofs << "handleColormap: [" << i << "] [" + << (void *) ((int) p[i]) << "].\n" + << logofs_flush; + } + + #endif + } // end anonymous block + +handleColormapEnd: + + handleCleanAndNullRequest(opcode, buffer, size); + + return 1; +} + +int ServerChannel::handleAlpha(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size) +{ + int resource = *(buffer + 1); + + #ifdef TEST + *logofs << "handleAlpha: Setting new unpack alpha " + << "for resource " << resource << ".\n" + << logofs_flush; + #endif + + handleUnpackStateInit(resource); + + handleUnpackAllocAlpha(resource); + + // + // New protocol versions send the alpha + // data in compressed form. + // + + // + // Since ProtoStep7 (#issue 108) + // + + { // An anonymous block is used here to limit the scope of local variables + unsigned int packed = GetULONG(buffer + 8, bigEndian_); + unsigned int unpacked = GetULONG(buffer + 12, bigEndian_); + + validateSize("alpha", packed, unpacked, 16, size); + + if (unpackState_[resource] -> alpha -> entries != unpacked && + unpackState_[resource] -> alpha -> data != NULL) + { + #ifdef TEST + *logofs << "handleAlpha: Freeing previously allocated " + << "unpack alpha data.\n" << logofs_flush; + #endif + + delete [] unpackState_[resource] -> alpha -> data; + + unpackState_[resource] -> alpha -> data = NULL; + unpackState_[resource] -> alpha -> entries = 0; + } + + #ifdef TEST + *logofs << "handleAlpha: Setting " << unpacked + << " bytes of unpack alpha data for resource " + << resource << ".\n" << logofs_flush; + #endif + + if (unpackState_[resource] -> alpha -> data == NULL) + { + unpackState_[resource] -> alpha -> data = new unsigned char[unpacked]; + + if (unpackState_[resource] -> alpha -> data == NULL) + { + #ifdef PANIC + *logofs << "handleAlpha: PANIC! Can't allocate " + << unpacked << " entries for unpack alpha data " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + goto handleAlphaEnd; + } + + #ifdef DEBUG + *logofs << "handleAlpha: Size of new alpha data is " + << unpacked << ".\n" << logofs_flush; + #endif + } + + unsigned int method = *(buffer + 4); + + if (method == PACK_ALPHA) + { + if (UnpackAlpha(method, buffer + 16, packed, + unpackState_[resource] -> alpha -> + data, unpacked) < 0) + { + #ifdef PANIC + *logofs << "handleAlpha: PANIC! Can't unpack " << packed + << " bytes to " << unpacked << " entries for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + delete [] unpackState_[resource] -> alpha -> data; + + unpackState_[resource] -> alpha -> data = NULL; + unpackState_[resource] -> alpha -> entries = 0; + + goto handleAlphaEnd; + } + } + else + { + memcpy((unsigned char *) unpackState_[resource] -> + alpha -> data, buffer + 16, unpacked); + } + + unpackState_[resource] -> alpha -> entries = unpacked; + + #if defined(DEBUG) && defined(DUMP) + + *logofs << "handleAlpha: Dumping alpha entries:\n" + << logofs_flush; + + const unsigned char *p = unpackState_[resource] -> alpha -> data; + + for (unsigned int i = 0; i < unpackState_[resource] -> + alpha -> entries; i++) + { + *logofs << "handleAlpha: [" << i << "] [" + << (void *) ((int) p[i]) << "].\n" + << logofs_flush; + } + + #endif + } //end anonymous block + +handleAlphaEnd: + + handleCleanAndNullRequest(opcode, buffer, size); + + return 1; +} + +int ServerChannel::handleImage(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size) +{ + int result = 1; + + // + // Save the original opcode together with + // the image state so we can later find if + // this is a plain or a packed image when + // moving data to the shared memory area. + // + + handleImageStateAlloc(opcode); + + if (opcode == opcodeStore_ -> putPackedImage) + { + // + // Unpack the image and put a X_PutImage in a + // new buffer. Save the expected output size, + // so, in the case of a decoding error we can + // still update the statistics. + // + + int length = GetULONG(buffer + 20, bigEndian_); + + #ifdef TEST + *logofs << "handleImage: Sending image for FD#" << fd_ + << " due to OPCODE#" << (unsigned int) opcode << " with " + << GetULONG(buffer + 16, bigEndian_) << " bytes packed " + << "and " << GetULONG(buffer + 20, bigEndian_) + << " bytes unpacked.\n" << logofs_flush; + #endif + + statistics -> addPackedBytesIn(size); + + result = handleUnpack(opcode, buffer, size); + + if (result < 0) + { + // + // Recover from the error. Send a X_NoOperation + // to keep the sequence counter in sync with + // the remote peer. + // + + size = 4; + buffer = writeBuffer_.addMessage(size); + + *buffer = X_NoOperation; + + PutUINT(size >> 2, buffer + 2, bigEndian_); + + #ifdef PANIC + *logofs << "handleImage: PANIC! Sending X_NoOperation for FD#" + << fd_ << " to recover from failed unpack.\n" + << logofs_flush; + #endif + + // + // Set the output length to reflect the amount of + // data that would have been produced by unpacking + // the image. This is advisable to keep the count- + // ers in sync with those at remote proxy. Setting + // the size here doesn't have any effect on the + // size of data sent to the X server as the actual + // size will be taken from the content of the write + // buffer. + // + + size = length; + } + + statistics -> addPackedBytesOut(size); + + // + // Refrain the write loop from putting + // opcode and size in the output buffer. + // + + opcode = 0; + } + + // + // Now image is unpacked as a X_PutImage + // in write buffer. Check if we can send + // the image using the MIT-SHM extension. + // + + if (result > 0) + { + result = handleShmem(opcode, buffer, size); + + // + // We already put opcode and size in + // the resulting buffer. + // + + if (result > 0) + { + opcode = 0; + } + } + + return 1; +} + +int ServerChannel::handleMotion(EncodeBuffer &encodeBuffer) +{ + #if defined(TEST) || defined(INFO) + + if (lastMotion_[0] == '\0') + { + *logofs << "handleMotion: PANIC! No motion events to send " + << "for FD#" << fd_ << ".\n" << logofs_flush; + + HandleCleanup(); + } + + #endif + + #if defined(TEST) || defined(INFO) + *logofs << "handleMotion: Sending motion events for FD#" + << fd_ << " at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + // + // Replicate code from read loop. When have + // time and wish, try to split everything + // in functions. + // + + if (proxy -> handleAsyncSwitch(fd_) < 0) + { + return -1; + } + + const unsigned char *buffer = lastMotion_; + unsigned char opcode = *lastMotion_; + unsigned int size = 32; + + if (GetUINT(buffer + 2, bigEndian_) < serverSequence_) + { + PutUINT(serverSequence_, (unsigned char *) buffer + 2, bigEndian_); + } + + encodeBuffer.encodeOpcodeValue(opcode, serverCache_ -> opcodeCache); + + unsigned int sequenceNum = GetUINT(buffer + 2, bigEndian_); + + unsigned int sequenceDiff = sequenceNum - serverSequence_; + + serverSequence_ = sequenceNum; + + #ifdef DEBUG + *logofs << "handleMotion: Last server sequence number for FD#" + << fd_ << " is " << serverSequence_ << " with " + << "difference " << sequenceDiff << ".\n" + << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(sequenceDiff, 16, + serverCache_ -> eventSequenceCache, 7); + + // + // If we fast encoded the message + // then skip the rest. + // + + if (control -> LocalDeltaCompression == 0) + { + int result = handleFastReadEvent(encodeBuffer, opcode, + buffer, size); + + #ifdef DEBUG + *logofs << "handleMotion: Sent saved motion event for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + lastMotion_[0] = '\0'; + + #ifdef DEBUG + *logofs << "handleMotion: Reset last motion event for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + if (result < 0) + { + return -1; + } + else if (result > 0) + { + return 1; + } + } + + // + // This should be just the part specific + // for motion events but is currently a + // copy-paste of code from the read loop. + // + + unsigned char detail = buffer[1]; + if (*buffer == MotionNotify) + encodeBuffer.encodeBoolValue((unsigned int) detail); + else if ((*buffer == EnterNotify) || (*buffer == LeaveNotify)) + encodeBuffer.encodeValue((unsigned int) detail, 3); + else if (*buffer == KeyRelease) + { + if (detail == serverCache_ -> keyPressLastKey) + encodeBuffer.encodeBoolValue(1); + else + { + encodeBuffer.encodeBoolValue(0); + encodeBuffer.encodeValue((unsigned int) detail, 8); + } + } + else if ((*buffer == ButtonPress) || (*buffer == ButtonRelease)) + encodeBuffer.encodeCachedValue(detail, 8, + serverCache_ -> buttonCache); + else + encodeBuffer.encodeValue((unsigned int) detail, 8); + unsigned int timestamp = GetULONG(buffer + 4, bigEndian_); + unsigned int timestampDiff = timestamp - serverCache_ -> lastTimestamp; + serverCache_ -> lastTimestamp = timestamp; + encodeBuffer.encodeCachedValue(timestampDiff, 32, + serverCache_ -> motionNotifyTimestampCache, 9); + int skipRest = 0; + if (*buffer == KeyRelease) + { + skipRest = 1; + for (unsigned int i = 8; i < 31; i++) + { + if (buffer[i] != serverCache_ -> keyPressCache[i - 8]) + { + skipRest = 0; + break; + } + } + encodeBuffer.encodeBoolValue(skipRest); + } + if (!skipRest) + { + const unsigned char *nextSrc = buffer + 8; + for (unsigned int i = 0; i < 3; i++) + { + encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 29, + *serverCache_ -> motionNotifyWindowCache[i], 6); + nextSrc += 4; + } + unsigned int rootX = GetUINT(buffer + 20, bigEndian_); + unsigned int rootY = GetUINT(buffer + 22, bigEndian_); + unsigned int eventX = GetUINT(buffer + 24, bigEndian_); + unsigned int eventY = GetUINT(buffer + 26, bigEndian_); + eventX -= rootX; + eventY -= rootY; + encodeBuffer.encodeCachedValue(rootX - + serverCache_ -> motionNotifyLastRootX, 16, + serverCache_ -> motionNotifyRootXCache, 6); + serverCache_ -> motionNotifyLastRootX = rootX; + encodeBuffer.encodeCachedValue(rootY - + serverCache_ -> motionNotifyLastRootY, 16, + serverCache_ -> motionNotifyRootYCache, 6); + serverCache_ -> motionNotifyLastRootY = rootY; + encodeBuffer.encodeCachedValue(eventX, 16, + serverCache_ -> motionNotifyEventXCache, 6); + encodeBuffer.encodeCachedValue(eventY, 16, + serverCache_ -> motionNotifyEventYCache, 6); + encodeBuffer.encodeCachedValue(GetUINT(buffer + 28, bigEndian_), + 16, serverCache_ -> motionNotifyStateCache); + if ((*buffer == EnterNotify) || (*buffer == LeaveNotify)) + encodeBuffer.encodeValue((unsigned int) buffer[30], 2); + else + encodeBuffer.encodeBoolValue((unsigned int) buffer[30]); + if ((*buffer == EnterNotify) || (*buffer == LeaveNotify)) + encodeBuffer.encodeValue((unsigned int) buffer[31], 2); + else if (*buffer == KeyPress) + { + serverCache_ -> keyPressLastKey = detail; + for (unsigned int i = 8; i < 31; i++) + { + serverCache_ -> keyPressCache[i - 8] = buffer[i]; + } + } + } + + // + // Print info about achieved compression + // and update the statistics. + // + + int bits = encodeBuffer.diffBits(); + + #if defined(TEST) || defined(OPCODES) + *logofs << "handleMotion: Handled event OPCODE#" << (unsigned int) buffer[0] + << " for FD#" << fd_ << " sequence " << sequenceNum << ". " + << size << " bytes in, " << bits << " bits (" << ((float) bits) / 8 + << " bytes) out.\n" << logofs_flush; + #endif + + statistics -> addEventBits(*buffer, size << 3, bits); + + #ifdef DEBUG + *logofs << "handleMotion: Sent saved motion event for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + lastMotion_[0] = '\0'; + + #ifdef DEBUG + *logofs << "handleMotion: Reset last motion event for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + return 1; +} + +int ServerChannel::handleConfiguration() +{ + #ifdef TEST + *logofs << "ServerChannel: Setting new buffer parameters " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + readBuffer_.setSize(control -> ServerInitialReadSize, + control -> ServerMaximumBufferSize); + + writeBuffer_.setSize(control -> TransportXBufferSize, + control -> TransportXBufferThreshold, + control -> TransportMaximumBufferSize); + + transport_ -> setSize(control -> TransportXBufferSize, + control -> TransportXBufferThreshold, + control -> TransportMaximumBufferSize); + return 1; +} + +int ServerChannel::handleFinish() +{ + #ifdef TEST + *logofs << "ServerChannel: Finishing connection for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + congestion_ = 0; + priority_ = 0; + + finish_ = 1; + + // + // Reset the motion event. + // + + lastMotion_[0] = '\0'; + + transport_ -> fullReset(); + + return 1; +} + +int ServerChannel::handleAsyncEvents() +{ + // + // Encode more events while decoding the + // proxy messages. + // + + if (transport_ -> readable() > 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleAsyncEvents: WARNING! Encoding events " + << "for FD#" << fd_ << " at " << strMsTimestamp() + << ".\n" << logofs_flush; + #endif + + #if defined(TEST) || defined(INFO) + + T_timestamp startTs = getTimestamp(); + + #endif + + if (proxy -> handleAsyncRead(fd_) < 0) + { + return -1; + } + + #if defined(TEST) || defined(INFO) + *logofs << "handleAsyncEvents: Spent " << diffTimestamp(startTs, + getTimestamp()) << " Ms handling events for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + return 1; + } + + return 0; +} + +int ServerChannel::handleUnpack(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size) +{ + int resource = *(buffer + 1); + + #ifdef DEBUG + *logofs << "handleUnpack: Unpacking image for resource " << resource + << " with method " << (unsigned int) *(buffer + 12) + << ".\n" << logofs_flush; + #endif + + handleUnpackStateInit(resource); + + T_geometry *geometryState = unpackState_[resource] -> geometry; + T_colormap *colormapState = unpackState_[resource] -> colormap; + T_alpha *alphaState = unpackState_[resource] -> alpha; + + if (geometryState == NULL) + { + #ifdef PANIC + *logofs << "handleUnpack: PANIC! Missing geometry unpacking " + << "image for resource " << resource << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Missing geometry unpacking " + << "image for resource " << resource << ".\n"; + + return -1; + } + + // + // Get the image data from the buffer. + // + + imageState_ -> drawable = GetULONG(buffer + 4, bigEndian_); + imageState_ -> gcontext = GetULONG(buffer + 8, bigEndian_); + + imageState_ -> method = *(buffer + 12); + + imageState_ -> format = *(buffer + 13); + imageState_ -> srcDepth = *(buffer + 14); + imageState_ -> dstDepth = *(buffer + 15); + + imageState_ -> srcLength = GetULONG(buffer + 16, bigEndian_); + imageState_ -> dstLength = GetULONG(buffer + 20, bigEndian_); + + imageState_ -> srcX = GetUINT(buffer + 24, bigEndian_); + imageState_ -> srcY = GetUINT(buffer + 26, bigEndian_); + imageState_ -> srcWidth = GetUINT(buffer + 28, bigEndian_); + imageState_ -> srcHeight = GetUINT(buffer + 30, bigEndian_); + + imageState_ -> dstX = GetUINT(buffer + 32, bigEndian_); + imageState_ -> dstY = GetUINT(buffer + 34, bigEndian_); + imageState_ -> dstWidth = GetUINT(buffer + 36, bigEndian_); + imageState_ -> dstHeight = GetUINT(buffer + 38, bigEndian_); + + #ifdef TEST + *logofs << "handleUnpack: Source X is " << imageState_ -> srcX + << " Y is " << imageState_ -> srcY << " width is " + << imageState_ -> srcWidth << " height is " + << imageState_ -> srcHeight << ".\n" + << logofs_flush; + #endif + + #ifdef TEST + *logofs << "handleUnpack: Destination X is " << imageState_ -> dstX + << " Y is " << imageState_ -> dstY << " width is " + << imageState_ -> dstWidth << " height is " + << imageState_ -> dstHeight << ".\n" + << logofs_flush; + #endif + + if (imageState_ -> srcX != 0 || imageState_ -> srcY != 0) + { + #ifdef PANIC + *logofs << "handleUnpack: PANIC! Unsupported source coordinates " + << "in unpack request.\n" << logofs_flush; + #endif + + return -1; + } + else if (imageState_ -> method == PACK_COLORMAP_256_COLORS && + (colormapState == NULL || colormapState -> data == NULL)) + { + #ifdef PANIC + *logofs << "handleUnpack: PANIC! Cannot find any unpack colormap.\n" + << logofs_flush; + #endif + + return -1; + } + + // + // Field srcLength carries size of image data in + // packed format. Field dstLength is size of the + // image in the original X bitmap format. + // + + unsigned int srcDataOffset = 40; + + unsigned int srcSize = imageState_ -> srcLength; + + unsigned int removeSize = size; + + unsigned char *srcData = buffer + srcDataOffset; + + // + // Get source and destination bits per pixel. + // + + int srcBitsPerPixel = MethodBitsPerPixel(imageState_ -> method); + + if (srcBitsPerPixel <= 0) + { + #ifdef PANIC + *logofs << "handleUnpack: PANIC! Can't identify source " + << "bits per pixel for method " << (unsigned int) + imageState_ -> method << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't identify source bits " + << "per pixel for method " << (unsigned int) + imageState_ -> method << ".\n"; + + writeBuffer_.removeMessage(removeSize); + + return -1; + } + + #ifdef TEST + *logofs << "handleUnpack: Source bits per pixel are " + << srcBitsPerPixel << " source data size is " + << srcSize << ".\n" << logofs_flush; + #endif + + int dstBitsPerPixel = UnpackBitsPerPixel(geometryState, imageState_ -> dstDepth); + + if (dstBitsPerPixel <= 0) + { + #ifdef PANIC + *logofs << "handleUnpack: PANIC! Can't identify " + << "destination bits per pixel for depth " + << (unsigned int) imageState_ -> dstDepth + << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't identify " + << "destination bits per pixel for depth " + << (unsigned int) imageState_ -> dstDepth + << ".\n"; + + writeBuffer_.removeMessage(removeSize); + + return -1; + } + + // + // Destination is a PutImage request. + // + + unsigned int dstDataOffset = 24; + + // + // Output buffer size must match the number of input + // pixels multiplied by the number of bytes per pixel + // of current geometry. + // + + size = (RoundUp4(imageState_ -> dstWidth * dstBitsPerPixel / 8) * + imageState_ -> dstHeight) + dstDataOffset; + + #ifdef TEST + *logofs << "handleUnpack: Destination bits per pixel are " + << dstBitsPerPixel << " destination data size is " + << size - dstDataOffset << ".\n" << logofs_flush; + #endif + + unsigned int dstSize = size - dstDataOffset; + + imageState_ -> dstLines = imageState_ -> dstHeight; + + unsigned char *dstData; + + // + // Size of the final output buffer had to be stored + // in the offset field of XImage/NXPackedImage. + // + + #ifdef WARNING + + if (dstSize != imageState_ -> dstLength) + { + *logofs << "handleUnpack: WARNING! Destination size mismatch " + << "with reported " << imageState_ -> dstLength + << " and actual " << dstSize << ".\n" + << logofs_flush; + } + + #endif + + // + // The decoding algorithm has put the packed image + // in the plain write buffer. Let's use the scratch + // buffer to uncompress the image. + // + + buffer = writeBuffer_.addScratchMessage(size); + + dstData = buffer + dstDataOffset; + + // + // Unpack image into the buffer. + // + + *buffer = (unsigned char) X_PutImage; + + *(buffer + 1) = imageState_ -> format; + + PutUINT(size >> 2, buffer + 2, bigEndian_); + + PutULONG(imageState_ -> drawable, buffer + 4, bigEndian_); + PutULONG(imageState_ -> gcontext, buffer + 8, bigEndian_); + + PutUINT(imageState_ -> dstWidth, buffer + 12, bigEndian_); + PutUINT(imageState_ -> dstLines, buffer + 14, bigEndian_); + + PutUINT(imageState_ -> dstX, buffer + 16, bigEndian_); + PutUINT(imageState_ -> dstY, buffer + 18, bigEndian_); + + *(buffer + 20) = 0; + *(buffer + 21) = imageState_ -> dstDepth; + + #ifdef TEST + *logofs << "handleUnpack: Write buffer size is " + << writeBuffer_.getLength() << " scratch size is " + << writeBuffer_.getScratchLength() << ".\n" + << logofs_flush; + #endif + + int result = 0; + + switch (imageState_ -> method) + { + case PACK_JPEG_8_COLORS: + case PACK_JPEG_64_COLORS: + case PACK_JPEG_256_COLORS: + case PACK_JPEG_512_COLORS: + case PACK_JPEG_4K_COLORS: + case PACK_JPEG_32K_COLORS: + case PACK_JPEG_64K_COLORS: + case PACK_JPEG_256K_COLORS: + case PACK_JPEG_2M_COLORS: + case PACK_JPEG_16M_COLORS: + { + result = UnpackJpeg(geometryState, imageState_ -> method, srcData, + srcSize, dstBitsPerPixel, imageState_ -> dstWidth, + imageState_ -> dstHeight, dstData, dstSize); + break; + } + case PACK_PNG_8_COLORS: + case PACK_PNG_64_COLORS: + case PACK_PNG_256_COLORS: + case PACK_PNG_512_COLORS: + case PACK_PNG_4K_COLORS: + case PACK_PNG_32K_COLORS: + case PACK_PNG_64K_COLORS: + case PACK_PNG_256K_COLORS: + case PACK_PNG_2M_COLORS: + case PACK_PNG_16M_COLORS: + { + result = UnpackPng(geometryState, imageState_ -> method, srcData, + srcSize, dstBitsPerPixel, imageState_ -> dstWidth, + imageState_ -> dstHeight, dstData, dstSize); + break; + } + case PACK_RGB_16M_COLORS: + { + result = UnpackRgb(geometryState, imageState_ -> method, srcData, + srcSize, dstBitsPerPixel, imageState_ -> dstWidth, + imageState_ -> dstHeight, dstData, dstSize); + break; + } + case PACK_RLE_16M_COLORS: + { + result = UnpackRle(geometryState, imageState_ -> method, srcData, + srcSize, dstBitsPerPixel, imageState_ -> dstWidth, + imageState_ -> dstHeight, dstData, dstSize); + break; + } + case PACK_BITMAP_16M_COLORS: + { + result = UnpackBitmap(geometryState, imageState_ -> method, srcData, + srcSize, dstBitsPerPixel, imageState_ -> dstWidth, + imageState_ -> dstHeight, dstData, dstSize); + break; + } + case PACK_COLORMAP_256_COLORS: + { + result = Unpack8(geometryState, colormapState, srcBitsPerPixel, + imageState_ -> srcWidth, imageState_ -> srcHeight, srcData, + srcSize, dstBitsPerPixel, imageState_ -> dstWidth, + imageState_ -> dstHeight, dstData, dstSize); + + break; + } + default: + { + const T_colormask *colorMask = MethodColorMask(imageState_ -> method); + + switch (imageState_ -> method) + { + case PACK_MASKED_8_COLORS: + case PACK_MASKED_64_COLORS: + case PACK_MASKED_256_COLORS: + { + result = Unpack8(geometryState, colorMask, imageState_ -> srcDepth, + imageState_ -> srcWidth, imageState_ -> srcHeight, + srcData, srcSize, imageState_ -> dstDepth, + imageState_ -> dstWidth, imageState_ -> dstHeight, + dstData, dstSize); + break; + } + case PACK_MASKED_512_COLORS: + case PACK_MASKED_4K_COLORS: + case PACK_MASKED_32K_COLORS: + case PACK_MASKED_64K_COLORS: + { + result = Unpack16(geometryState, colorMask, imageState_ -> srcDepth, + imageState_ -> srcWidth, imageState_ -> srcHeight, + srcData, srcSize, imageState_ -> dstDepth, + imageState_ -> dstWidth, imageState_ -> dstHeight, + dstData, dstSize); + break; + } + case PACK_MASKED_256K_COLORS: + case PACK_MASKED_2M_COLORS: + case PACK_MASKED_16M_COLORS: + { + result = Unpack24(geometryState, colorMask, imageState_ -> srcDepth, + imageState_ -> srcWidth, imageState_ -> srcHeight, + srcData, srcSize, imageState_ -> dstDepth, + imageState_ -> dstWidth, imageState_ -> dstHeight, + dstData, dstSize); + break; + } + default: + { + break; + } + } + } + } + + writeBuffer_.removeMessage(removeSize); + + if (result <= 0) + { + #ifdef PANIC + *logofs << "handleUnpack: PANIC! Failed to unpack image " + << "with method '" << (unsigned int) imageState_ -> method + << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Failed to unpack image " + << "with method '" << (unsigned int) imageState_ -> method + << "'.\n"; + + // + // TODO: We should mark the image somehow, + // and force the remote to remove it from + // the cache. + // + + writeBuffer_.removeScratchMessage(); + + return -1; + } + + // + // Alpha channel is used only on some 32 bits pixmaps + // and only if render extension is in use. Presently + // we don't have an efficient way to know in advance + // if mask must be applied or not to the image. If an + // alpha channel is set, the function will check if + // the size of the alpha data matches the size of the + // image. In the worst case we'll create an useless + // alpha plane for a pixmap that doesn't need it. + // + + if (alphaState != NULL && alphaState -> data != NULL && + imageState_ -> dstDepth == 32) + { + UnpackAlpha(alphaState, dstData, dstSize, imageByteOrder_); + } + + return 1; +} + +int ServerChannel::handleAuthorization(unsigned char *buffer) +{ + // + // At the present moment we don't support more than + // a single display for each proxy, so authorization + // data is shared among all the channels. + // + // Use the following code to simulate authentication + // failures on a LSB machine: + // + // memcpy(buffer + 12 + (((buffer[6] + 256 * + // buffer[7]) + 3) & ~3), "1234567890123456", 16); + // + + if (auth == NULL) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleAuthorization: Forwarding the real cookie " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + return 0; + } + else if (auth -> checkCookie(buffer) == 1) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleAuthorization: Matched the fake cookie " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + return 1; + } + else + { + #if defined(TEST) || defined(INFO) + *logofs << "handleAuthorization: WARNING! Failed to match " + << "the fake cookie for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + return -1; + } +} + +int ServerChannel::handleAuthorization(const unsigned char *buffer, int size) +{ + // + // Check the X server's response and, in the case of + // an error, print the textual information reported + // by the server. + // + + if (*buffer != 1) + { + const char *reason = NULL; + + // + // At the moment we don't take into account the end- + // ianess of the reply. This should work in any case + // because we simply try to match a few well-known + // error strings. + // + + if (size >= INVALID_COOKIE_SIZE + 8 && + memcmp(buffer + 8, INVALID_COOKIE_DATA, + INVALID_COOKIE_SIZE) == 0) + { + reason = INVALID_COOKIE_DATA; + } + else if (size >= NO_AUTH_PROTO_SIZE + 8 && + memcmp(buffer + 8, NO_AUTH_PROTO_DATA, + NO_AUTH_PROTO_SIZE) == 0) + { + reason = NO_AUTH_PROTO_DATA; + } + else + { + reason = "Unknown"; + } + + #ifdef WARNING + *logofs << "handleAuthorization: WARNING! X connection failed " + << "with error '" << reason << "' on FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + cerr << "Warning" << ": X connection failed " + << "with error '" << reason << "'.\n"; + } + #if defined(TEST) || defined(INFO) + else + { + *logofs << "handleAuthorization: X connection successful " + << "on FD#" << fd_ << ".\n" << logofs_flush; + } + #endif + + return 1; +} + +// +// Use a simple encoding. Need to handle the image +// requests in the usual way and the X_ListExtensions +// and X_QueryExtension to hide MIT-SHM and RENDER +// in the reply. +// + +int ServerChannel::handleFastWriteRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + // + // All the NX requests are handled in the + // main message loop. + // + + // + // Since ProtoStep7 (#issue 108) + // + // The X_PutImage can be handled here only + // if a split was not requested. + // + + if ((opcode >= X_NXFirstOpcode && opcode <= X_NXLastOpcode) || + (opcode == X_PutImage && splitState_.resource != nothing) || + opcode == X_ListExtensions || + opcode == X_QueryExtension) + { + return 0; + } + + #ifdef DEBUG + *logofs << "handleFastWriteRequest: Decoding raw request OPCODE#" + << (unsigned int) opcode << " for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + buffer = writeBuffer_.addMessage(4); + + #ifndef __sun + + unsigned int *next = (unsigned int *) decodeBuffer.decodeMemory(4); + + *((unsigned int *) buffer) = *next; + + #else /* #ifndef __sun */ + + memcpy(buffer, decodeBuffer.decodeMemory(4), 4); + + #endif /* #ifndef __sun */ + + size = GetUINT(buffer + 2, bigEndian_) << 2; + + if (size < 4) + { + #ifdef WARNING + *logofs << "handleFastWriteRequest: WARNING! Assuming size 4 " + << "for suspicious message of size " << size + << ".\n" << logofs_flush; + #endif + + size = 4; + } + + writeBuffer_.registerPointer(&buffer); + + if (writeBuffer_.getAvailable() < size - 4 || + (int) size >= control -> TransportFlushBufferSize) + { + #ifdef DEBUG + *logofs << "handleFastWriteRequest: Using scratch buffer for OPCODE#" + << (unsigned int) opcode << " with size " << size << " and " + << writeBuffer_.getLength() << " bytes in buffer.\n" + << logofs_flush; + #endif + + // + // The procedure moving data to shared memory + // assumes that the full message is stored in + // the scratch buffer. We can safely let the + // scratch buffer inherit the decode buffer + // at the next offset. + // + + writeBuffer_.removeMessage(4); + + buffer = writeBuffer_.addScratchMessage(((unsigned char *) + decodeBuffer.decodeMemory(size - 4)) - 4, size); + } + else + { + writeBuffer_.addMessage(size - 4); + + #ifndef __sun + + if (size <= 32) + { + next = (unsigned int *) decodeBuffer.decodeMemory(size - 4); + + for (unsigned int i = 4; i < size; i += sizeof(unsigned int)) + { + *((unsigned int *) (buffer + i)) = *next++; + } + } + else + { + memcpy(buffer + 4, decodeBuffer.decodeMemory(size - 4), size - 4); + } + + #else /* #ifndef __sun */ + + memcpy(buffer + 4, decodeBuffer.decodeMemory(size - 4), size - 4); + + #endif /* #ifndef __sun */ + } + + // + // Opcode could have been tainted by the client + // proxy. Replace the original opcode with the + // one sent in the decode buffer. + // + + *buffer = opcode; + + writeBuffer_.unregisterPointer(); + + if (opcode == X_PutImage) + { + handleImage(opcode, buffer, size); + } + + #if defined(TEST) || defined(OPCODES) + + if (opcode != 0) + { + *logofs << "handleFastWriteRequest: Handled request " + << "OPCODE#" << (unsigned int) opcode << " (" + << DumpOpcode(opcode) << ") for FD#" << fd_ + << " sequence " << clientSequence_ << ". " + << size << " bytes out.\n" << logofs_flush; + } + else + { + *logofs << "handleFastWriteRequest: Handled image or " + << "other request for FD#" << fd_ + << " sequence " << clientSequence_ << ". " + << size << " bytes out.\n" << logofs_flush; + } + + #endif + + handleFlush(flush_if_needed); + + return 1; +} + +// +// Use the simplest encoding except for replies that +// need to be managed some way. +// + +int ServerChannel::handleFastReadReply(EncodeBuffer &encodeBuffer, const unsigned char &opcode, + const unsigned char *&buffer, const unsigned int &size) +{ + // + // If we pushed a X_GetInputFocus in the sequence + // queue this means that the original message was + // a NX request for which we have to provide a NX + // reply. + // + + if ((opcode >= X_NXFirstOpcode && + opcode <= X_NXLastOpcode) || + opcode == X_QueryExtension || + opcode == X_ListExtensions || + opcode == X_GetInputFocus) + { + return 0; + } + + #ifdef DEBUG + *logofs << "handleFastReadReply: Encoding raw reply OPCODE#" + << (unsigned int) opcode << " for FD#" << fd_ + << " with size " << size << ".\n" + << logofs_flush; + #endif + + encodeBuffer.encodeMemory(buffer, size); + + // + // Send back the reply as soon + // as possible. + // + + priority_++; + + int bits = encodeBuffer.diffBits(); + + #if defined(TEST) || defined(OPCODES) + *logofs << "handleFastReadReply: Handled raw reply OPCODE#" + << (unsigned int) opcode << " for FD#" << fd_ << " sequence " + << serverSequence_ << ". " << size << " bytes in, " + << bits << " bits (" << ((float) bits) / 8 + << " bytes) out.\n" << logofs_flush; + #endif + + statistics -> addReplyBits(opcode, size << 3, bits); + + return 1; +} + +int ServerChannel::handleFastReadEvent(EncodeBuffer &encodeBuffer, const unsigned char &opcode, + const unsigned char *&buffer, const unsigned int &size) +{ + #ifdef DEBUG + *logofs << "handleFastReadEvent: Encoding raw " + << (opcode == X_Error ? "error" : "event") << " OPCODE#" + << (unsigned int) opcode << " for FD#" << fd_ + << " with size " << size << ".\n" + << logofs_flush; + #endif + + encodeBuffer.encodeMemory(buffer, size); + + switch (opcode) + { + case X_Error: + case ButtonPress: + case ButtonRelease: + case KeyPress: + case KeyRelease: + { + priority_++; + } + } + + int bits = encodeBuffer.diffBits(); + + #if defined(TEST) || defined(OPCODES) + + if (opcode == X_Error) + { + unsigned char code = *(buffer + 1); + + *logofs << "handleFastReadEvent: Handled error ERR_CODE#" + << (unsigned int) code << " for FD#" << fd_; + + *logofs << " RES_ID#" << GetULONG(buffer + 4, bigEndian_); + + *logofs << " MIN_OP#" << GetUINT(buffer + 8, bigEndian_); + + *logofs << " MAJ_OP#" << (unsigned int) *(buffer + 10); + + *logofs << " sequence " << serverSequence_ << ". " << size + << " bytes in, " << bits << " bits (" << ((float) bits) / 8 + << " bytes) out.\n" << logofs_flush; + } + else + { + *logofs << "handleFastReadEvent: Handled event OPCODE#" + << (unsigned int) *buffer << " for FD#" << fd_ + << " sequence " << serverSequence_ << ". " << size + << " bytes in, " << bits << " bits (" << ((float) bits) / 8 + << " bytes) out.\n" << logofs_flush; + } + + #endif + + statistics -> addEventBits(opcode, size << 3, bits); + + return 1; +} + +void ServerChannel::initCommitQueue() +{ + #ifdef TEST + *logofs << "initCommitQueue: Resetting the queue of split commits " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + for (int i = 0; i < MAX_COMMIT_SEQUENCE_QUEUE; i++) + { + commitSequenceQueue_[i] = 0; + } +} + +void ServerChannel::updateCommitQueue(unsigned short sequence) +{ + for (int i = 0; i < MAX_COMMIT_SEQUENCE_QUEUE - 1; i++) + { + commitSequenceQueue_[i + 1] = commitSequenceQueue_[i]; + } + + #ifdef TEST + *logofs << "updateCommitQueue: Saved " << sequence + << " as last sequence number of image to commit.\n" + << logofs_flush; + #endif + + commitSequenceQueue_[0] = sequence; +} + +int ServerChannel::checkCommitError(unsigned char error, unsigned short sequence, + const unsigned char *buffer) +{ + // + // Check if error is due to an image commit + // generated at the end of a split. + // + // TODO: It should zero the head of the list + // when an event comes with a sequence number + // greater than the value of the last element + // added. + // + + for (int i = 0; i < MAX_COMMIT_SEQUENCE_QUEUE && + commitSequenceQueue_[i] != 0; i++) + { + #ifdef TEST + *logofs << "checkCommitError: Checking committed image's " + << "sequence number " << commitSequenceQueue_[i] + << " with input sequence " << sequence << ".\n" + << logofs_flush; + #endif + + if (commitSequenceQueue_[i] == sequence) + { + #ifdef WARNING + + *logofs << "checkCommitError: WARNING! Failed operation for " + << "FD#" << fd_ << " with ERR_CODE#" + << (unsigned int) *(buffer + 1); + + *logofs << " RES_ID#" << GetULONG(buffer + 4, bigEndian_); + + *logofs << " MIN_OP#" << GetUINT(buffer + 8, bigEndian_); + + *logofs << " MAJ_OP#" << (unsigned int) *(buffer + 10); + + *logofs << " sequence " << sequence << ".\n"; + + *logofs << logofs_flush; + + #endif + + cerr << "Warning" << ": Failed commit operation " + << "with ERR_CODE#" << (unsigned int) error; + + cerr << " RES_ID#" << GetULONG(buffer + 4, bigEndian_); + + cerr << " MIN_OP#" << GetUINT(buffer + 8, bigEndian_); + + cerr << " MAJ_OP#" << (unsigned int) *(buffer + 10); + + cerr << ".\n"; + + #ifdef WARNING + *logofs << "checkCommitError: WARNING! Suppressing error on " + << "OPCODE#" << (unsigned int) opcodeStore_ -> commitSplit + << " for FD#" << fd_ << " with sequence " << sequence + << " at position " << i << ".\n" << logofs_flush; + #endif + + return 0; + } + } + + return 0; +} + +// +// Check if the user pressed the CTRL+ALT+SHIFT+ESC +// keystroke. At the present moment it uses different +// keycodes based on the client OS. This should be +// implemented in a way that is platform independent +// (that's not an easy task, considered that we don't +// have access to the higher level X libraries). +// + +int ServerChannel::checkKeyboardEvent(unsigned char event, unsigned short sequence, + const unsigned char *buffer) +{ + #ifdef TEST + *logofs << "checkKeyboardEvent: Checking escape sequence with byte [1] " + << (void *) ((unsigned) *(buffer + 1)) << " and bytes [28-29] " + << (void *) ((unsigned) GetUINT(buffer + 28, bigEndian_)) + << " for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + #ifdef __APPLE__ + + int alert = (*(buffer + 1) == 0x3d && + GetUINT(buffer + 28, bigEndian_) == 0x2005); + + #else + + int alert = (*(buffer + 1) == 0x09 && + ((GetUINT(buffer + 28, bigEndian_) & + 0x0d) == 0x0d)); + + #endif + + if (alert == 1) + { + #ifdef PANIC + *logofs << "checkKeyboardEvent: PANIC! Received sequence " + << "CTRL+ALT+SHIFT+ESC " << "for FD#"<< fd_ + << ". Showing the abort dialog.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Received sequence CTRL+ALT+SHIFT+ESC. " + << "Showing the abort dialog.\n"; + + HandleAlert(CLOSE_UNRESPONSIVE_X_SERVER_ALERT, 1); + } + + return alert; +} + +// +// Handle the MIT-SHM initialization +// messages exchanged with the remote +// proxy. +// + +int ServerChannel::handleShmemReply(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned int stage, const unsigned char *buffer, + const unsigned int size) +{ + #ifdef TEST + *logofs << "handleShmemReply: Returning shmem reply for " + << "stage " << stage << ".\n" << logofs_flush; + #endif + + if (opcode == X_QueryExtension) + { + encodeBuffer.encodeValue(stage, 2); + +#ifndef ANDROID + shmemState_ -> present = *(buffer + 8); +#else + shmemState_ -> present = 0; + cerr << "Info: handleShmemReply: In android no shared memory. Setting present to 0 hardcoded\n"; +#endif + shmemState_ -> opcode = *(buffer + 9); + shmemState_ -> event = *(buffer + 10); + shmemState_ -> error = *(buffer + 11); + + #ifdef TEST + *logofs << "handleShmemReply: Extension present is " + << shmemState_ -> present << " with base OPCODE#" + << (unsigned int) shmemState_ -> opcode << " base event " + << (unsigned int) shmemState_ -> event << " base error " + << (unsigned int) shmemState_ -> error << ".\n" + << logofs_flush; + #endif + } + else if (opcode == X_GetInputFocus) + { + encodeBuffer.encodeValue(stage, 2); + + encodeBuffer.encodeBoolValue(0); + + if (shmemState_ -> present == 1 && + shmemState_ -> address != NULL && + shmemState_ -> segment > 0 && + shmemState_ -> id > 0) + { + cerr << "Info" << ": Using shared memory parameters 1/" + << (shmemState_ -> size / 1024) << "K.\n"; + +#ifndef ANDROID + shmemState_ -> enabled = 1; +#else + cerr << "Info: handleShmemReply: In android no shared memory. Setting enabled to -1. This should not be displayed\n"; + shmemState_ -> enabled = -1; +#endif + + encodeBuffer.encodeBoolValue(1); + } + else + { + #ifdef TEST + *logofs << "handleShmemReply: WARNING! Not using shared memory " + << "support in X server for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + cerr << "Info" << ": Using shared memory parameters 0/0K.\n"; + + handleShmemStateRemove(); + + encodeBuffer.encodeBoolValue(0); + } + } + else + { + #ifdef PANIC + *logofs << "handleShmemReply: PANIC! Conversation error " + << "handling shared memory support for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Conversation error handling " + << "shared memory support.\n"; + + return -1; + } + + return 1; +} + +int ServerChannel::handleShmemRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + // + // We need to query and initialize MIT-SHM on + // the real X server. To do this we'll need 3 + // requests. At the end we'll have to encode + // the final reply for the X client side. + // + + handleShmemStateAlloc(); + + unsigned int stage; + + decodeBuffer.decodeValue(stage, 2); + + unsigned int expected = shmemState_ -> stage + 1; + + if (stage != expected || stage > 2) + { + #ifdef PANIC + *logofs << "handleShmemRequest: PANIC! Unexpected stage " + << stage << " in handling shared memory " + << "support for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Unexpected stage " + << stage << " in handling shared memory " + << "support for FD#" << fd_ << ".\n"; + + return -1; + } + + switch (stage) + { + case 0: + { + unsigned int enableClient; + unsigned int enableServer; + + decodeBuffer.decodeBoolValue(enableClient); + decodeBuffer.decodeBoolValue(enableServer); + + unsigned int clientSegment; + unsigned int serverSegment; + + decodeBuffer.decodeValue(clientSegment, 29, 9); + decodeBuffer.decodeValue(serverSegment, 29, 9); + + shmemState_ -> segment = serverSegment; + + #ifdef TEST + *logofs << "handleShmemRequest: Size of the shared memory " + << "segment will be " << control -> ShmemServerSize + << ".\n" << logofs_flush; + #endif + + #ifdef TEST + *logofs << "handleShmemRequest: Sending X_QueryExtension request " + << "for FD#" << fd_ << " due to OPCODE#" << (unsigned int) + opcodeStore_ -> getShmemParameters << " in stage " + << stage << ".\n" << logofs_flush; + #endif + + opcode = X_QueryExtension; + + size = 16; + buffer = writeBuffer_.addMessage(size); + + PutUINT(7, buffer + 4, bigEndian_); + + // + // Simply make the query fail if shared + // memory support is disabled by the + // user. + // +#ifndef ANDROID + if (control -> ShmemServer == 1 && + control -> ShmemServerSize > 0 && + enableServer == 1) + { + memcpy(buffer + 8, "MIT-SHM", 7); + } + else + { + memcpy(buffer + 8, "NO-MIT-", 7); + } +#else + cerr << "Info: handleShmemRequest: In android no shared memory. Returning NO-MIT- answer\n"; + + memcpy(buffer + 8, "NO-MIT-", 7); +#endif + sequenceQueue_.push(clientSequence_, opcode, + opcodeStore_ -> getShmemParameters, stage); + + // + // Save the sequence number so we can + // later identify any matching X error + // received from server. + // + + shmemState_ -> sequence = clientSequence_; + + break; + } + case 1: + { + if (shmemState_ -> present == 1) + { + // + // Make the segment read-write for everybody on + // Cygwin (to avoid any lack of support or any + // performance issue) and on MacOS/X (where the + // 0600 mask doesn't seem to work). + // + + #if defined(__CYGWIN32__) || defined(__APPLE__) + + int permissions = 0777; + + #else + + int permissions = 0600; + + #endif + + shmemState_ -> size = control -> ShmemServerSize; + +#ifndef ANDROID + shmemState_ -> id = shmget(IPC_PRIVATE, shmemState_ -> size, + IPC_CREAT | permissions); +#else + cerr << "Info: handleShmemReqyest: In android no shared memory (shmget). This message should not be displayed present should never be 1 in android\n"; + shmemState_ -> id = -1; +#endif + if (shmemState_ -> id >= 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleShmemRequest: Allocated shared memory " + << "segment of " << shmemState_ -> size + << " bytes with id " << shmemState_ -> id + << ".\n" << logofs_flush; + #endif + + +#ifndef ANDROID + shmemState_ -> address = shmat(shmemState_ -> id, 0, 0); +#else + cerr << "Info: handleShmemReqyest: In android no shared memory (shmat). This message should not be displayed. present should never be 1 in android\n"; + shmemState_ -> address = NULL; +#endif + if (shmemState_ -> address != NULL) + { + #ifdef TEST + *logofs << "handleShmemRequest: Sending X_ShmAttach request " + << "for FD#" << fd_ << " due to OPCODE#" << (unsigned int) + opcodeStore_ -> getShmemParameters << " in stage " + << stage << ".\n" << logofs_flush; + #endif + + opcode = shmemState_ -> opcode; + + size = 16; + buffer = writeBuffer_.addMessage(size); + + *(buffer + 1) = X_ShmAttach; + + PutULONG(shmemState_ -> segment, buffer + 4, bigEndian_); + PutULONG(shmemState_ -> id, buffer + 8, bigEndian_); + + *(buffer + 12) = 1; + + shmemState_ -> sequence = clientSequence_; + + break; + } + else + { + #ifdef WARNING + *logofs << "handleShmemRequest: WARNING! Can't attach the shared " + << "memory segment. Error is " << EGET() << " '" + << ESTR() << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Can't attach the shared memory " + << "segment. Error is " << EGET() << " '" + << ESTR() << "'.\n"; + } + } + else + { + #ifndef __CYGWIN32__ + + #ifdef WARNING + *logofs << "handleShmemRequest: WARNING! Can't create the shared " + << "memory segment. Error is " << EGET() << " '" + << ESTR() << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Can't create the shared memory " + << "segment. Error is " << EGET() << " '" + << ESTR() << "'.\n"; + + #else + + #ifdef TEST + *logofs << "handleShmemRequest: WARNING! Can't create the shared " + << "memory segment. Error is " << EGET() << " '" + << ESTR() << "'.\n" << logofs_flush; + #endif + + #endif + } + } + + if (shmemState_ -> present != 0) + { + #ifdef TEST + *logofs << "handleShmemRequest: Resetting shared memory " + << "presence flag for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + shmemState_ -> present = 0; + } + + handleNullRequest(opcode, buffer, size); + + break; + } + default: + { + #ifdef TEST + *logofs << "handleShmemRequest: Sending X_GetInputFocus request " + << "for FD#" << fd_ << " due to OPCODE#" << (unsigned int) + opcodeStore_ -> getShmemParameters << " in stage " + << stage << ".\n" << logofs_flush; + #endif + + opcode = X_GetInputFocus; + + size = 4; + buffer = writeBuffer_.addMessage(size); + + sequenceQueue_.push(clientSequence_, opcode, + opcodeStore_ -> getShmemParameters, stage); + break; + } + } + + shmemState_ -> stage += 1; + + return 1; +} + +// +// Handling of MIT-SHM extension has been plugged late in +// the design, so we have to make some assumptions. Image +// is a X_PutImage request contained either in the scratch +// buffer or in the normal write buffer. We need to move +// the image data to the shared memory segment and replace +// the X_PutImage request with a X_ShmPutImage. +// + +int ServerChannel::handleShmem(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size) +{ + if (shmemState_ == NULL || shmemState_ -> enabled != 1) + { + #ifdef TEST + + if (shmemState_ != NULL) + { + *logofs << "handleShmem: PANIC! Shared memory " + << "state found but support is not enabled " + << "for FD#" << fd_ << " in stage " + << shmemState_ -> stage << ".\n" + << logofs_flush; + } + + #endif + + return 0; + } +#ifdef ANDROID + cerr << "Info: handleShmem: In android no shared memory. enabled should never be 1. This should not be displayed\n"; + return 0; +#endif + + // + // Ignore null requests and requests that will not result + // in a single X_PutImage. To conform with the other func- + // tions, we get the opcode passed as a parameter. It can + // be zero if we don't want the write loop to put opcode + // and length in the resulting buffer. Anyway we are only + // interested in the original opcode of the request, that + // is stored in the image state. + // + + unsigned char *dstData = buffer + 24; + unsigned int dstDataSize = size - 24; + + if (dstDataSize == 0 || dstDataSize > + (unsigned int) control -> MaximumRequestSize) + { + #ifdef TEST + *logofs << "handleShmem: Ignoring image with opcode " + << (unsigned int) imageState_ -> opcode + << " and size " << size << " for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + return 0; + } + + #ifdef TEST + *logofs << "handleShmem: Handling image with opcode " + << (unsigned int) imageState_ -> opcode + << " and size " << size << " for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + // + // Get image data from buffer. + // + + if (imageState_ -> opcode == X_PutImage) + { + // + // We still need to get the image's data. + // + + imageState_ -> format = *(buffer + 1); + + imageState_ -> drawable = GetULONG(buffer + 4, bigEndian_); + imageState_ -> gcontext = GetULONG(buffer + 8, bigEndian_); + + imageState_ -> dstWidth = GetUINT(buffer + 12, bigEndian_); + imageState_ -> dstHeight = GetUINT(buffer + 14, bigEndian_); + + imageState_ -> srcX = 0; + imageState_ -> srcY = 0; + + imageState_ -> srcWidth = imageState_ -> dstWidth; + imageState_ -> srcHeight = imageState_ -> dstHeight; + + imageState_ -> dstX = GetUINT(buffer + 16, bigEndian_); + imageState_ -> dstY = GetUINT(buffer + 18, bigEndian_); + + imageState_ -> leftPad = *(buffer + 20); + imageState_ -> dstDepth = *(buffer + 21); + + imageState_ -> dstLines = imageState_ -> dstHeight; + + imageState_ -> dstLength = size - 24; + } + + // + // Skip the MIT-SHM operation if the image + // is 1 bits-per-plane. + // + + if (imageState_ -> dstDepth == 1) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleShmem: Ignoring image with opcode " + << (unsigned int) imageState_ -> opcode << " depth " + << (unsigned int) imageState_ -> dstDepth << " and " + << "size " << size << " for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + return 0; + } + + // + // If the image can't fit in the available + // space, check if the completion event is + // arrived. + // + + #if defined(TEST) || defined(INFO) + + if (isTimestamp(shmemState_ -> last) == 0 && + shmemState_ -> offset != 0) + { + *logofs << "handleShmem: PANIC! No timestamp for sequence " + << shmemState_ -> sequence << " with offset " + << shmemState_ -> offset << ".\n" + << logofs_flush; + } + + #endif + + if (shmemState_ -> offset + imageState_ -> dstLength > + shmemState_ -> size) + { + if (imageState_ -> dstLength > shmemState_ -> size) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleShmem: WARNING! Can't fit the image " + << "in the available memory segment for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + return 0; + } + else if (handleShmemEvent() <= 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleShmem: WARNING! Missing completion " + << "after " << diffTimestamp(shmemState_ -> last, + getTimestamp()) << " Ms for shared memory " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + return 0; + } + } + + // + // Let image start at current offset + // in the shared segment. + // + + #ifdef TEST + *logofs << "handleShmem: Copying " << dstDataSize + << " bytes to shared memory at offset " + << shmemState_ -> offset << " for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + memcpy((unsigned char *) shmemState_ -> address + + shmemState_ -> offset, dstData, dstDataSize); + + // + // Get rid of the original X_PutImage + // request. + // + + if (writeBuffer_.getScratchData() != NULL) + { + writeBuffer_.removeScratchMessage(); + } + else + { + writeBuffer_.removeMessage(size); + } + + // + // Add a X_ShmPutImage request to the + // write buffer. + // + + buffer = writeBuffer_.addMessage(40); + + *buffer = shmemState_ -> opcode; + + *(buffer + 1) = X_ShmPutImage; + + PutUINT(40 >> 2, buffer + 2, bigEndian_); + + PutULONG(imageState_ -> drawable, buffer + 4, bigEndian_); + PutULONG(imageState_ -> gcontext, buffer + 8, bigEndian_); + + PutUINT(imageState_ -> dstWidth, buffer + 12, bigEndian_); + PutUINT(imageState_ -> dstLines, buffer + 14, bigEndian_); + + PutUINT(imageState_ -> srcX, buffer + 16, bigEndian_); + PutUINT(imageState_ -> srcY, buffer + 18, bigEndian_); + + PutUINT(imageState_ -> dstWidth, buffer + 20, bigEndian_); + PutUINT(imageState_ -> dstLines, buffer + 22, bigEndian_); + + PutUINT(imageState_ -> dstX, buffer + 24, bigEndian_); + PutUINT(imageState_ -> dstY, buffer + 26, bigEndian_); + + *(buffer + 28) = imageState_ -> dstDepth; + *(buffer + 29) = imageState_ -> format; + *(buffer + 30) = 1; + + PutULONG(shmemState_ -> segment, buffer + 32, bigEndian_); + PutULONG(shmemState_ -> offset, buffer + 36, bigEndian_); + + shmemState_ -> offset += dstDataSize; + + shmemState_ -> sequence = clientSequence_; + shmemState_ -> last = getTimestamp(); + + #ifdef TEST + *logofs << "handleShmem: Saved shared memory sequence " + << shmemState_ -> sequence << " for FD#" << fd_ + << " with offset " << shmemState_ -> offset + << " at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + // + // Make the X server read immediately + // from the shared memory buffer and + // produce the completion event. + // + + handleFlush(flush_if_any); + + return 1; +} + +// +// Try to read more events from the socket in the +// attempt to get the completion event required +// to reset the MIT-SHM segment. +// + +int ServerChannel::handleShmemEvent() +{ + #if defined(TEST) || defined(INFO) + *logofs << "handleShmemEvent: Waiting for shared memory " + << "sequence " << shmemState_ -> sequence + << " for X server FD#" << fd_ << ".\n" + << logofs_flush; + + T_timestamp startTs = getTimestamp(); + + #endif + + while (isTimestamp(shmemState_ -> last) != 0) + { + if (handleWait(control -> ShmemTimeout) <= 0) + { + break; + } + #if defined(TEST) || defined(INFO) + else + { + *logofs << "handleShmemEvent: WARNING! Encoded events " + << "for FD#" << fd_ << " at " << strMsTimestamp() + << ".\n" << logofs_flush; + } + #endif + } + + if (isTimestamp(shmemState_ -> last) == 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleShmemEvent: Spent " + << diffTimestamp(startTs, getTimestamp()) << " Ms " + << "waiting for shared memory sequence for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + return 1; + } + + #if defined(TEST) || defined(INFO) + *logofs << "handleShmemEvent: WARNING! Can't reset shared " + << "memory sequence for FD#" << fd_ << " after " + << diffTimestamp(shmemState_ -> last, getTimestamp()) + << " Ms.\n" << logofs_flush; + #endif + + return 0; +} + +int ServerChannel::checkShmemEvent(unsigned char event, unsigned short sequence, + const unsigned char *buffer) +{ + if (isTimestamp(shmemState_ -> last) == 1 && + sequence == shmemState_ -> sequence) + { + #ifdef TEST + *logofs << "checkShmemEvent: Reset shared memory sequence " + << shmemState_ -> sequence << " for FD#" << fd_ + << " after " << diffTimestamp(shmemState_ -> last, + getTimestamp()) << " Ms.\n" << logofs_flush; + #endif + + shmemState_ -> sequence = 0; + shmemState_ -> offset = 0; + shmemState_ -> last = nullTimestamp(); + } + #ifdef TEST + else + { + *logofs << "checkShmemEvent: Skipping past shared memory " + << "image sequence " << sequence << " for FD#" + << fd_ << ".\n" << logofs_flush; + } + #endif + + return 1; +} + +int ServerChannel::checkShmemError(unsigned char error, unsigned short sequence, + const unsigned char *buffer) +{ + #ifdef TEST + + *logofs << "checkShmemError: WARNING! Failed operation for " + << "FD#" << fd_ << " in stage " << shmemState_ -> stage + << " with ERR_CODE#" << (unsigned int) *(buffer + 1); + + *logofs << " RES_ID#" << GetULONG(buffer + 4, bigEndian_); + + *logofs << " MIN_OP#" << GetUINT(buffer + 8, bigEndian_); + + *logofs << " MAJ_OP#" << (unsigned int) *(buffer + 10); + + *logofs << " sequence " << sequence << ".\n"; + + *logofs << logofs_flush; + + #endif + + // + // If enabled flag is <= 0 we are still + // in the inizialization phase. In this + // case force presence to false. + // + + if (shmemState_ -> enabled != 1) + { + if (shmemState_ -> present != 0) + { + #ifdef TEST + *logofs << "checkShmemError: Resetting shared memory " + << "presence flag for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + shmemState_ -> present = 0; + } + + return 0; + } + + if (shmemState_ -> sequence == sequence) + { + // + // Reset the sequence and timestamp. + // + + shmemState_ -> sequence = 0; + shmemState_ -> offset = 0; + shmemState_ -> last = nullTimestamp(); + } + + return 1; +} + +int ServerChannel::handleFontRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + // + // Send a synchronization request and use + // the reply to return the requested font + // path. + // + + #ifdef TEST + *logofs << "handleFontRequest: Sending X_GetInputFocus request " + << "for FD#" << fd_ << " due to OPCODE#" << (unsigned int) + opcodeStore_ -> getFontParameters << ".\n" + << logofs_flush; + #endif + + opcode = X_GetInputFocus; + + size = 4; + buffer = writeBuffer_.addMessage(size); + + sequenceQueue_.push(clientSequence_, X_GetInputFocus, + opcodeStore_ -> getFontParameters); + + return 1; +} + +int ServerChannel::handleFontReply(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size) +{ + #ifdef TEST + *logofs << "handleFontReply: Encoding font operation " + << "reply with size " << size << ".\n" + << logofs_flush; + #endif + + char data[256]; + + if (fontPort_ != -1) + { + sprintf(data + 1, "tcp/localhost:%d", fontPort_); + } + else + { + *(data + 1) = '\0'; + } + + *data = strlen(data + 1); + + unsigned char *next = (unsigned char *) data; + + unsigned int length = (unsigned int) (*next++); + + encodeBuffer.encodeValue(length, 8); + + encodeBuffer.encodeTextData(next, length); + + return 1; +} + +int ServerChannel::handleCacheRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + unsigned int mask; + + decodeBuffer.decodeCachedValue(mask, 32, clientCache_ -> + setCacheParametersCache); + + splitState_.save = (mask >> 8) & 0xff; + splitState_.load = mask & 0xff; + + #ifdef TEST + *logofs << "handleCacheRequest: Set cache parameters to " + << "save " << splitState_.save << " load " + << splitState_.load << ".\n" << logofs_flush; + #endif + + handleNullRequest(opcode, buffer, size); + + return 1; +} + +int ServerChannel::handleStartSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + // + // Prepare for the split for the selected + // resource. Old proxy versions only used + // the split store at position 0. + // + + // Since ProtoStep7 (#issue 108) + unsigned char resource; + + decodeBuffer.decodeCachedValue(resource, 8, + clientCache_ -> resourceCache); + + splitState_.resource = resource; + + splitState_.current = splitState_.resource; + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleStartSplitRequest: SPLIT! Registered id " + << splitState_.resource << " as resource " + << "waiting for a split.\n" << logofs_flush; + #endif + + handleNullRequest(opcode, buffer, size); + + return 1; +} + +int ServerChannel::handleEndSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + // + // Verify that the agent resource matches. + // + + // Since ProtoStep7 (#issue 108) + unsigned char resource; + + decodeBuffer.decodeCachedValue(resource, 8, + clientCache_ -> resourceCache); + + #ifdef TEST + + if (splitState_.resource == nothing) + { + #ifdef PANIC + *logofs << "handleEndSplitRequest: PANIC! SPLIT! Received an end of " + << "split for resource id " << (unsigned int) *(buffer + 1) + << " without a previous start.\n" + << logofs_flush; + #endif + + HandleCleanup(); + } + else if (resource != splitState_.resource) + { + #ifdef PANIC + *logofs << "handleEndSplitRequest: PANIC! SPLIT! Invalid resource id " + << resource << " received while waiting for resource id " + << splitState_.resource << ".\n" << logofs_flush; + #endif + + HandleCleanup(); + } + + #endif + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleEndSplitRequest: SPLIT! Reset id " + << splitState_.resource << " as resource " + << "selected for splits.\n" << logofs_flush; + #endif + + splitState_.resource = nothing; + + handleNullRequest(opcode, buffer, size); + + return 1; +} + +int ServerChannel::handleSplitChecksum(DecodeBuffer &decodeBuffer, T_checksum &checksum) +{ + unsigned int receive; + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeBoolValue(receive); + + if (receive == 1) + { + checksum = new md5_byte_t[MD5_LENGTH]; + + for (unsigned int i = 0; i < MD5_LENGTH; i++) + { + decodeBuffer.decodeValue(receive, 8); + + if (checksum != NULL) + { + checksum[i] = (unsigned char) receive; + } + } + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitChecksum: SPLIT! Received checksum " + << "[" << DumpChecksum(checksum) << "].\n" + << logofs_flush; + #endif + + return 1; + } + + return 0; +} + +void ServerChannel::handleShmemStateAlloc() +{ + if (shmemState_ == NULL) + { + shmemState_ = new T_shmem_state(); + + shmemState_ -> stage = -1; + shmemState_ -> present = -1; + shmemState_ -> enabled = -1; + + shmemState_ -> segment = -1; + shmemState_ -> id = -1; + shmemState_ -> address = NULL; + shmemState_ -> size = 0; + + shmemState_ -> opcode = 0xff; + shmemState_ -> event = 0xff; + shmemState_ -> error = 0xff; + + shmemState_ -> sequence = 0; + shmemState_ -> offset = 0; + shmemState_ -> last = nullTimestamp(); + + shmemState_ -> checked = 0; + } +} + +void ServerChannel::handleShmemStateRemove() +{ + if (shmemState_ != NULL) + { + if (shmemState_ -> address != NULL) + { +#ifndef ANDROID + shmdt((char *) shmemState_ -> address); +#else + cerr << "Info: handleShmemStateRemove: In android no shared memory. This should not be displayed. address should always be NULL\n"; +#endif + } + + if (shmemState_ -> id > 0) + { +#ifndef ANDROID + shmctl(shmemState_ -> id, IPC_RMID, 0); +#else + cerr << "Info: handleShmemStateRemove: In android no shared memory. This should not be displayed. id should always be 0\n"; +#endif + } + + delete shmemState_; + + shmemState_ = NULL; + } +} + +void ServerChannel::handleUnpackStateInit(int resource) +{ + if (unpackState_[resource] == NULL) + { + unpackState_[resource] = new T_unpack_state(); + + if (unpackState_[resource] == NULL) + { + #ifdef PANIC + *logofs << "handleUnpackStateInit: PANIC! Can't allocate " + << "memory for unpack state in context [A].\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't allocate memory for " + << "unpack state in context [A].\n"; + + HandleAbort(); + } + + unpackState_[resource] -> geometry = NULL; + unpackState_[resource] -> colormap = NULL; + unpackState_[resource] -> alpha = NULL; + } +} + +void ServerChannel::handleUnpackAllocGeometry(int resource) +{ + if (unpackState_[resource] -> geometry == NULL) + { + unpackState_[resource] -> geometry = new T_geometry(); + + if (unpackState_[resource] -> geometry == NULL) + { + #ifdef PANIC + *logofs << "handleUnpackAllocGeometry: PANIC! Can't allocate " + << "memory for unpack state in context [B].\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't allocate memory for " + << "unpack state in context [B].\n"; + + HandleAbort(); + } + + unpackState_[resource] -> geometry -> depth1_bpp = 4; + unpackState_[resource] -> geometry -> depth4_bpp = 4; + unpackState_[resource] -> geometry -> depth8_bpp = 8; + unpackState_[resource] -> geometry -> depth16_bpp = 16; + unpackState_[resource] -> geometry -> depth24_bpp = 32; + unpackState_[resource] -> geometry -> depth32_bpp = 32; + + unpackState_[resource] -> geometry -> red_mask = 0xff0000; + unpackState_[resource] -> geometry -> green_mask = 0x00ff00; + unpackState_[resource] -> geometry -> blue_mask = 0x0000ff; + + unpackState_[resource] -> geometry -> image_byte_order = imageByteOrder_; + unpackState_[resource] -> geometry -> bitmap_bit_order = bitmapBitOrder_; + unpackState_[resource] -> geometry -> scanline_unit = scanlineUnit_; + unpackState_[resource] -> geometry -> scanline_pad = scanlinePad_; + } +} + +void ServerChannel::handleUnpackAllocColormap(int resource) +{ + if (unpackState_[resource] -> colormap == NULL) + { + unpackState_[resource] -> colormap = new T_colormap(); + + if (unpackState_[resource] -> colormap == NULL) + { + #ifdef PANIC + *logofs << "handleUnpackAllocColormap: PANIC! Can't allocate " + << "memory for unpack state in context [C].\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't allocate memory for " + << "unpack state in context [C].\n"; + + HandleAbort(); + } + + unpackState_[resource] -> colormap -> entries = 0; + unpackState_[resource] -> colormap -> data = NULL; + } +} + +void ServerChannel::handleUnpackAllocAlpha(int resource) +{ + if (unpackState_[resource] -> alpha == NULL) + { + unpackState_[resource] -> alpha = new T_alpha(); + + if (unpackState_[resource] -> alpha == NULL) + { + #ifdef PANIC + *logofs << "handleUnpackAllocAlpha: PANIC! Can't allocate " + << "memory for unpack state in context [D].\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't allocate memory for " + << "unpack state in context [D].\n"; + + HandleAbort(); + } + + unpackState_[resource] -> alpha -> entries = 0; + unpackState_[resource] -> alpha -> data = NULL; + } +} + +void ServerChannel::handleUnpackStateRemove(int resource) +{ + if (unpackState_[resource] != NULL) + { + delete unpackState_[resource] -> geometry; + + if (unpackState_[resource] -> colormap != NULL) + { + delete [] unpackState_[resource] -> colormap -> data; + } + + delete unpackState_[resource] -> colormap; + + if (unpackState_[resource] -> alpha != NULL) + { + delete [] unpackState_[resource] -> alpha -> data; + } + + delete unpackState_[resource] -> alpha; + + delete unpackState_[resource]; + + unpackState_[resource] = NULL; + } +} + +void ServerChannel::handleEncodeCharInfo(const unsigned char *nextSrc, EncodeBuffer &encodeBuffer) +{ + unsigned int value = GetUINT(nextSrc, bigEndian_) | + (GetUINT(nextSrc + 10, bigEndian_) << 16); + + encodeBuffer.encodeCachedValue(value, 32, + *serverCache_ -> queryFontCharInfoCache[0], 6); + + nextSrc += 2; + + for (unsigned int i = 1; i < 5; i++) + { + unsigned int value = GetUINT(nextSrc, bigEndian_); + + nextSrc += 2; + + encodeBuffer.encodeCachedValue(value, 16, + *serverCache_ -> queryFontCharInfoCache[i], 6); + } +} + +int ServerChannel::setBigEndian(int flag) +{ + bigEndian_ = flag; + + readBuffer_.setBigEndian(flag); + + return 1; +} + +int ServerChannel::setReferences() +{ + #ifdef TEST + *logofs << "ServerChannel: Initializing the static " + << "members for the server channels.\n" + << logofs_flush; + #endif + + #ifdef REFERENCES + + references_ = 0; + + #endif + + return 1; +} |